You've already forked lazarus-ccr
LazMapViewer: Fix logical area operations to be compatible with reversed longitudes when the dateline is crossed. Add unit tests for it.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@8806 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -188,62 +188,34 @@ uses
|
|||||||
|
|
||||||
function hasIntersectArea(const Area1: TRealArea; const Area2: TRealArea): boolean;
|
function hasIntersectArea(const Area1: TRealArea; const Area2: TRealArea): boolean;
|
||||||
begin
|
begin
|
||||||
Result := (Area1.TopLeft.Lon <= Area2.BottomRight.Lon) and
|
Result := Area1.Intersects(Area2);
|
||||||
(Area1.BottomRight.Lon >= Area2.TopLeft.Lon) and
|
|
||||||
(Area1.TopLeft.Lat >= Area2.BottomRight.Lat) and
|
|
||||||
(Area1.BottomRight.Lat <= Area2.TopLeft.Lat);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function IntersectArea(const Area1: TRealArea; const Area2: TRealArea): TRealArea;
|
function IntersectArea(const Area1: TRealArea; const Area2: TRealArea): TRealArea;
|
||||||
begin
|
begin
|
||||||
Result := Area1;
|
Result := Area1.Intersection(Area2);
|
||||||
if Result.TopLeft.Lon<Area2.topLeft.Lon then
|
|
||||||
Result.TopLeft.Lon:=Area2.topLeft.Lon;
|
|
||||||
if Result.TopLeft.Lat>Area2.topLeft.Lat then
|
|
||||||
Result.TopLeft.Lat:=Area2.topLeft.Lat;
|
|
||||||
if Result.BottomRight.Lon>Area2.BottomRight.Lon then
|
|
||||||
Result.BottomRight.Lon:=Area2.BottomRight.Lon;
|
|
||||||
if Result.BottomRight.Lat<Area2.BottomRight.Lat then
|
|
||||||
Result.BottomRight.Lat:=Area2.BottomRight.Lat;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function PtInsideArea(const aPoint: TRealPoint; const Area: TRealArea): boolean;
|
function PtInsideArea(const aPoint: TRealPoint; const Area: TRealArea): boolean;
|
||||||
begin
|
begin
|
||||||
Result := (Area.TopLeft.Lon <= aPoint.Lon) and
|
Result := Area.ContainsPoint(aPoint);
|
||||||
(Area.BottomRight.Lon >= aPoint.Lon) and
|
|
||||||
(Area.TopLeft.Lat >= aPoint.Lat) and
|
|
||||||
(Area.BottomRight.Lat <= aPoint.Lat);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function AreaInsideArea(const AreaIn: TRealArea; const AreaOut: TRealArea): boolean;
|
function AreaInsideArea(const AreaIn: TRealArea; const AreaOut: TRealArea): boolean;
|
||||||
begin
|
begin
|
||||||
Result := (AreaIn.TopLeft.Lon >= AreaOut.TopLeft.Lon) and
|
Result := AreaIn.Equal(AreaIn.Intersection(AreaOut));
|
||||||
(AreaIn.BottomRight.Lon <= AreaOut.BottomRight.Lon) and
|
|
||||||
(AreaOut.TopLeft.Lat >= AreaIn.TopLeft.Lat) and
|
|
||||||
(AreaOut.BottomRight.Lat <= AreaIn.BottomRight.Lat);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure ExtendArea(var AreaToExtend: TRealArea; const Area: TRealArea);
|
procedure ExtendArea(var AreaToExtend: TRealArea; const Area: TRealArea);
|
||||||
begin
|
begin
|
||||||
if AreaToExtend.TopLeft.Lon>Area.TopLeft.Lon then
|
AreaToExtend := AreaToExtend.Union(Area);
|
||||||
AreaToExtend.TopLeft.Lon:=Area.TopLeft.Lon;
|
|
||||||
if AreaToExtend.BottomRight.Lon<Area.BottomRight.Lon then
|
|
||||||
AreaToExtend.BottomRight.Lon:=Area.BottomRight.Lon;
|
|
||||||
|
|
||||||
if AreaToExtend.TopLeft.Lat<Area.TopLeft.Lat then
|
|
||||||
AreaToExtend.TopLeft.Lat:=Area.TopLeft.Lat;
|
|
||||||
if AreaToExtend.BottomRight.Lat>Area.BottomRight.Lat then
|
|
||||||
AreaToExtend.BottomRight.Lat:=Area.BottomRight.Lat;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function GetAreaOf(objs: TGPSObjList): TRealArea;
|
function GetAreaOf(objs: TGPSObjList): TRealArea;
|
||||||
var
|
var
|
||||||
i: integer;
|
i: integer;
|
||||||
begin
|
begin
|
||||||
Result.TopLeft.Lon := 0;
|
Result.Init(0, 0, 0, 0);
|
||||||
Result.TopLeft.Lat := 0;
|
|
||||||
Result.BottomRight.Lon := 0;
|
|
||||||
Result.BottomRight.Lat := 0;
|
|
||||||
if Objs.Count>0 then
|
if Objs.Count>0 then
|
||||||
begin
|
begin
|
||||||
Result := Objs[0].BoundingBox;
|
Result := Objs[0].BoundingBox;
|
||||||
@ -419,10 +391,7 @@ var
|
|||||||
i: integer;
|
i: integer;
|
||||||
ptArea: TRealArea;
|
ptArea: TRealArea;
|
||||||
begin
|
begin
|
||||||
Area.BottomRight.lon := 0;
|
Area.Init(0, 0, 0, 0);
|
||||||
Area.BottomRight.lat := 0;
|
|
||||||
Area.TopLeft.lon := 0;
|
|
||||||
Area.TopLeft.lat := 0;
|
|
||||||
Lock;
|
Lock;
|
||||||
try
|
try
|
||||||
if Count > 0 then
|
if Count > 0 then
|
||||||
@ -567,10 +536,7 @@ var
|
|||||||
Objs: TGPSObjarray;
|
Objs: TGPSObjarray;
|
||||||
i: integer;
|
i: integer;
|
||||||
begin
|
begin
|
||||||
Result.BottomRight.Lat := 0;
|
Result.Init(0, 0, 0, 0);
|
||||||
Result.BottomRight.Lon := 0;
|
|
||||||
Result.TopLeft.Lat := 0;
|
|
||||||
Result.TopLeft.Lon := 0;
|
|
||||||
Lock;
|
Lock;
|
||||||
try
|
try
|
||||||
IdsToObj(Ids, Objs, AIdOwner);
|
IdsToObj(Ids, Objs, AIdOwner);
|
||||||
@ -708,10 +674,7 @@ var
|
|||||||
i: integer;
|
i: integer;
|
||||||
ptArea: TRealArea;
|
ptArea: TRealArea;
|
||||||
begin
|
begin
|
||||||
Area.BottomRight.lon := 0;
|
Area.Init(0, 0, 0, 0);
|
||||||
Area.BottomRight.lat := 0;
|
|
||||||
Area.TopLeft.lon := 0;
|
|
||||||
Area.TopLeft.lat := 0;
|
|
||||||
if FPoints.Count > 0 then
|
if FPoints.Count > 0 then
|
||||||
begin
|
begin
|
||||||
Area := FPoints[0].BoundingBox;
|
Area := FPoints[0].BoundingBox;
|
||||||
|
@ -39,21 +39,207 @@ Type
|
|||||||
function GetLatRad: Extended;
|
function GetLatRad: Extended;
|
||||||
procedure SetLatRad(AValue: Extended);
|
procedure SetLatRad(AValue: Extended);
|
||||||
public
|
public
|
||||||
|
procedure Init(ALon, ALat: Double);
|
||||||
property LonRad: Extended read GetLonRad write SetLonRad;
|
property LonRad: Extended read GetLonRad write SetLonRad;
|
||||||
property LatRad: Extended read GetLatRad write SetLatRad;
|
property LatRad: Extended read GetLatRad write SetLatRad;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TRealArea }
|
{ TRealArea }
|
||||||
TRealArea = Record
|
TRealArea = Record
|
||||||
|
public
|
||||||
TopLeft : TRealPoint;
|
TopLeft : TRealPoint;
|
||||||
BottomRight : TRealPoint;
|
BottomRight : TRealPoint;
|
||||||
|
public
|
||||||
|
procedure Init(ALeft, ATop, ARight, ABottom: Extended);
|
||||||
|
procedure Init(ATopLeft, ABottomRight: TRealPoint);
|
||||||
|
function ContainsPoint(APoint: TRealPoint): boolean;
|
||||||
|
function Equal(Area: TRealArea): Boolean;
|
||||||
|
function Intersection(const Area: TRealArea): TRealArea;
|
||||||
|
function Intersects(const Area: TRealArea): boolean;
|
||||||
|
function Union(const Area: TRealArea): TRealArea;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
|
{ Helper functions to simplify using the cyclic coordinates }
|
||||||
|
|
||||||
|
{ Checks whether x is between x1 and x2 (where x1 < x2) }
|
||||||
|
function LinearInRange(x, x1, x2: Extended): Boolean;
|
||||||
|
begin
|
||||||
|
Result := InRange(x, x1, x2);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Checks whether x is between x1 and x2 where x1 and x2 can be in any order.
|
||||||
|
When x1 > x2 it is assumed that the interval crosses the dateline. }
|
||||||
|
function CyclicInRange(x, x1, x2: Extended): Boolean;
|
||||||
|
begin
|
||||||
|
if x1 <= x2 then
|
||||||
|
Result := inRange(x, x1, x2)
|
||||||
|
else
|
||||||
|
Result := (x > x1) or (x < x2);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{ Checks whether the line segment between A1 and A2 intersects the line segment
|
||||||
|
between B1 and B2. It is assumed that A1 < A2 and B1 < B2. }
|
||||||
|
function LinearIntersects(A1, A2, B1, B2: Extended): Boolean;
|
||||||
|
begin
|
||||||
|
Result := InRange(A1, B1, B2) or InRange(A2, B1, B2) or
|
||||||
|
InRange(B1, A1, A2) or InRange(B2, A1, A2);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Checks whether the line segment between A1 and A2 intersects the line segment
|
||||||
|
between B1 and B2. A1 and A2, as well as B1 and B2 can be in any order.
|
||||||
|
When the coordinate with index 2 is greater than the coordinate with index 1
|
||||||
|
it is assumed that the segment crosses the dateline. }
|
||||||
|
function CyclicIntersects(L1, R1, L2, R2: Extended): Boolean;
|
||||||
|
begin
|
||||||
|
if (L1 <= R1) and (L2 <= R2) then
|
||||||
|
Result := LinearIntersects(L1, R1, L2, R2)
|
||||||
|
else
|
||||||
|
if (L1 <= R1) and (L2 > R2) then
|
||||||
|
Result := (L2 <= R1) or (R2 >= L1)
|
||||||
|
else
|
||||||
|
if (L1 > R1) and (L2 <= R2) then
|
||||||
|
Result := (R1 >= L2) or (L1 <= R2)
|
||||||
|
else
|
||||||
|
Result := true;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Calculates in Res1 and Res2 the endpoints of the overlap of the segments
|
||||||
|
between A1 and A2 and between B1 and B2. A1 and A2, and B1 and B2 must be
|
||||||
|
in ascending order.
|
||||||
|
The function returns false if the segments do not overlap. }
|
||||||
|
function LinearIntersection(A1, A2, B1, B2: Extended; out Res1, Res2: Extended): Boolean;
|
||||||
|
begin
|
||||||
|
Result := false;
|
||||||
|
if (A2 < B1) or (B2 < A1) then
|
||||||
|
exit;
|
||||||
|
Res1 := A1;
|
||||||
|
Res2 := A2;
|
||||||
|
if B1 > Res1 then Res1 := B1;
|
||||||
|
if B2 < Res2 then Res2 := B2;
|
||||||
|
Result := true;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Calculates in L and R the endpoints of the overlap of the segments
|
||||||
|
between L1 and R1 and between L2 and R2. L1 and R1, and L2 and R2 can be in
|
||||||
|
any order. If L1 > R1 it is assumed that this segment crosses the dateline.
|
||||||
|
Likewise with L2/R2.
|
||||||
|
The function returns false if the segments do not overlap. }
|
||||||
|
function CyclicIntersection(L1, R1, L2, R2: Extended; out L, R: Extended): Boolean;
|
||||||
|
begin
|
||||||
|
Result := false;
|
||||||
|
if (L1 <= R1) and (L2 <= R2) then
|
||||||
|
Result := LinearIntersection(L1, R1, L2, R2, L, R)
|
||||||
|
else
|
||||||
|
if (L1 <= R1) and (L2 > R2) then
|
||||||
|
begin
|
||||||
|
if (L2 > R1) and (L1 > R2) then
|
||||||
|
exit;
|
||||||
|
Result := true;
|
||||||
|
L := L1;
|
||||||
|
R := R1;
|
||||||
|
if (L2 <= L1) or (R2 >= R1) then exit;
|
||||||
|
if L2 < R1 then R := L2;
|
||||||
|
if R2 > L1 then L := R2;
|
||||||
|
end else
|
||||||
|
if (L1 > R1) and (L2 <= R2) then
|
||||||
|
begin
|
||||||
|
if (L1 > R2) and (R1 < L2) then exit;
|
||||||
|
L := L2;
|
||||||
|
R := R2;
|
||||||
|
Result := true;
|
||||||
|
if (L1 <= L2) or (R1 >= R2) then exit;
|
||||||
|
if L1 < R2 then R := L1;
|
||||||
|
if R1 > L2 then L := R1;
|
||||||
|
end else
|
||||||
|
begin
|
||||||
|
Result := true;
|
||||||
|
L := L1;
|
||||||
|
R := R1;
|
||||||
|
if L2 > L1 then L := L2;
|
||||||
|
if R2 < R1 then R := R2;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Calculates the union of the sements between A1/A2 and between B1/B2 and
|
||||||
|
returns the endpoints of the union in Res1 and Res2. It is assumed then
|
||||||
|
A1/A2 and B1/B2 are in ascending order. }
|
||||||
|
procedure LinearUnion(A1, A2, B1, B2: Extended; out Res1, Res2: Extended);
|
||||||
|
begin
|
||||||
|
Res1 := A1;
|
||||||
|
Res2 := A2;
|
||||||
|
if B1 < Res1 then Res1 := B1;
|
||||||
|
if B2 > Res2 then Res2 := B2;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Calculates the union of the sements between L1/R1 and between L2/R2 and
|
||||||
|
returns the endpoints of the union in L and R. L1/R1 and L2/R2 can be in any
|
||||||
|
order. When L1 > R1 then it is assumed that this segment crosses the dateline.
|
||||||
|
Likewise with L2/R2. }
|
||||||
|
procedure CyclicUnion(L1, R1, L2, R2: Extended; out L, R: Extended);
|
||||||
|
procedure SetLimits(aL, aR: Extended);
|
||||||
|
begin
|
||||||
|
L := aL;
|
||||||
|
R := aR;
|
||||||
|
end;
|
||||||
|
begin
|
||||||
|
// Both between -180 and 180 deg
|
||||||
|
if (L1 <= R1) and (L2 <= R2) then
|
||||||
|
LinearUnion(L1, R1, L2, R2, L, R)
|
||||||
|
else
|
||||||
|
// 2nd crossing dateline //-180 180
|
||||||
|
if (L1 <= R1) and (L2 > R2) then // | L1-----R1 |
|
||||||
|
begin
|
||||||
|
if L2 <= L1 then // |-R2 L2--------------------|
|
||||||
|
SetLimits(L2, R2)
|
||||||
|
else
|
||||||
|
if L2 <= R1 then
|
||||||
|
begin
|
||||||
|
if R2 < L1 then // |-R2 L2--------------|
|
||||||
|
SetLimits(L1, R2)
|
||||||
|
else // |----------R2 L2------------| // Complete overlap
|
||||||
|
SetLimits(-180.0, 180.0);
|
||||||
|
end else
|
||||||
|
begin // L2 > R1
|
||||||
|
if R2 < L1 then // |-R2 L2--| // No overlap here. Since we want to "extend", we keep L1R1
|
||||||
|
SetLimits(L1, R1)
|
||||||
|
else // |----------R2 L2--|
|
||||||
|
if R2 < R1 then
|
||||||
|
SetLimits(L2, R1)
|
||||||
|
else // R2 > R // |-------------------R2 L2--|
|
||||||
|
SetLimits(L2, R2);
|
||||||
|
end;
|
||||||
|
end else
|
||||||
|
// 1st crossing dateline
|
||||||
|
if (L1 > R1) and (L2 <= R2) then
|
||||||
|
begin
|
||||||
|
CyclicUnion(L2, R2, L1, R1, L, R);
|
||||||
|
end else
|
||||||
|
// both crossing dateline
|
||||||
|
begin
|
||||||
|
if L2 < R1 then // complete overlap
|
||||||
|
SetLimits(-180, 180)
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
SetLimits(L1, R1);
|
||||||
|
if L2 < L then L := L2;
|
||||||
|
if R2 > R then R := R2;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ TRealPoint }
|
{ TRealPoint }
|
||||||
|
|
||||||
|
procedure TRealPoint.Init(ALon, ALat: Double);
|
||||||
|
begin
|
||||||
|
Lon := ALon;
|
||||||
|
Lat := ALat;
|
||||||
|
end;
|
||||||
|
|
||||||
function TRealPoint.GetLonRad: Extended;
|
function TRealPoint.GetLonRad: Extended;
|
||||||
begin
|
begin
|
||||||
Result := DegToRad(Self.Lon);
|
Result := DegToRad(Self.Lon);
|
||||||
@ -74,5 +260,72 @@ begin
|
|||||||
Self.Lat := RadToDeg(AValue);
|
Self.Lat := RadToDeg(AValue);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{ TRealArea
|
||||||
|
|
||||||
|
It is assumed (and not checked) that ATop and ABottom are between -90 and 90
|
||||||
|
and ALeft and ARight between -180 and 180. When ALeft and ARight are in
|
||||||
|
reverse order it is assumed that the dateline is crossed.
|
||||||
|
}
|
||||||
|
procedure TRealArea.Init(ALeft, ATop, ARight, ABottom: Extended);
|
||||||
|
begin
|
||||||
|
TopLeft.Lon := ALeft;
|
||||||
|
TopLeft.Lat := ATop;
|
||||||
|
BottomRight.Lon := ARight;
|
||||||
|
BottomRight.Lat := ABottom;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TRealArea.Init(ATopLeft, ABottomRight: TRealPoint);
|
||||||
|
begin
|
||||||
|
TopLeft := ATopLeft;
|
||||||
|
BottomRight := ABottomRight;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Checks whether the given point is inside the area (including borders). }
|
||||||
|
function TRealArea.ContainsPoint(APoint: TRealPoint): boolean;
|
||||||
|
begin
|
||||||
|
Result :=
|
||||||
|
LinearInRange(APoint.Lat, BottomRight.Lat, TopLeft.Lat) and
|
||||||
|
CyclicInRange(APoint.Lon, TopLeft.Lon, BottomRight.Lon);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TRealArea.Equal(Area: TRealArea): Boolean;
|
||||||
|
begin
|
||||||
|
Result :=
|
||||||
|
(TopLeft.Lon = Area.TopLeft.Lon) and
|
||||||
|
(TopLeft.Lat = Area.TopLeft.Lat) and
|
||||||
|
(BottomRight.Lon = Area.BottomRight.Lon) and
|
||||||
|
(BottomRight.Lat = Area.BottomRight.Lat);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TRealArea.Intersection(const Area: TRealArea): TRealArea;
|
||||||
|
var
|
||||||
|
B, T, L, R: Extended;
|
||||||
|
begin
|
||||||
|
LinearIntersection(BottomRight.Lat, TopLeft.Lat, Area.BottomRight.Lat, Area.TopLeft.Lat, B, T);
|
||||||
|
CyclicIntersection(TopLeft.Lon, BottomRight.Lon, Area.TopLeft.Lon, Area.BottomRight.Lon, L, R);
|
||||||
|
Result.Init(L, T, R, B);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TRealArea.Intersects(const Area: TRealArea): boolean;
|
||||||
|
var
|
||||||
|
A1, A2: TRealArea;
|
||||||
|
begin
|
||||||
|
Result :=
|
||||||
|
LinearIntersects(BottomRight.Lat, TopLeft.Lat, Area.BottomRight.Lat, Area.TopLeft.Lat) and
|
||||||
|
CyclicIntersects(TopLeft.Lon, BottomRight.Lon, Area.TopLeft.Lon, Area.BottomRight.Lon);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Calculates the union with the other area. When the date line is crossed the
|
||||||
|
right longitude becomes smaller than the left longitude! }
|
||||||
|
function TRealArea.Union(const Area: TRealArea): TRealArea;
|
||||||
|
var
|
||||||
|
B, T, L, R: Extended;
|
||||||
|
begin
|
||||||
|
LinearUnion(BottomRight.Lat, TopLeft.Lat, Area.BottomRight.Lat, Area.TopLeft.Lat, B, T);
|
||||||
|
CyclicUnion(TopLeft.Lon, BottomRight.Lon, Area.TopLeft.Lon, Area.BottomRight.Lon, L, R);
|
||||||
|
Result.Init(L, T, R, B);
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -37,9 +37,12 @@
|
|||||||
<IsPartOfProject Value="True"/>
|
<IsPartOfProject Value="True"/>
|
||||||
</Unit>
|
</Unit>
|
||||||
<Unit>
|
<Unit>
|
||||||
<Filename Value="mvmisctests_engine.pas"/>
|
<Filename Value="mvtests_types.pas"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
</Unit>
|
||||||
|
<Unit>
|
||||||
|
<Filename Value="mvtests_engine.pas"/>
|
||||||
<IsPartOfProject Value="True"/>
|
<IsPartOfProject Value="True"/>
|
||||||
<UnitName Value="mvMiscTests_Engine"/>
|
|
||||||
</Unit>
|
</Unit>
|
||||||
</Units>
|
</Units>
|
||||||
</ProjectOptions>
|
</ProjectOptions>
|
||||||
|
@ -3,7 +3,8 @@ program mapviewer_tests;
|
|||||||
{$mode objfpc}{$H+}
|
{$mode objfpc}{$H+}
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Interfaces, Forms, GuiTestRunner, mvMiscTests_Engine;
|
Interfaces, Forms, GuiTestRunner,
|
||||||
|
mvtests_engine, mvtests_types;
|
||||||
|
|
||||||
{$R *.res}
|
{$R *.res}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
unit mvMiscTests_Engine;
|
unit mvtests_engine;
|
||||||
|
|
||||||
{$mode objfpc}{$H+}
|
{$mode objfpc}{$H+}
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ uses
|
|||||||
Classes, SysUtils, fpcunit, testutils, testregistry;
|
Classes, SysUtils, fpcunit, testutils, testregistry;
|
||||||
|
|
||||||
type
|
type
|
||||||
TMiscTests_Engine= class(TTestCase)
|
TMvTests_Engine= class(TTestCase)
|
||||||
published
|
published
|
||||||
procedure Test_Distance;
|
procedure Test_Distance;
|
||||||
procedure Test_LatToStr_DMS;
|
procedure Test_LatToStr_DMS;
|
||||||
@ -94,7 +94,7 @@ const
|
|||||||
var
|
var
|
||||||
PointFormatsettings: TFormatSettings;
|
PointFormatsettings: TFormatSettings;
|
||||||
|
|
||||||
procedure TMiscTests_Engine.Test_Distance;
|
procedure TMvTests_Engine.Test_Distance;
|
||||||
const
|
const
|
||||||
TOLERANCE = 2;
|
TOLERANCE = 2;
|
||||||
RADIUS = 6378; // Earth radius in km, as used by the references
|
RADIUS = 6378; // Earth radius in km, as used by the references
|
||||||
@ -110,7 +110,7 @@ begin
|
|||||||
);
|
);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMiscTests_Engine.Test_LatToStr_Deg;
|
procedure TMvTests_Engine.Test_LatToStr_Deg;
|
||||||
const
|
const
|
||||||
NO_DMS = false;
|
NO_DMS = false;
|
||||||
var
|
var
|
||||||
@ -125,7 +125,7 @@ begin
|
|||||||
);
|
);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMiscTests_Engine.Test_LatToStr_DMS;
|
procedure TMvTests_Engine.Test_LatToStr_DMS;
|
||||||
const
|
const
|
||||||
NEED_DMS = true;
|
NEED_DMS = true;
|
||||||
var
|
var
|
||||||
@ -140,7 +140,7 @@ begin
|
|||||||
);
|
);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMiscTests_Engine.Test_LonToStr_Deg;
|
procedure TMvTests_Engine.Test_LonToStr_Deg;
|
||||||
const
|
const
|
||||||
NO_DMS = false;
|
NO_DMS = false;
|
||||||
var
|
var
|
||||||
@ -155,7 +155,7 @@ begin
|
|||||||
);
|
);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMiscTests_Engine.Test_LonToStr_DMS;
|
procedure TMvTests_Engine.Test_LonToStr_DMS;
|
||||||
const
|
const
|
||||||
NEED_DMS = true;
|
NEED_DMS = true;
|
||||||
var
|
var
|
||||||
@ -170,7 +170,7 @@ begin
|
|||||||
);
|
);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMiscTests_Engine.Test_SplitGPS;
|
procedure TMvTests_Engine.Test_SplitGPS;
|
||||||
const
|
const
|
||||||
TOLERANCE = 1e-5;
|
TOLERANCE = 1e-5;
|
||||||
var
|
var
|
||||||
@ -218,7 +218,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMiscTests_Engine.Test_ZoomFactor;
|
procedure TMvTests_Engine.Test_ZoomFactor;
|
||||||
var
|
var
|
||||||
z: Integer;
|
z: Integer;
|
||||||
f: Extended;
|
f: Extended;
|
||||||
@ -236,6 +236,6 @@ initialization
|
|||||||
PointFormatSettings.DecimalSeparator := '.';
|
PointFormatSettings.DecimalSeparator := '.';
|
||||||
DMS_Decimals := 4;
|
DMS_Decimals := 4;
|
||||||
|
|
||||||
RegisterTest(TMiscTests_Engine);
|
RegisterTest(TMvTests_Engine);
|
||||||
end.
|
end.
|
||||||
|
|
436
components/lazmapviewer/unittests/mvtests_types.pas
Normal file
436
components/lazmapviewer/unittests/mvtests_types.pas
Normal file
@ -0,0 +1,436 @@
|
|||||||
|
unit mvtests_types;
|
||||||
|
|
||||||
|
{$mode objfpc}{$H+}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes, SysUtils, fpcunit, testutils, testregistry;
|
||||||
|
|
||||||
|
type
|
||||||
|
|
||||||
|
TMvTestsArea= class(TTestCase)
|
||||||
|
published
|
||||||
|
procedure Test_PointInArea;
|
||||||
|
procedure Test_Intersection;
|
||||||
|
procedure Test_Union;
|
||||||
|
end;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses
|
||||||
|
mvTypes;
|
||||||
|
|
||||||
|
function AreaToStr(Area: TRealArea): String;
|
||||||
|
begin
|
||||||
|
Result := Format('L=%.6f T=%.6f R=%.6f B=%.6f', [
|
||||||
|
Area.TopLeft.Lon, Area.TopLeft.Lat, Area.BottomRight.Lon, Area.BottomRight.Lat
|
||||||
|
]);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMvTestsArea.Test_PointInArea;
|
||||||
|
var
|
||||||
|
counter: Integer;
|
||||||
|
a: TRealArea;
|
||||||
|
p: TRealPoint;
|
||||||
|
begin
|
||||||
|
// Regular area, point inside
|
||||||
|
counter := 1;
|
||||||
|
a.Init(0, 10, 10, 0);
|
||||||
|
p.Init(5, 5);
|
||||||
|
AssertEquals(
|
||||||
|
'Point in area test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
true, // expected
|
||||||
|
a.ContainsPoint(p) // actual
|
||||||
|
);
|
||||||
|
|
||||||
|
// Regular area, point's longitude outside
|
||||||
|
inc(counter);
|
||||||
|
p.Init(15, 5);
|
||||||
|
AssertEquals(
|
||||||
|
'Point in area test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
false, // expected
|
||||||
|
a.ContainsPoint(p) // actual
|
||||||
|
);
|
||||||
|
|
||||||
|
// Regular area, point's latitude outside
|
||||||
|
inc(counter);
|
||||||
|
p.Init(5, 15);
|
||||||
|
AssertEquals(
|
||||||
|
'Point in area test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
false, // expected
|
||||||
|
a.ContainsPoint(p) // actual
|
||||||
|
);
|
||||||
|
|
||||||
|
// Area crossing dateline, point inside in the eastern part (left of dateline)
|
||||||
|
inc(counter);
|
||||||
|
a.Init(170, 40, -170, 30);
|
||||||
|
p.Init(175, 35);
|
||||||
|
AssertEquals(
|
||||||
|
'Point in area test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
true, // expected
|
||||||
|
a.ContainsPoint(p) // actual
|
||||||
|
);
|
||||||
|
|
||||||
|
// Area crossing dateline, point inside in the western part (right of dateline)
|
||||||
|
inc(counter);
|
||||||
|
a.Init(170, 40, -170, 30);
|
||||||
|
p.Init(-175, 35);
|
||||||
|
AssertEquals(
|
||||||
|
'Point in area test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
true, // expected
|
||||||
|
a.ContainsPoint(p) // actual
|
||||||
|
);
|
||||||
|
|
||||||
|
// Area crossing dateline, point at dateline (east)
|
||||||
|
inc(counter);
|
||||||
|
a.Init(170, 40, -170, 30);
|
||||||
|
p.Init(180, 35);
|
||||||
|
AssertEquals(
|
||||||
|
'Point in area test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
true, // expected
|
||||||
|
a.ContainsPoint(p) // actual
|
||||||
|
);
|
||||||
|
|
||||||
|
// Area crossing dateline, point at dateline (west)
|
||||||
|
inc(counter);
|
||||||
|
a.Init(170, 40, -170, 30);
|
||||||
|
p.Init(-180, 35);
|
||||||
|
AssertEquals(
|
||||||
|
'Point in area test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
true, // expected
|
||||||
|
a.ContainsPoint(p) // actual
|
||||||
|
);
|
||||||
|
|
||||||
|
// Area crossing dateline, point's longitude outside, eastern part
|
||||||
|
inc(counter);
|
||||||
|
a.Init(170, 40, -170, 30);
|
||||||
|
p.Init(160, 35);
|
||||||
|
AssertEquals(
|
||||||
|
'Point in area test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
false, // expected
|
||||||
|
a.ContainsPoint(p) // actual
|
||||||
|
);
|
||||||
|
|
||||||
|
// Area crossing dateline, point's longitude outside, western part
|
||||||
|
inc(counter);
|
||||||
|
a.Init(170, 40, -170, 30);
|
||||||
|
p.Init(-160, 35);
|
||||||
|
AssertEquals(
|
||||||
|
'Point in area test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
false, // expected
|
||||||
|
a.ContainsPoint(p) // actual
|
||||||
|
);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMvTestsArea.Test_Union;
|
||||||
|
var
|
||||||
|
counter: Integer;
|
||||||
|
a, b, expected, actual: TRealArea;
|
||||||
|
begin
|
||||||
|
// Regular areas, separated
|
||||||
|
counter := 1; // #1
|
||||||
|
a.Init(0, 10, 10, 0);
|
||||||
|
b.Init(20, 40, 30, 20);
|
||||||
|
expected.Init(0, 40, 30, 0);
|
||||||
|
actual := a.Union(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area union test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
AreaToStr(expected),
|
||||||
|
AreaToStr(actual)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Regular areas, partly overlapping
|
||||||
|
inc(counter); // #2
|
||||||
|
a.Init(0, 10, 10, 0);
|
||||||
|
b.Init(5, 40, 30, 20);
|
||||||
|
expected.Init(0, 40, 30, 0);
|
||||||
|
actual := a.Union(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area union test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
AreaToStr(expected),
|
||||||
|
AreaToStr(actual)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Regular areas, partly overlapping
|
||||||
|
inc(counter); // #3
|
||||||
|
a.Init(5, 10, 10, 0); // | x---x |
|
||||||
|
b.Init(0, 40, 30, 20); // | x-------x |
|
||||||
|
expected.Init(0, 40, 30, 0);
|
||||||
|
actual := a.Union(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area union test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
AreaToStr(expected),
|
||||||
|
AreaToStr(actual)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Regular areas, partly overlapping
|
||||||
|
inc(counter); // #4
|
||||||
|
a.Init(10, 10, 40, 0); // | x----x |
|
||||||
|
b.Init(0, 40, 20, 20); // | x---x |
|
||||||
|
expected.Init(0, 40, 40, 0);
|
||||||
|
actual := a.Union(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area union test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
AreaToStr(expected),
|
||||||
|
AreaToStr(actual)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Regular areas, partly overlapping
|
||||||
|
inc(counter); // #5
|
||||||
|
a.Init(10, 10, 40, 0); // | x----x |
|
||||||
|
b.Init(30, 40, 60, 20); // | x-----x |
|
||||||
|
expected.Init(10, 40, 60, 0);
|
||||||
|
actual := a.Union(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area union test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
AreaToStr(expected),
|
||||||
|
AreaToStr(actual)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Second area crossing dateline
|
||||||
|
inc(counter); // #6
|
||||||
|
a.Init(-90, 10, 90, 0); // | x------x |
|
||||||
|
b.Init(-140, 40, -160, 20); // |--x x------------------|
|
||||||
|
expected.Init(-140, 40, -160, 0);
|
||||||
|
actual := a.Union(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area union test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
AreaToStr(expected),
|
||||||
|
AreaToStr(actual)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Second area crossing dateline
|
||||||
|
inc(counter); // #7
|
||||||
|
a.Init(-90, 10, 90, 0); // | x------x |
|
||||||
|
b.Init(0, 40, -160, 20); // |--x x-------------|
|
||||||
|
expected.Init(-90, 40, -160, 0);
|
||||||
|
actual := a.Union(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area union test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
AreaToStr(expected),
|
||||||
|
AreaToStr(actual)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Second area crossing dateline
|
||||||
|
inc(counter); // #8
|
||||||
|
a.Init(-90, 10, 90, 0); // | x------x |
|
||||||
|
b.Init(160, 40, -160, 20); // |--x x------|
|
||||||
|
expected.Init(-90, 40, 90, 0);
|
||||||
|
actual := a.Union(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area union test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
AreaToStr(expected),
|
||||||
|
AreaToStr(actual)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Second area crossing dateline
|
||||||
|
inc(counter); // #9
|
||||||
|
a.Init(-90, 10, 90, 0); // | x------x |
|
||||||
|
b.Init(30, 40, -30, 20); // |---------x x-----------|
|
||||||
|
expected.Init(-180, 40, 180, 0);
|
||||||
|
actual := a.Union(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area union test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
AreaToStr(expected),
|
||||||
|
AreaToStr(actual)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Second area crossing dateline
|
||||||
|
inc(counter); // #10
|
||||||
|
a.Init(-90, 10, 90, 0); // | x------x |
|
||||||
|
b.Init(150, 40, -30, 20); // |---------x x----|
|
||||||
|
expected.Init(150, 40, 90, 0);
|
||||||
|
actual := a.Union(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area union test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
AreaToStr(expected),
|
||||||
|
AreaToStr(actual)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Second area crossing dateline
|
||||||
|
inc(counter); // #11
|
||||||
|
a.Init(-90, 10, 90, 0); // | x------x |
|
||||||
|
b.Init(150, 40, 120, 20); // |-----------------x x---|
|
||||||
|
expected.Init(150, 40, 120, 0);
|
||||||
|
actual := a.Union(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area union test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
AreaToStr(expected),
|
||||||
|
AreaToStr(actual)
|
||||||
|
);
|
||||||
|
|
||||||
|
// First area crossing dateline: like #6
|
||||||
|
inc(counter); // #12
|
||||||
|
a.Init(-140, 40, -160, 20); // |--x x------------------|
|
||||||
|
b.Init(-90, 10, 90, 0); // | x------x |
|
||||||
|
expected.Init(-140, 40, -160, 0);
|
||||||
|
actual := a.Union(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area union test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
AreaToStr(expected),
|
||||||
|
AreaToStr(actual)
|
||||||
|
);
|
||||||
|
// Skipping other "1st area crossing" tests since arguments are just flipped
|
||||||
|
|
||||||
|
// Both areas crossing dateline
|
||||||
|
inc(counter); // #13
|
||||||
|
a.Init(170, 60, -150, 50); // |---x x----|
|
||||||
|
b.Init(160, 30, -160, 10); // |----x x-----|
|
||||||
|
expected.Init(160, 60, -150, 10);
|
||||||
|
actual := a.Union(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area union test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
AreaToStr(expected),
|
||||||
|
AreaToStr(actual)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Both areas crossing dateline
|
||||||
|
inc(counter); // #43
|
||||||
|
a.Init(170, 60, 0, 50); // |--------x x----|
|
||||||
|
b.Init(160, 30, -160, 10); // |----x x-----|
|
||||||
|
expected.Init(160, 60, 0, 10);
|
||||||
|
actual := a.Union(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area union test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
AreaToStr(expected),
|
||||||
|
AreaToStr(actual)
|
||||||
|
);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMvTestsArea.Test_Intersection;
|
||||||
|
var
|
||||||
|
counter: Integer;
|
||||||
|
a, b, expected, actual: TRealArea;
|
||||||
|
intersects: Boolean;
|
||||||
|
begin
|
||||||
|
// Regular areas, separated
|
||||||
|
counter := 1;
|
||||||
|
a.Init(0, 10, 10, 0);
|
||||||
|
b.Init(20, 40, 30, 20);
|
||||||
|
intersects := a.Intersects(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area intersection detection test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
false,
|
||||||
|
intersects
|
||||||
|
);
|
||||||
|
|
||||||
|
// Regular areas, partly overlapping
|
||||||
|
inc(counter);
|
||||||
|
a.Init(0, 30, 20, 0);
|
||||||
|
b.Init(5, 40, 30, 20);
|
||||||
|
intersects := a.Intersects(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area intersection detection test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
true,
|
||||||
|
intersects
|
||||||
|
);
|
||||||
|
expected.Init(5, 30, 20, 20);
|
||||||
|
actual := a.Intersection(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area intersection test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
AreaToStr(expected),
|
||||||
|
AreaToStr(actual)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Regular areas, partly overlapping, reverse order
|
||||||
|
inc(counter);
|
||||||
|
a.Init(5, 40, 30, 20);
|
||||||
|
b.Init(0, 30, 20, 0);
|
||||||
|
intersects := a.Intersects(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area intersection detection test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
true,
|
||||||
|
intersects
|
||||||
|
);
|
||||||
|
expected.Init(5, 30, 20, 20);
|
||||||
|
actual := a.Intersection(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area intersection test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
AreaToStr(expected),
|
||||||
|
AreaToStr(actual)
|
||||||
|
);
|
||||||
|
|
||||||
|
// First area crossing date line, no overlaps
|
||||||
|
inc(counter);
|
||||||
|
a.Init(160, 40, -170, 20);
|
||||||
|
b.Init(-160, 30, -150, 0);
|
||||||
|
intersects := a.Intersects(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area intersection detection test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
false,
|
||||||
|
intersects
|
||||||
|
);
|
||||||
|
|
||||||
|
// First area crossing date line, overlaps on the left side of date lie
|
||||||
|
inc(counter);
|
||||||
|
a.Init(160, 40, -170, 20);
|
||||||
|
b.Init(165, 30, 170, 0);
|
||||||
|
intersects := a.Intersects(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area intersection detection test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
true,
|
||||||
|
intersects
|
||||||
|
);
|
||||||
|
expected.Init(165, 30, 170, 20);
|
||||||
|
actual := a.Intersection(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area intersection test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
AreaToStr(expected),
|
||||||
|
AreaToStr(actual)
|
||||||
|
);
|
||||||
|
|
||||||
|
// First area crossing date line, overlaps on the right side of date lie
|
||||||
|
inc(counter);
|
||||||
|
a.Init(160, 40, -160, 20);
|
||||||
|
b.Init(-170, 30, -165, 0);
|
||||||
|
intersects := a.Intersects(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area intersection detection test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
true,
|
||||||
|
intersects
|
||||||
|
);
|
||||||
|
expected.Init(-170, 30, -165, 20);
|
||||||
|
actual := a.Intersection(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area intersection test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
AreaToStr(expected),
|
||||||
|
AreaToStr(actual)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Second area crossing date line, no overlaps
|
||||||
|
inc(counter);
|
||||||
|
a.Init(-160, 30, -150, 0);
|
||||||
|
b.Init(160, 40, -170, 20);
|
||||||
|
intersects := a.Intersects(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area intersection detection test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
false,
|
||||||
|
intersects
|
||||||
|
);
|
||||||
|
|
||||||
|
// Second area crossing date line, overlaps on the left side of dateline
|
||||||
|
inc(counter);
|
||||||
|
a.Init(165, 30, 170, 0);
|
||||||
|
b.Init(160, 40, -170, 20);
|
||||||
|
intersects := a.Intersects(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area intersection detection test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
true,
|
||||||
|
intersects
|
||||||
|
);
|
||||||
|
expected.Init(165, 30, 170, 20);
|
||||||
|
actual := a.Intersection(b);
|
||||||
|
AssertEquals(
|
||||||
|
'Area intersection test #' + IntToStr(counter) + ' mismatch',
|
||||||
|
AreaToStr(expected),
|
||||||
|
AreaToStr(actual)
|
||||||
|
);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
initialization
|
||||||
|
RegisterTest(TMvTestsArea);
|
||||||
|
|
||||||
|
end.
|
||||||
|
|
Reference in New Issue
Block a user