
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@9636 8e941d3f-bd1b-0410-a28a-d453659cc2b4
1140 lines
27 KiB
ObjectPascal
1140 lines
27 KiB
ObjectPascal
{ Map Viewer - basic gps object
|
|
(C) 2014 ti_dic@hotmail.com
|
|
|
|
License: modified LGPL with linking exception (like RTL, FCL and LCL)
|
|
|
|
See the file COPYING.modifiedLGPL.txt, included in the Lazarus distribution,
|
|
for details about the license.
|
|
|
|
See also: https://wiki.lazarus.freepascal.org/FPC_modified_LGPL
|
|
}
|
|
|
|
unit mvGpsObj;
|
|
|
|
{$mode objfpc}{$H+}
|
|
{$WARN 6058 off : Call to subroutine "$1" marked as inline is not inlined}
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, Graphics, fgl, contnrs, syncobjs,
|
|
mvTypes, mvGeoMath;
|
|
|
|
const
|
|
NO_ELEVATION = -10000000;
|
|
NO_ELE = NO_ELEVATION; // deprecated: use NO_ELEVATION
|
|
NO_DATE = 0;
|
|
|
|
type
|
|
TIdArray = Array of integer;
|
|
TGPSObj = class;
|
|
TGPSObjClass = class of TGPSObj;
|
|
|
|
TGPSObjList_ = specialize TFPGObjectList<TGPSObj>;
|
|
|
|
{ TGPSObjList }
|
|
|
|
TGPSObjList = class(TGPSObjList_)
|
|
private
|
|
FRef: TObject;
|
|
public
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ TGPSObjEnumerator }
|
|
|
|
TGPSObjEnumerator = class
|
|
private
|
|
function GetCurrent: TGPSObj; virtual; abstract;
|
|
public
|
|
function GetEnumerator: TGPSObjEnumerator;
|
|
function MoveNext: Boolean; virtual; abstract;
|
|
property Current: TGPSObj read GetCurrent;
|
|
end;
|
|
|
|
{ TGPSObjDrawEvent }
|
|
|
|
TGPSObjDrawEvent = procedure(Sender: TObject; AGPSObj: TGPSObj;
|
|
AArea: TRealArea) of object;
|
|
|
|
{ TGPSObj }
|
|
|
|
TGPSObj = class
|
|
private
|
|
//BBoxSet: Boolean;
|
|
FExtraData: TObject;
|
|
FName: String;
|
|
FIdOwner: integer;
|
|
FOnDrawObj: TGPSObjDrawEvent;
|
|
FVisible: Boolean;
|
|
FZOrder: Integer;
|
|
function GetBoundingBox: TRealArea;
|
|
function GetAllObjs: TGPSObjEnumerator; virtual;
|
|
procedure SetExtraData(AValue: TObject);
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Assign(AObj: TGPSObj); virtual;
|
|
procedure GetArea(out Area: TRealArea); virtual; abstract;
|
|
procedure Draw(AView: TObject; Area: TRealArea); virtual; abstract;
|
|
property Name: String read FName write FName;
|
|
property ExtraData: TObject read FExtraData write SetExtraData;
|
|
property IdOwner: Integer read FIdOwner;
|
|
property BoundingBox: TRealArea read GetBoundingBox;
|
|
property ZOrder: Integer read FZOrder;
|
|
property Visible: Boolean read FVisible write FVisible;
|
|
property AllObjs: TGPSObjEnumerator read GetAllObjs;
|
|
property OnDrawObj: TGPSObjDrawEvent read FOnDrawObj write FOnDrawObj;
|
|
end;
|
|
|
|
TGPSObjarray = Array of TGPSObj;
|
|
|
|
{ TGPSPoint }
|
|
|
|
TGPSPoint = class(TGPSObj)
|
|
private
|
|
FRealPt: TRealPoint;
|
|
FElevation: Double;
|
|
FDateTime: TDateTime;
|
|
function GetLat: Double;
|
|
function GetLon: Double;
|
|
procedure SetLat(AValue: Double);
|
|
procedure SetLon(AValue: Double);
|
|
public
|
|
constructor Create(ALon,ALat: double; AElevation: double = NO_ELEVATION;
|
|
ADateTime: TDateTime = NO_DATE);
|
|
class function CreateFrom(aPt: TRealPoint; AElevation: Double = NO_ELEVATION;
|
|
ADateTime: TDateTime = NO_DATE): TGPSPoint;
|
|
|
|
procedure Assign(AObj: TGPSObj); override;
|
|
procedure GetArea(out Area: TRealArea);override;
|
|
procedure Draw({%H-}AView: TObject; {%H-}Area: TRealArea); override;
|
|
function HasElevation: boolean;
|
|
function HasDateTime: Boolean;
|
|
function DistanceInKmFrom(OtherPt: TGPSPoint; UseElevation: boolean=true): double;
|
|
procedure MoveTo(ALon, ALat: Double; AElevation: double = NO_ELE;
|
|
ADateTime: TDateTime = NO_DATE);
|
|
|
|
property Lon: Double read GetLon write SetLon;
|
|
property Lat: Double read GetLat write SetLat;
|
|
property Elevation: double read FElevation write FElevation;
|
|
property DateTime: TDateTime read FDateTime write FDateTime;
|
|
property RealPoint: TRealPoint read FRealPt;
|
|
end;
|
|
|
|
TGPSPointList = specialize TFPGObjectList<TGPSPoint>;
|
|
|
|
{ TGPSPointOfInterest }
|
|
|
|
TGPSPointOfInterest = class(TGPSPoint)
|
|
private
|
|
FImageAnchorX: Integer;
|
|
FImageAnchorY: Integer;
|
|
FImageIndex: Integer;
|
|
FTextPositionHor: TTextPositionHor;
|
|
FTextPositionVert: TTextPositionVert;
|
|
public
|
|
constructor Create(ALon, ALat: Double; AElevation: Double = NO_ELEVATION;
|
|
ADateTime: TDateTime = NO_DATE);
|
|
procedure Draw({%H-}AView: TObject; {%H-}Area: TRealArea); override;
|
|
property ImageAnchorX: Integer read FImageAnchorX write FImageAnchorX default 50; // Percentage!
|
|
property ImageAnchorY: Integer read FImageAnchorY write FImageAnchorY default 100; // Percentage!
|
|
property ImageIndex: Integer read FImageIndex write FImageIndex default -1;
|
|
property TextPositionHor: TTextPositionHor read FTextPositionHor write FTextPositionHor default tphCenter;
|
|
property TextPositionVert: TTextPositionVert read FTextPositionVert write FTextPositionVert default tpvBelow;
|
|
end;
|
|
|
|
{ TGPSPolyLine }
|
|
|
|
TGPSPolyLine = class(TGPSObj)
|
|
private
|
|
FPoints: TGPSPointList;
|
|
function GetAllObjs: TGPSObjEnumerator; override;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure GetArea(out Area: TRealArea); override;
|
|
property Points: TGPSPointList read FPoints;
|
|
end;
|
|
|
|
{ TGPSTrack }
|
|
|
|
TGPSTrack = class(TGPSPolyLine)
|
|
private
|
|
FConnectColor: TColor;
|
|
FConnectWidth: Double;
|
|
FDateTime: TDateTime;
|
|
FLineWidth: Double; // Line width in mm
|
|
FLineColor: TColor;
|
|
FOpacity: Single;
|
|
function GetDateTime: TDateTime;
|
|
public
|
|
constructor Create;
|
|
|
|
procedure Draw({%H-}AView: TObject; {%H-}Area: TRealArea); override;
|
|
function TrackLengthInKm(UseEle: Boolean=true): double;
|
|
|
|
property DateTime: TDateTime read GetDateTime write FDateTime;
|
|
property LineColor: TColor read FLineColor write FLineColor;
|
|
property LineWidth: Double read FLineWidth write FLineWidth;
|
|
property ConnectColor: TColor read FConnectColor write FConnectColor;
|
|
property ConnectWidth: Double read FConnectWidth write FConnectWidth;
|
|
property Opacity: Single read FOpacity write FOpacity;
|
|
end;
|
|
|
|
{ TGPSArea }
|
|
|
|
TGPSArea = class(TGPSPolyLine)
|
|
private
|
|
FFillColor: TColor;
|
|
FLineColor: TColor;
|
|
FLineWidth: Double;
|
|
FOpacity: Single;
|
|
public
|
|
constructor Create;
|
|
procedure Draw({%H-}AView: TObject; {%H-}Area: TRealArea); override;
|
|
property FillColor: TColor read FFillColor write FFillColor;
|
|
property LineColor: TColor read FLineColor write FLineColor;
|
|
property LineWidth: Double read FLineWidth write FLineWidth;
|
|
property Opacity: Single read FOpacity write FOpacity;
|
|
end;
|
|
|
|
{ TGPSObjectList }
|
|
|
|
TModifiedEvent = procedure (Sender: TObject; objs: TGPSObjList;
|
|
Adding: boolean) of object;
|
|
|
|
TGPSObjectList = class(TGPSObj)
|
|
private
|
|
Crit: TCriticalSection;
|
|
FPending: TObjectList;
|
|
FRefCount: integer;
|
|
FOnModified: TModifiedEvent;
|
|
FUpdating: integer;
|
|
FItems: TGPSObjList;
|
|
function GetCount: integer;
|
|
function GetItem(AIndex: Integer): TGpsObj;
|
|
function GetAllObjs: TGPSObjEnumerator; override;
|
|
protected
|
|
procedure _Delete(Idx: Integer; var DelLst: TGPSObjList);
|
|
procedure FreePending;
|
|
procedure DecRef;
|
|
procedure CallModified(lst: TGPSObjList; Adding: boolean);
|
|
// property Items: TGPSObjList read FItems;
|
|
procedure IdsToObj(const Ids: TIdArray; out objs: TGPSObjArray; AIdOwner: integer);
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
Procedure ClearAll;
|
|
Procedure Clear(OwnedBy: integer);
|
|
procedure ClearExcept(OwnedBy: integer; const ExceptLst: TIdArray;
|
|
out Notfound: TIdArray);
|
|
procedure GetArea(out Area: TRealArea); override;
|
|
procedure Draw({%H-}AView: TObject; {%H-}Area: TRealArea); override;
|
|
function GetObjectsInArea(const Area: TRealArea; AClass: TGPSObjClass = Nil): TGPSObjList;
|
|
function GetIdsArea(const Ids: TIdArray; AIdOwner: integer): TRealArea;
|
|
|
|
function Add(aItem: TGpsObj; AIdOwner: Integer; AZOrder: Integer = 0): Integer;
|
|
function Delete(AItem: TGPSObj): Boolean;
|
|
function IndexOf(AItem: TGPSObj): Integer;
|
|
function ChangeZOrder(AItem: TGPSObj; ANewZOrder: Integer): Integer;
|
|
function MoveToBack(AItem: TGPSObj): Integer;
|
|
function MoveToFront(AItem: TGPSObj): Integer;
|
|
procedure DeleteById(const Ids: Array of integer);
|
|
function FindTrackByID(const id: Integer): TGpsTrack;
|
|
|
|
procedure BeginUpdate;
|
|
procedure EndUpdate;
|
|
|
|
procedure Lock;
|
|
procedure UnLock;
|
|
|
|
property Count: integer read GetCount;
|
|
property Items[AIndex: Integer]: TGpsObj read GetItem; default;
|
|
property OnModified: TModifiedEvent read FOnModified write FOnModified;
|
|
end;
|
|
|
|
function HasIntersectArea(const Area1: TRealArea; const Area2: TRealArea): boolean;
|
|
function IntersectArea(const Area1: TRealArea; const Area2: TRealArea): TRealArea;
|
|
function PtInsideArea(const aPoint: TRealPoint; const Area: TRealArea): boolean;
|
|
function AreaInsideArea(const AreaIn: TRealArea; const AreaOut: TRealArea): boolean;
|
|
procedure ExtendArea(var AreaToExtend: TRealArea; const Area: TRealArea);
|
|
function GetAreaOf(objs: TGPSObjList): TRealArea;
|
|
function GoingEast(Lon1, Lon2: Double): Boolean;
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
mvExtraData, mvMapViewer, Math;
|
|
|
|
type
|
|
{ TGPSObjEnumeratorFrom }
|
|
|
|
generic TGPSObjEnumeratorFrom<T: Class> = class(TGPSObjEnumerator)
|
|
private
|
|
type
|
|
TItemClass = T;
|
|
TOwnerListClass = specialize TFPGObjectList<T>;
|
|
private
|
|
FList: TOwnerListClass;
|
|
FCurrent: TItemClass;
|
|
FIndex: Integer;
|
|
function GetCurrent: TGPSObj; override;
|
|
public
|
|
constructor Create(AList: TOwnerListClass);
|
|
function MoveNext: Boolean; override;
|
|
end;
|
|
|
|
function GoingEast(Lon1, Lon2: Double): Boolean;
|
|
begin
|
|
// Assume the shortest path (<180 deg)
|
|
Result := ((Lon1 < Lon2) and (Lon2 - Lon1 < 180.0))
|
|
or ((Lon1 > 0) and (Lon2 < 0) and (Lon1 - Lon2 > 180.0));
|
|
end;
|
|
|
|
function hasIntersectArea(const Area1: TRealArea; const Area2: TRealArea): boolean;
|
|
begin
|
|
Result := Area1.Intersects(Area2);
|
|
end;
|
|
|
|
function IntersectArea(const Area1: TRealArea; const Area2: TRealArea): TRealArea;
|
|
begin
|
|
Result := Area1.Intersection(Area2);
|
|
end;
|
|
|
|
function PtInsideArea(const aPoint: TRealPoint; const Area: TRealArea): boolean;
|
|
begin
|
|
Result := Area.ContainsPoint(aPoint);
|
|
end;
|
|
|
|
function AreaInsideArea(const AreaIn: TRealArea; const AreaOut: TRealArea): boolean;
|
|
begin
|
|
Result := AreaIn.Equal(AreaIn.Intersection(AreaOut));
|
|
end;
|
|
|
|
procedure ExtendArea(var AreaToExtend: TRealArea; const Area: TRealArea);
|
|
begin
|
|
AreaToExtend := AreaToExtend.Union(Area);
|
|
end;
|
|
|
|
function GetAreaOf(objs: TGPSObjList): TRealArea;
|
|
var
|
|
i: integer;
|
|
begin
|
|
Result.Init(0, 0, 0, 0);
|
|
if Objs.Count > 0 then
|
|
begin
|
|
Result := Objs[0].BoundingBox;
|
|
for i:=1 to pred(Objs.Count) do
|
|
ExtendArea(Result, Objs[i].BoundingBox);
|
|
end;
|
|
end;
|
|
|
|
{ TGPSObjEnumeratorFrom }
|
|
|
|
function TGPSObjEnumeratorFrom.GetCurrent: TGPSObj;
|
|
begin
|
|
Result := FCurrent;
|
|
end;
|
|
|
|
constructor TGPSObjEnumeratorFrom.Create(AList: TOwnerListClass);
|
|
begin
|
|
FList := AList;
|
|
FIndex := -1;
|
|
end;
|
|
|
|
function TGPSObjEnumeratorFrom.MoveNext: Boolean;
|
|
begin
|
|
Inc(FIndex);
|
|
Result := FIndex < FList.Count;
|
|
if Result then
|
|
FCurrent := FList[FIndex];
|
|
end;
|
|
|
|
{ TGPSObjEnumerator }
|
|
|
|
function TGPSObjEnumerator.GetEnumerator: TGPSObjEnumerator;
|
|
begin
|
|
Result := Self;
|
|
end;
|
|
|
|
{ TGPSArea }
|
|
|
|
constructor TGPSArea.Create;
|
|
begin
|
|
inherited;
|
|
FFillColor := clNone;
|
|
FLineColor := clDefault; // --> use MapView.DefaultTrackColor
|
|
FLineWidth := -1; // --> use MapView.DefaultTrackWidth
|
|
FOpacity := 1.0;
|
|
end;
|
|
|
|
procedure TGPSArea.Draw(AView: TObject; Area: TRealArea);
|
|
begin
|
|
if Assigned(FOnDrawObj)
|
|
then FOnDrawObj(AView, Self, Area)
|
|
else TMapView(AView).DrawArea(Area, Self);
|
|
end;
|
|
|
|
{ TGPSPolyLine }
|
|
|
|
function TGPSPolyLine.GetAllObjs: TGPSObjEnumerator;
|
|
begin
|
|
Result := specialize TGPSObjEnumeratorFrom<TGPSPoint>.Create(FPoints);
|
|
end;
|
|
|
|
constructor TGPSPolyLine.Create;
|
|
begin
|
|
inherited;
|
|
FPoints := TGPSPointList.Create(true);
|
|
end;
|
|
|
|
destructor TGPSPolyLine.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
FreeAndNil(FPoints);
|
|
end;
|
|
|
|
procedure TGPSPolyLine.GetArea(out Area: TRealArea);
|
|
var
|
|
i: integer;
|
|
ptArea: TRealArea;
|
|
pt1, pt2: TRealPoint;
|
|
begin
|
|
Area.Init(0, 0, 0, 0);
|
|
if FPoints.Count > 0 then
|
|
begin
|
|
pt1 := FPoints[0].RealPoint;
|
|
Area := FPoints[0].BoundingBox;
|
|
for i:=1 to pred(FPoints.Count) do
|
|
begin
|
|
pt2 := FPoints[I].RealPoint;
|
|
if GoingEast(pt1.Lon, pt2.Lon)
|
|
then ptArea.Init(pt1.Lon, Max(pt1.Lat, pt2.Lat), pt2.Lon, Min(pt1.Lat, pt2.Lat))
|
|
else ptArea.Init(pt2.Lon, Max(pt1.Lat, pt2.Lat), pt1.Lon, Min(pt1.Lat, pt2.Lat));
|
|
ExtendArea(Area, ptArea);
|
|
pt1 := pt2;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ TGPSObjList }
|
|
|
|
destructor TGPSObjList.Destroy;
|
|
begin
|
|
if Assigned(FRef) then
|
|
TGPSObjectList(FRef).DecRef;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ TGPSObj }
|
|
|
|
procedure TGPSObj.Assign(AObj: TGPSObj);
|
|
begin
|
|
FName := AObj.Name;
|
|
end;
|
|
|
|
procedure TGPSObj.SetExtraData(AValue: TObject);
|
|
begin
|
|
if FExtraData=AValue then Exit;
|
|
if Assigned(FExtraData) then
|
|
FreeAndNil(FExtraData);
|
|
FExtraData := AValue;
|
|
end;
|
|
|
|
constructor TGPSObj.Create;
|
|
begin
|
|
FVisible := True;
|
|
end;
|
|
|
|
function TGPSObj.GetBoundingBox: TRealArea;
|
|
var
|
|
A: TRealArea;
|
|
begin
|
|
GetArea(A);
|
|
Result := A;
|
|
end;
|
|
|
|
function TGPSObj.GetAllObjs: TGPSObjEnumerator;
|
|
begin
|
|
Result := Nil;
|
|
end;
|
|
|
|
destructor TGPSObj.Destroy;
|
|
begin
|
|
FreeAndNil(FExtraData);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ TGPSObjectList }
|
|
|
|
function TGPSObjectList.GetCount: integer;
|
|
begin
|
|
Result := FItems.Count
|
|
end;
|
|
|
|
function TGPSObjectList.GetItem(AIndex: Integer): TGpsObj;
|
|
begin
|
|
Result := FItems[AIndex];
|
|
end;
|
|
|
|
procedure TGPSObjectList._Delete(Idx: Integer; var DelLst: TGPSObjList); // wp: was "out"
|
|
var
|
|
Item: TGpsObj;
|
|
begin
|
|
Lock;
|
|
try
|
|
if not(Assigned(DelLst)) then
|
|
begin
|
|
DelLst := TGpsObjList.Create(False);
|
|
DelLst.FRef := Self;
|
|
inc(FRefCount);
|
|
end;
|
|
if not Assigned(FPending) then
|
|
FPending := TObjectList.Create(true);
|
|
Item := FItems.Extract(FItems[Idx]);
|
|
FPending.Add(Item);
|
|
finally
|
|
UnLock;
|
|
end;
|
|
DelLst.Add(Item);
|
|
end;
|
|
|
|
procedure TGPSObjectList.FreePending;
|
|
begin
|
|
if Assigned(FPending) then
|
|
begin
|
|
Lock;
|
|
try
|
|
FreeAndNil(FPending);
|
|
finally
|
|
UnLock;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TGPSObjectList.DecRef;
|
|
begin
|
|
FRefCount-=1;
|
|
if FRefCount=0 then
|
|
FreePending;
|
|
end;
|
|
|
|
procedure TGPSObjectList.Lock;
|
|
begin
|
|
if Assigned(Crit) then
|
|
Crit.Enter;
|
|
end;
|
|
|
|
procedure TGPSObjectList.UnLock;
|
|
begin
|
|
if Assigned(Crit) then
|
|
Crit.Leave;
|
|
end;
|
|
|
|
procedure TGPSObjectList.CallModified(lst: TGPSObjList; Adding: boolean);
|
|
begin
|
|
if (FUpdating=0) and Assigned(FOnModified) then
|
|
FOnModified(self, lst, Adding)
|
|
end;
|
|
|
|
procedure TGPSObjectList.IdsToObj(const Ids: TIdArray; out objs: TGPSObjArray;
|
|
AIdOwner: integer);
|
|
|
|
function ToSelect(aId: integer): boolean;
|
|
var
|
|
i: integer;
|
|
begin
|
|
result := false;
|
|
for i:=low(Ids) to high(Ids) do
|
|
if Ids[i]=aId then
|
|
begin
|
|
result := true;
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
i,nb : integer;
|
|
begin
|
|
objs := nil;
|
|
SetLength(objs, Length(Ids));
|
|
nb := 0;
|
|
Lock;
|
|
try
|
|
for i:=0 to pred(FItems.Count) do
|
|
begin
|
|
if (AIdOwner = 0) or (AIdOwner = FItems[i].FIdOwner) then
|
|
if Assigned(FItems[i].ExtraData) and FItems[i].ExtraData.InheritsFrom(TDrawingExtraData) then
|
|
begin
|
|
if ToSelect(TDrawingExtraData(FItems[i].ExtraData).Id) then
|
|
begin
|
|
objs[nb] := FItems[i];
|
|
nb+=1;
|
|
end;
|
|
end;
|
|
end;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
SetLength(objs, nb);
|
|
end;
|
|
|
|
function TGPSObjectList.GetAllObjs: TGPSObjEnumerator;
|
|
begin
|
|
Result := specialize TGPSObjEnumeratorFrom<TGPSObj>.Create(FItems);
|
|
end;
|
|
|
|
procedure TGPSObjectList.GetArea(out Area: TRealArea);
|
|
var
|
|
i: integer;
|
|
ptArea: TRealArea;
|
|
begin
|
|
Area.Init(0, 0, 0, 0);
|
|
Lock;
|
|
try
|
|
if Count > 0 then
|
|
begin
|
|
Area := Items[0].BoundingBox;
|
|
for i:=1 to pred(Count) do
|
|
begin
|
|
ptArea := Items[i].BoundingBox;
|
|
ExtendArea(Area, ptArea);
|
|
end;
|
|
end;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
procedure TGPSObjectList.Draw(AView: TObject; Area: TRealArea);
|
|
begin
|
|
//;
|
|
end;
|
|
|
|
function TGPSObjectList.GetObjectsInArea(const Area: TRealArea;
|
|
AClass: TGPSObjClass {= Nil}): TGPSObjList;
|
|
var
|
|
I: integer;
|
|
ItemArea: TRealArea;
|
|
begin
|
|
Result := TGPSObjList.Create(false);
|
|
Lock;
|
|
try
|
|
Inc(FRefCount);
|
|
for I := 0 to Pred(Count) do
|
|
begin
|
|
ItemArea := Items[I].BoundingBox;
|
|
if (not Assigned(AClass) or (Items[I] is AClass)) and
|
|
hasIntersectArea(Area,ItemArea)
|
|
then
|
|
Result.Add(Items[I]);
|
|
end;
|
|
if Result.Count > 0 then
|
|
Result.FRef := Self
|
|
else
|
|
Dec(FRefCount);
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
constructor TGPSObjectList.Create;
|
|
begin
|
|
inherited;
|
|
Crit := TCriticalSection.Create;
|
|
FItems := TGPSObjList.Create(true);
|
|
end;
|
|
|
|
destructor TGPSObjectList.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
FreeAndNil(FItems);
|
|
FreeAndNil(FPending);
|
|
FreeAndNil(Crit);
|
|
end;
|
|
|
|
procedure TGPSObjectList.ClearAll;
|
|
begin
|
|
Lock;
|
|
try
|
|
FItems.Clear;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
procedure TGPSObjectList.Clear(OwnedBy: integer);
|
|
var
|
|
i: integer;
|
|
DelObj: TGPSObjList;
|
|
begin
|
|
DelObj := nil;
|
|
Lock;
|
|
try
|
|
for i:=pred(FItems.Count) downto 0 do
|
|
if (OwnedBy = 0) or (FItems[i].FIdOwner = OwnedBy) then
|
|
_Delete(i,DelObj);
|
|
finally
|
|
Unlock;
|
|
end;
|
|
if Assigned(DelObj) then
|
|
begin
|
|
CallModified(DelObj, false);
|
|
DelObj.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TGPSObjectList.ClearExcept(OwnedBy: integer;
|
|
const ExceptLst: TIdArray; out Notfound: TIdArray);
|
|
|
|
var
|
|
Found: TIdArray;
|
|
|
|
function ToDel(aIt: TGPsObj): boolean;
|
|
var
|
|
i,Id: integer;
|
|
begin
|
|
if (aIt.ExtraData=nil) or not(aIt.ExtraData.InheritsFrom(TDrawingExtraData)) then
|
|
Result := true
|
|
else
|
|
Begin
|
|
Result := true;
|
|
Id := TDrawingExtraData(aIt.ExtraData).Id;
|
|
for i := Low(ExceptLst) to High(ExceptLst) do
|
|
if Id = ExceptLst[i] then
|
|
begin
|
|
Result := false;
|
|
SetLength(Found, Length(Found)+1);
|
|
Found[high(Found)] := Id;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
i,j: integer;
|
|
IsFound: boolean;
|
|
DelLst: TGPSObjList;
|
|
begin
|
|
DelLst := nil;
|
|
SetLength(NotFound{%H-}, 0);
|
|
SetLength(Found, 0);
|
|
Lock;
|
|
try
|
|
for i := pred(FItems.Count) downto 0 do
|
|
begin
|
|
if (FItems[i].FIdOwner = OwnedBy) or (OwnedBy = 0) then
|
|
Begin
|
|
if ToDel(FItems[i]) then
|
|
_Delete(i,DelLst);
|
|
end;
|
|
end;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
for i:=low(ExceptLst) to high(ExceptLst) do
|
|
begin
|
|
IsFound := false;
|
|
for j:=Low(Found) to High(Found) do
|
|
if Found[j] = ExceptLst[i] then
|
|
begin
|
|
IsFound := true;
|
|
break;
|
|
end;
|
|
if not IsFound then
|
|
begin
|
|
SetLength(NotFound, Length(NotFound)+1);
|
|
NotFound[high(NotFound)] := ExceptLst[i];
|
|
end;
|
|
end;
|
|
if Assigned(DelLst) then
|
|
begin
|
|
CallModified(DelLst, false);
|
|
DelLst.Free;
|
|
end;
|
|
end;
|
|
|
|
function TGPSObjectList.GetIdsArea(const Ids: TIdArray; AIdOwner: integer): TRealArea;
|
|
var
|
|
Objs: TGPSObjarray;
|
|
i: integer;
|
|
begin
|
|
Result.Init(0, 0, 0, 0);
|
|
Lock;
|
|
try
|
|
IdsToObj(Ids, Objs, AIdOwner);
|
|
if Length(Objs) > 0 then
|
|
begin
|
|
Result := Objs[0].BoundingBox;
|
|
for i:=succ(Low(Objs)) to High(Objs) do
|
|
ExtendArea(Result, Objs[i].BoundingBox);
|
|
end;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
function TGPSObjectList.Add(aItem: TGpsObj; AIdOwner: Integer; AZOrder: Integer
|
|
): Integer;
|
|
var
|
|
mList: TGPSObjList;
|
|
|
|
// Returns index _after_ the rightmost less or equal
|
|
function FindLeftmost: Integer;
|
|
var
|
|
L, M: Integer;
|
|
begin
|
|
L := 0; Result := FItems.Count;
|
|
while L < Result do
|
|
begin
|
|
M := (L + Result) div 2;
|
|
if FItems[M].ZOrder > AZOrder
|
|
then Result := M
|
|
else L := Succ(M);
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
aItem.FIdOwner := AIdOwner;
|
|
aItem.FZOrder := AZOrder;
|
|
Lock;
|
|
try
|
|
Result := FindLeftmost;
|
|
FItems.Insert(Result, aItem);
|
|
mList := TGPSObjList.Create(false);
|
|
mList.Add(aItem);
|
|
inc(FRefCount);
|
|
mList.FRef := Self;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
CallModified(mList, true);
|
|
mList.Free;
|
|
end;
|
|
|
|
function TGPSObjectList.IndexOf(AItem: TGPSObj): Integer;
|
|
begin
|
|
Result := FItems.IndexOf(AItem);
|
|
end;
|
|
|
|
function TGPSObjectList.Delete(AItem: TGPSObj): Boolean;
|
|
var
|
|
DelLst: TGPSObjList;
|
|
I: Integer;
|
|
begin
|
|
DelLst := Nil;
|
|
Lock;
|
|
try
|
|
I := FItems.IndexOf(AItem);
|
|
Result := -1 < I;
|
|
if Result then
|
|
_Delete(I, DelLst);
|
|
finally
|
|
Unlock;
|
|
end;
|
|
if Assigned(DelLst) then
|
|
begin
|
|
CallModified(DelLst, False);
|
|
DelLst.Free;
|
|
end;
|
|
end;
|
|
|
|
function TGPSObjectList.ChangeZOrder(AItem: TGPSObj; ANewZOrder: Integer
|
|
): Integer;
|
|
var
|
|
Item: TGPSObj;
|
|
begin
|
|
Lock;
|
|
try
|
|
Result := -1;
|
|
Item := FItems.Extract(AItem);
|
|
if Assigned(Item) then
|
|
begin
|
|
Result := Add(AItem, AItem.IdOwner, ANewZOrder);
|
|
CallModified(Nil, True); // Like EndUpdate?
|
|
end;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
function TGPSObjectList.MoveToBack(AItem: TGPSObj): Integer;
|
|
begin
|
|
Result := FItems.IndexOf(AItem);
|
|
if Result > 0 then
|
|
Result := ChangeZOrder(AItem, Pred(FItems.First.ZOrder))
|
|
end;
|
|
|
|
function TGPSObjectList.MoveToFront(AItem: TGPSObj): Integer;
|
|
begin
|
|
Result := FItems.IndexOf(AItem);
|
|
if (Result >= 0) and (Result < Pred(FItems.Count)) then
|
|
Result := ChangeZOrder(AItem, FItems.Last.ZOrder);
|
|
end;
|
|
|
|
procedure TGPSObjectList.DeleteById(const Ids: array of integer);
|
|
|
|
function ToDelete(const AId: integer): Boolean;
|
|
var
|
|
i: integer;
|
|
begin
|
|
result := false;
|
|
For i:=Low(Ids) to High(Ids) do
|
|
if Ids[i] = AId then
|
|
begin
|
|
result := true;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
Extr: TDrawingExtraData;
|
|
i: integer;
|
|
DelLst: TGPSObjList;
|
|
begin
|
|
DelLst := nil;
|
|
Lock;
|
|
try
|
|
for i:=pred(Count) downto 0 do
|
|
begin
|
|
if Assigned(Items[i].ExtraData) then
|
|
begin
|
|
if Items[i].ExtraData.InheritsFrom(TDrawingExtraData) then
|
|
begin
|
|
Extr := TDrawingExtraData(Items[i].ExtraData);
|
|
if ToDelete(Extr.Id) then
|
|
_Delete(i, DelLst);
|
|
end;
|
|
end;
|
|
end;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
if Assigned(DelLst) then
|
|
begin
|
|
CallModified(DelLst, false);
|
|
DelLst.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TGPSObjectList.BeginUpdate;
|
|
begin
|
|
inc(FUpdating);
|
|
end;
|
|
|
|
procedure TGPSObjectList.EndUpdate;
|
|
begin
|
|
if FUpdating > 0 then
|
|
begin
|
|
Dec(FUpdating);
|
|
if FUpdating = 0 then
|
|
CallModified(nil, true);
|
|
end;
|
|
end;
|
|
|
|
function TGPSObjectList.FindTrackByID(const id: Integer): TGpsTrack;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to pred(FItems.Count) do
|
|
if (ID = FItems[i].IdOwner) and (FItems[i] is TGpsTrack) then
|
|
begin
|
|
Result := TGpsTrack(FItems[i]);
|
|
exit;
|
|
end;
|
|
Result := nil;
|
|
end;
|
|
|
|
|
|
{ TGPSTrack }
|
|
|
|
function TGPSTrack.GetDateTime: TDateTime;
|
|
begin
|
|
if FDateTime = 0 then
|
|
Begin
|
|
if FPoints.Count > 0 then
|
|
FDateTime := FPoints[0].DateTime;
|
|
end;
|
|
Result := FDateTime;
|
|
end;
|
|
|
|
constructor TGPSTrack.Create;
|
|
begin
|
|
inherited;
|
|
FLineColor := clDefault; // --> use MapView.DefaultTrackColor
|
|
FLineWidth := -1; // --> use MapView.DefaultTrackWidth
|
|
FConnectColor := clNone; // --> None, clDefault for LineColor
|
|
FConnectWidth := -1; // --> use LineWidth
|
|
FOpacity := 1.0;
|
|
end;
|
|
|
|
|
|
procedure TGPSTrack.Draw(AView: TObject; Area: TRealArea);
|
|
begin
|
|
if Assigned(FOnDrawObj)
|
|
then FOnDrawObj(AView, Self, Area)
|
|
else TMapView(AView).DrawTrack(Area, Self);
|
|
end;
|
|
|
|
function TGPSTrack.TrackLengthInKm(UseEle: Boolean): double;
|
|
var
|
|
i: integer;
|
|
begin
|
|
Result := 0;
|
|
for i:=1 to pred(FPoints.Count) do
|
|
result += FPoints[i].DistanceInKmFrom(FPoints[pred(i)], UseEle);
|
|
end;
|
|
|
|
|
|
{ TGPSPoint }
|
|
|
|
constructor TGPSPoint.Create(ALon, ALat: double; AElevation: double;
|
|
ADateTime: TDateTime);
|
|
begin
|
|
inherited Create;
|
|
MoveTo(ALon, ALat, AElevation, ADateTime);
|
|
end;
|
|
|
|
class function TGPSPoint.CreateFrom(aPt: TRealPoint;
|
|
AElevation: Double = NO_ELEVATION; ADateTime: TDateTime = NO_DATE): TGPSPoint;
|
|
begin
|
|
Result := Create(aPt.Lon, aPt.Lat, AElevation, ADateTime);
|
|
end;
|
|
|
|
procedure TGPSPoint.Assign(AObj: TGPSObj);
|
|
begin
|
|
if (AObj is TGPSPoint) then
|
|
begin
|
|
inherited Assign(AObj);
|
|
FRealPt := TGPSPoint(AObj).RealPoint;
|
|
FElevation := TGPSPoint(AObj).Elevation;
|
|
FDateTime := TGPSPoint(AObj).DateTime;
|
|
end;
|
|
end;
|
|
|
|
function TGPSPoint.GetLat: Double;
|
|
begin
|
|
result := FRealPt.Lat;
|
|
end;
|
|
|
|
function TGPSPoint.GetLon: Double;
|
|
begin
|
|
result := FRealPt.Lon;
|
|
end;
|
|
|
|
procedure TGPSPoint.SetLat(AValue: Double);
|
|
begin
|
|
FRealPt.Lat := AValue;
|
|
end;
|
|
|
|
procedure TGPSPoint.SetLon(AValue: Double);
|
|
begin
|
|
FRealPt.Lon := AValue;
|
|
end;
|
|
|
|
procedure TGPSPoint.GetArea(out Area: TRealArea);
|
|
begin
|
|
Area.TopLeft := FRealPt;
|
|
Area.BottomRight := FRealPt;
|
|
end;
|
|
|
|
procedure TGPSPoint.Draw(AView: TObject; Area: TRealArea);
|
|
begin
|
|
TMapView(AView).DrawPt(Area, Self);
|
|
end;
|
|
|
|
function TGPSPoint.HasElevation: boolean;
|
|
begin
|
|
Result := FElevation <> NO_ELEVATION;
|
|
end;
|
|
|
|
function TGPSPoint.HasDateTime: Boolean;
|
|
begin
|
|
Result := FDateTime <> NO_DATE;
|
|
end;
|
|
|
|
function TGPSPoint.DistanceInKmFrom(OtherPt: TGPSPoint;
|
|
UseElevation: Boolean = true): Double;
|
|
var
|
|
dElev: Double;
|
|
begin
|
|
Result := CalcGeoDistance(Lat, Lon, OtherPt.Lat, OtherPt.Lon, duKilometers, esEllipsoid);
|
|
if UseElevation and HasElevation and OtherPt.HasElevation and (FElevation <> OtherPt.Elevation) then
|
|
begin
|
|
dElev := (FElevation - OtherPt.Elevation) * 0.001; // Elevation is given in meters
|
|
Result := sqrt(sqr(dElev) + sqr(Result));
|
|
end;
|
|
end;
|
|
|
|
(*
|
|
function TGPSPoint.DistanceInKmFrom(OtherPt: TGPSPoint;
|
|
UseElevation: boolean = true): double;
|
|
var
|
|
a: double;
|
|
lat1, lat2, lon1, lon2, t1, t2, t3, t4, t5, rad_dist: double;
|
|
DiffEle: Double;
|
|
begin
|
|
a := PI / 180;
|
|
lat1 := lat * a;
|
|
lat2 := OtherPt.lat * a;
|
|
lon1 := lon * a;
|
|
lon2 := OtherPt.lon * a;
|
|
|
|
t1 := sin(lat1) * sin(lat2);
|
|
t2 := cos(lat1) * cos(lat2);
|
|
t3 := cos(lon1 - lon2);
|
|
t4 := t2 * t3;
|
|
t5 := t1 + t4;
|
|
if t5 >= 1.0 then
|
|
rad_dist := 0.0
|
|
else
|
|
if t5 <= 1.0 then
|
|
rad_dist := pi
|
|
else
|
|
rad_dist := arctan(-t5/sqrt(-t5 * t5 + 1)) + 2 * arctan(1);
|
|
result := (rad_dist * 3437.74677 * 1.1508) * 1.6093470878864446;
|
|
if UseElevation and (FElevation <> OtherPt.FElevation) then
|
|
if (HasElevation) and (OtherPt.HasElevation) then
|
|
begin
|
|
//FElevation is assumed in meters
|
|
DiffEle := (FElevation - OtherPt.Elevation) / 1000;
|
|
Result := sqrt(DiffEle*DiffEle + result*result);
|
|
end;
|
|
end;
|
|
*)
|
|
|
|
procedure TGPSPoint.MoveTo(ALon, ALat: Double; AElevation: double = NO_ELEVATION;
|
|
ADateTime: TDateTime = NO_DATE);
|
|
begin
|
|
FRealPt.Lon := ALon;
|
|
FRealPt.Lat := ALat;
|
|
FElevation := AElevation;
|
|
FDateTime := ADateTime;
|
|
end;
|
|
|
|
|
|
{ TGPSPointOfInterest }
|
|
|
|
constructor TGPSPointOfInterest.Create(ALon, ALat: Double;
|
|
AElevation: Double = NO_ELEVATION; ADateTime: TDateTime = NO_DATE);
|
|
begin
|
|
inherited;
|
|
FImageAnchorX := 50; // These are percentages!
|
|
FImageAnchorY := 100;
|
|
FImageIndex := -1;
|
|
FTextPositionHor := tphCenter;
|
|
FTextPositionVert := tpvBelow;
|
|
end;
|
|
|
|
procedure TGPSPointOfInterest.Draw(AView: TObject; Area: TRealArea);
|
|
begin
|
|
if Assigned(FOnDrawObj)
|
|
then FOnDrawObj(AView, Self, Area)
|
|
else TMapView(AView).DrawPointOfInterest(Area, Self);
|
|
end;
|
|
|
|
|
|
end.
|
|
|