lazarus-ccr/components/lazmapviewer/source/mvmapviewer.pas

3077 lines
79 KiB
ObjectPascal

{ Initial map viewer library:
Copyright (C) 2011 Maciej Kaczkowski / keit.co
Extensions:
(C) 2014 ti_dic@hotmail.com
(C) 2019 Werner Pamler (user wp at Lazarus forum https://forum.lazarus.freepascal.org)
(C) 2023 Yuliyan Ivanov (user alpine at Lazarus forum https://forum.lazarus.freepascal.org)
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
}
// ToDo: Make Active work at designtime.
// "Deprecated" warnings:
// - function names containing "LonLat" were copied and named to contain "LatLon"
// (will be removed in v1.0)
unit mvMapViewer;
{$MODE objfpc}{$H+}
{$WARN 6058 off : Call to subroutine "$1" marked as inline is not inlined}
interface
uses
Classes, SysUtils, Controls, GraphType, Graphics, FPImage, IntfGraphics,
Forms, ImgList, LCLVersion, fgl,
MvTypes, MvGPSObj, MvEngine, MvMapProvider, MvDownloadEngine, MvDrawingEngine,
mvCache, mvExtraData;
Type
TDrawGpsPointEvent = procedure (Sender: TObject;
ADrawer: TMvCustomDrawingEngine; APoint: TGpsPoint) of object;
TMapViewOption =
(
mvoMouseDragging, // Allow dragging of the map with the mouse
mvoMouseZooming, // Allow zooming into the map with the mouse
mvoLatLonInDMS // Use Degrees, Minutes and Seconds for Lat/Lon
);
TMapViewOptions = set of TMapViewOption;
TCacheLocation = (clProfile, clTemp, clCustom);
const
DefaultMapViewOptions = [mvoMouseDragging, mvoMouseZooming];
type
TMapItem = class;
TMapView = class;
TMapPoint = class;
TMapLayer = class;
TMapLayers = class;
TPointOfInterest = class;
TPointsOfInterest = class;
TMapTrack = class;
TMapTracks = class;
TGPSTileLayer = class;
TGPSComboLayer = class;
TPointOfInterestDrawEvent = procedure(Sender: TObject;
ADrawer: TMvCustomDrawingEngine; APoint: TPointOfInterest) of object;
TMapTrackDrawEvent = procedure(Sender: TObject;
ADrawer: TMvCustomDrawingEngine; ATrack: TMapTrack) of object;
{ TMapObjectList }
TMapObjectList = class(specialize TFPGObjectList<TObject>)
public
constructor Create(ASingleObj: TObject = Nil); reintroduce;
constructor Create(AList: TMapObjectList);
class function AddListToResult(AList, AResult: TMapObjectList): TMapObjectList;
end;
{ TMapItem }
TMapItem = class(TCollectionItem)
private
FCaption: TCaption;
FTag: PtrInt;
FVisible: Boolean;
function GetGPSObj: TGPSObj; virtual; abstract;
function GetView: TMapView; virtual;
function GetLayer: TMapLayer; virtual;
procedure SetCaption(AValue: TCaption);
procedure SetVisible(AValue: Boolean);
protected
function GetDisplayName: string; override;
procedure SetIndex(Value: Integer); override;
procedure ItemChanged; virtual; abstract;
public
function HitTest(constref Area: TRealArea): TMapObjectList; virtual; abstract;
property View: TMapView read GetView;
property Layer: TMapLayer read GetLayer;
PROPERTY GPSObj: TGPSObj read GetGPSObj;
published
property Caption: TCaption read FCaption write SetCaption;
property Visible: Boolean read FVisible write SetVisible default True;
property Tag: PtrInt read FTag write FTag default 0;
end;
{ TMapCollectionBase }
TMapCollectionBase = class(TOwnedCollection)
private
FBaseZ: Integer;
protected
function GetView: TMapView; virtual;
function GetLayer: TMapLayer; virtual;
procedure FixOrder(APrevIndex, AIndex: Integer); virtual;
procedure Update(Item: TCollectionItem); override;
public
property View: TMapView read GetView;
property Layer: TMapLayer read GetLayer;
property BaseZ: Integer read FBaseZ write FBaseZ;
end;
{ TMapCollection }
generic TMapCollection<T: TMapItem; OT: class> = class(TMapCollectionBase)
private
type
TItemClass = T;
TOwnerClass = OT;
private
FMCOwner: TOwnerClass;
function GetFirst: TItemClass;
function GetItems(Index: Integer): TItemClass;
function GetLast: TItemClass;
procedure SetItems(Index: Integer; AValue: TItemClass);
public
constructor Create(AOwner: OT; ABaseZ: Integer);
function HitTest(constref Area: TRealArea): TMapObjectList; virtual;
property MCOwner: TOwnerClass read FMCOwner;
property First: TItemClass read GetFirst;
property Last: TItemClass read GetLast;
property Items[Index: Integer]: TItemClass read GetItems write SetItems; default;
end;
{ TMapLayer }
TMapLayer = class(TMapItem)
strict private
FComboLayer: TGPSComboLayer;
FDrawMode: TItemDrawMode;
FUseThreads: Boolean;
FMapProvider: String;
FOpacity: Single;
FPointsOfInterest: TPointsOfInterest;
FTracks: TMapTracks;
private
function GetGPSObj: TGPSObj; override;
function GetView: TMapView; override;
function GetLayer: TMapLayer; override;
function GetMapProvider: String;
function GetPointsOfInterest: TPointsOfInterest;
function GetTracks: TMapTracks;
function GetUseThreads: Boolean;
procedure SetDrawMode(AValue: TItemDrawMode);
procedure SetMapProvider(AValue: String);
procedure SetOpacity(AValue: Single);
procedure SetPointsOfInterest(AValue: TPointsOfInterest);
procedure SetTracks(AValue: TMapTracks);
procedure SetUseThreads(AValue: Boolean);
protected
procedure ItemChanged; override;
public
constructor Create(ACollection: TCollection); override;
destructor Destroy; override;
function HitTest(constref Area: TRealArea): TMapObjectList; override;
procedure AssignFromGPSList(AList: TGPSObjectList);
property ComboLayer: TGPSComboLayer read FComboLayer;
published
property MapProvider: String read GetMapProvider write SetMapProvider;
property UseThreads: Boolean read GetUseThreads write SetUseThreads default True;
property DrawMode: TItemDrawMode read FDrawMode write SetDrawMode default idmUseOpacity;
property Opacity: Single read FOpacity write SetOpacity default 0.25;
property PointsOfInterest: TPointsOfInterest read GetPointsOfInterest write SetPointsOfInterest;
property Tracks: TMapTracks read GetTracks write SetTracks;
end;
{ TMapLayers }
TMapLayers = class(specialize TMapCollection<TMapLayer, TMapView>)
protected
function GetView: TMapView; override;
function GetLayer: TMapLayer; override;
procedure FixOrder(APrevIndex, AIndex: Integer); override;
end;
{ TMapCenter }
TMapCenter = class(TPersistent)
private
FLatitude: Double;
FLongitude: Double;
FView: TMapView;
function GetLatLonInDMS: Boolean;
procedure SetLatitude(AValue: Double);
procedure SetLongitude(AValue: Double);
procedure SetViewCenter;
protected
function GetOwner: TPersistent; override;
public
constructor Create(AView: TMapView);
property LatLonInDMS: Boolean read GetLatLonInDMS;
published
property Longitude: Double read FLongitude write SetLongitude;
property Latitude: Double read FLatitude write SetLatitude;
end;
{ TMapPoint }
TMapPoint = class(TMapItem)
private
FDateTime: TDateTime;
FElevation: Double;
FLatitude: Double;
FLongitude: Double;
FPoint: TGPSPoint;
function GetLatLonInDMS: Boolean;
function GetToScreen: TPoint;
function IsDateTimeStored: Boolean;
function IsElevationStored: Boolean;
procedure SetDateTime(AValue: TDateTime);
procedure SetElevation(AValue: Double);
procedure SetLatitude(AValue: Double);
procedure SetLongitude(AValue: Double);
function GetGPSObj: TGPSObj; override;
protected
procedure ItemChanged; override;
function CreatePoint: TGPSPoint; virtual;
procedure DestroyPoint; virtual;
public
constructor Create(ACollection: TCollection); override;
destructor Destroy; override;
procedure AssignTo(Dest: TPersistent); override;
function HitTest(constref Area: TRealArea): TMapObjectList; override;
property LatLonInDMS: Boolean read GetLatLonInDMS;
property ToScreen: TPoint read GetToScreen;
published
property Longitude: Double read FLongitude write SetLongitude;
property Latitude: Double read FLatitude write SetLatitude;
property Elevation: Double read FElevation write SetElevation stored IsElevationStored;
property DateTime: TDateTime read FDateTime write SetDateTime stored IsDateTimeStored;
end;
{ TMapTrackPoint }
TMapTrackPoint = class(TMapPoint)
private
FMark: TSegmentExtraData.TSegmentMark;
function MarkIsStored: Boolean;
procedure SetMark(AValue: TSegmentExtraData.TSegmentMark);
protected
function GPSTrack: TGPSTrack;
function CreatePoint: TGPSPoint; override;
procedure DestroyPoint; override;
public
procedure AssignTo(Dest: TPersistent); override;
published
property Mark: TSegmentExtraData.TSegmentMark read FMark write SetMark stored MarkIsStored;
end;
{ TMapTrackPoints }
TMapTrackPoints = class(specialize TMapCollection<TMapTrackPoint, TMapTrack>)
protected
function GetLayer: TMapLayer; override;
procedure FixOrder(APrevIndex, AIndex: Integer); override;
end;
{ TPointOfInterest }
TPointOfInterest = class(TMapPoint)
private
FImageIndex: TImageIndex;
FOnDrawPoint: TPointOfInterestDrawEvent;
procedure SetImageIndex(AValue: TImageIndex);
procedure SetOnDrawPoint(AValue: TPointOfInterestDrawEvent);
protected
procedure DrawPoint(Sender: TObject; AGPSObj: TGPSObj; AArea: TRealArea);
procedure ItemChanged; override;
function CreatePoint: TGPSPoint; override;
procedure DestroyPoint; override;
public
constructor Create(ACollection: TCollection); override;
procedure AssignTo(Dest: TPersistent); override;
published
property ImageIndex: TImageIndex read FImageIndex write SetImageIndex default -1;
property OnDrawPoint: TPointOfInterestDrawEvent read FOnDrawPoint write SetOnDrawPoint;
end;
{ TPointsOfInterest }
TPointsOfInterest = class(specialize TMapCollection<TPointOfInterest, TMapLayer>)
protected
function GetLayer: TMapLayer; override;
end;
{ TMapPolygon }
TMapPolygon = class(TMapItem)
private
FPoints: TGPSPointList;
published
property Points: TGPSPointList read FPoints;
end;
{ TMapTrack }
TMapTrack = class(TMapItem)
private
FConnectColor: TColor;
FConnectWidth: Double;
FLineColor: TColor;
FLineWidth: Double;
FPoints: TMapTrackPoints;
FTrack: TGPSTrack;
FOnDrawTrack: TMapTrackDrawEvent;
function GetGPSObj: TGPSObj; override;
function GetPoints: TMapTrackPoints;
procedure SetConnectColor(AValue: TColor);
procedure SetConnectWidth(AValue: Double);
procedure SetLineColor(AValue: TColor);
procedure SetLineWidth(AValue: Double);
procedure SetOnDrawTrack(AValue: TMapTrackDrawEvent);
procedure SetPoints(AValue: TMapTrackPoints);
protected
procedure DrawTrack(Sender: TObject; AGPSObj: TGPSObj; AArea: TRealArea);
public
constructor Create(ACollection: TCollection); override;
destructor Destroy; override;
procedure ItemChanged; override;
function HitTest(constref Area: TRealArea): TMapObjectList; override;
published
//property DateTime: TDateTime read GetDateTime write FDateTime;
property LineColor: TColor read FLineColor write SetLineColor default clDefault;
property LineWidth: Double read FLineWidth write SetLineWidth;
property ConnectColor: TColor read FConnectColor write SetConnectColor default clNone;
property ConnectWidth: Double read FConnectWidth write SetConnectWidth;
property Points: TMapTrackPoints read GetPoints write SetPoints;
property OnDrawTrack: TMapTrackDrawEvent read FOnDrawTrack write SetOnDrawTrack;
end;
{ TMapTracks }
TMapTracks = class(specialize TMapCollection<TMapTrack, TMapLayer>)
protected
function GetLayer: TMapLayer; override;
end;
{ TMapView }
TMapView = class(TCustomControl)
private
FCacheLocation: TCacheLocation;
FCachePath, FCacheFullPath: String;
FCenter: TMapCenter;
FDownloadEngine: TMvCustomDownloadEngine;
FBuiltinDownloadEngine: TMvCustomDownloadEngine;
FEngine: TMapViewerEngine;
FBuiltinDrawingEngine: TMvCustomDrawingEngine;
FDrawingEngine: TMvCustomDrawingEngine;
FDrawPreviewTiles: boolean;
FActive: boolean;
FLayers: TMapLayers;
FGPSItems: array [0..9] of TGPSObjectList;
FOptions: TMapViewOptions;
FPOIImage: TBitmap;
FPOITextBgColor: TColor;
FOnDrawGpsPoint: TDrawGpsPointEvent;
FDebugTiles: Boolean;
FDefaultTrackColor: TColor;
FDefaultTrackWidth: Integer;
FFont: TFont;
FPOIImages: TCustomImageList;
FPOIImagesWidth: Integer;
FCacheOnDisk: Boolean;
FZoomMax: Integer;
FZoomMin: Integer;
procedure CallAsyncInvalidate;
procedure DoAsyncInvalidate({%H-}Data: PtrInt);
procedure DrawObjects(const {%H-}TileId: TTileId; aLeft, aTop, aRight,aBottom: integer);
procedure DrawGpsObj(const {%H-}Area: TRealArea; AObj: TGPSObj);
function GetCacheMaxAge: Integer;
function GetCacheOnDisk: boolean;
function GetCenter: TRealPoint;
function GetCyclic: Boolean;
function GetDownloadEngine: TMvCustomDownloadEngine;
function GetDrawingEngine: TMvCustomDrawingEngine;
function GetDrawPreviewTiles: Boolean;
function GetGPSItems: TGPSObjectList;
function GetGPSLayer(Layer: Integer): TGPSObjectList;
function GetInactiveColor: TColor;
function GetLayers: TMapLayers;
function GetMapProvider: String;
function GetOnCenterMove: TNotifyEvent;
function GetOnChange: TNotifyEvent;
function GetOnZoomChange: TNotifyEvent;
function GetUseThreads: boolean;
function GetZoom: integer;
function GetZoomToCursor: Boolean;
function IsCacheMaxAgeStored: Boolean;
function IsCachePathStored: Boolean;
function IsFontStored: Boolean;
function IsLayersStored: Boolean;
procedure SetActive(AValue: boolean);
procedure SetCacheLocation(AValue: TCacheLocation);
procedure SetCacheMaxAge(AValue: Integer);
procedure SetCacheOnDisk(AValue: boolean);
procedure SetCachePath(AValue: String);
procedure SetCenter(AValue: TRealPoint);
procedure SetCyclic(AValue: Boolean);
procedure SetDebugTiles(AValue: Boolean);
procedure SetDefaultTrackColor(AValue: TColor);
procedure SetDefaultTrackWidth(AValue: Integer);
procedure SetDownloadEngine(AValue: TMvCustomDownloadEngine);
procedure SetDrawingEngine(AValue: TMvCustomDrawingEngine);
procedure SetDrawPreviewTiles(AValue: Boolean);
procedure SetFont(AValue: TFont);
procedure SetInactiveColor(AValue: TColor);
procedure SetLayers(const ALayers: TMapLayers);
procedure SetMapProvider(AValue: String);
procedure SetOnCenterMove(AValue: TNotifyEvent);
procedure SetOnChange(AValue: TNotifyEvent);
procedure SetOnZoomChange(AValue: TNotifyEvent);
procedure SetOptions(AValue: TMapViewOptions);
procedure SetPOIImage(const AValue: TBitmap);
procedure SetPOIImages(const AValue: TCustomImageList);
procedure SetPOIImagesWidth(AValue: Integer);
procedure SetPOITextBgColor(AValue: TColor);
procedure SetUseThreads(AValue: boolean);
procedure SetZoom(AValue: integer);
procedure SetZoomMax(AValue: Integer);
procedure SetZoomMin(AValue: Integer);
procedure SetZoomToCursor(AValue: Boolean);
procedure UpdateFont(Sender: TObject);
procedure UpdateImage(Sender: TObject);
protected
AsyncInvalidate : boolean;
procedure ActivateEngine;
procedure DblClick; override;
procedure DoDrawStretchedTile(const TileId: TTileID; X, Y: Integer; TileImg: TPictureCacheItem; const R: TRect);
procedure DoDrawTile(const TileId: TTileId; X,Y: integer; TileImg: TPictureCacheItem);
procedure DoDrawTileInfo(const {%H-}TileID: TTileID; X,Y: Integer);
procedure DoEraseBackground(const R: TRect);
procedure DoTileDownloaded(const TileId: TTileId);
function DoMouseWheel(Shift: TShiftState; WheelDelta: Integer;
MousePos: TPoint): Boolean; override;
procedure DoOnResize; override;
function IsActive: Boolean;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
procedure MouseMove(Shift: TShiftState; X,Y: Integer); override;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure Paint; override;
procedure OnGPSItemsModified(Sender: TObject; objs: TGPSObjList;
Adding: boolean);
function CreateLayers: TMapLayers; virtual;
procedure UpdateLayers;
procedure ChangeCachePath(AOldLoc: TCacheLocation; ANewPath: String);
class function CacheDirectory(ALoc: TCacheLocation; ACustomPath: String): String;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function CyclicPointOf(APoint: TPoint; ARefX: LongInt; Eastwards: Boolean = True): TPoint;
function CyclicPointsOf(APoint: TPoint): TPointArray;
function TrackLineColor(AColor: TColor; ExtraData: TObject): TColor;
function TrackLineWidth(AWidth: Double; ExtraData: TObject): Integer;
procedure DrawPointOfInterest(const {%H-}Area: TRealArea; APt: TGPSPointOfInterest);
procedure DrawPt(const {%H-}Area: TRealArea; APt: TGPSPoint);
procedure DrawTrack(const {%H-}Area: TRealArea; trk: TGPSTrack);
procedure DrawArea(const {%H-}Area: TRealArea; ar: TGPSArea);
procedure ClearBuffer;
procedure GetMapProviders(lstProviders: TStrings);
function GetVisibleArea: TRealArea;
function LatLonToScreen(aPt: TRealPoint): TPoint;
function LatLonToScreen(Lat, Lon: Double): TPoint; overload;
function LonLatToScreen(aPt: TRealPoint): TPoint; deprecated 'Use LatLonToScreen';
function ObjsAtScreenPt(X, Y: Integer; ATolerance: Integer = -1): TGPSObjarray;
procedure SaveToFile(AClass: TRasterImageClass; const AFileName: String);
function SaveToImage(AClass: TRasterImageClass): TRasterImage;
procedure SaveToStream(AClass: TRasterImageClass; AStream: TStream);
function ScreenToLatLon(aPt: TPoint): TRealPoint;
function ScreenToLonLat(aPt: TPoint): TRealPoint; deprecated 'Use ScreenToLatLon';
procedure CenterOnObj(obj: TGPSObj);
procedure Redraw; inline;
procedure ZoomOnArea(const aArea: TRealArea);
procedure ZoomOnObj(obj: TGPSObj);
procedure WaitEndOfRendering;
procedure StartDragging(X, Y: Integer);
procedure EndDragging(X, Y: Integer);
procedure AbortDragging;
property Center: TRealPoint read GetCenter write SetCenter;
property Engine: TMapViewerEngine read FEngine;
property GPSItems: TGPSObjectList read GetGPSItems;
property GPSLayer[L: Integer]: TGPSObjectList read GetGPSLayer;
published
property Active: boolean read FActive write SetActive default false;
property Align;
property CacheOnDisk: boolean read GetCacheOnDisk write SetCacheOnDisk default true;
property CacheLocation: TCacheLocation read FCacheLocation write SetCacheLocation default clProfile;
property CachePath: String read FCachePath write SetCachePath stored IsCachePathStored;
property CacheFullPath: String read FCacheFullPath stored False;
property CacheMaxAge: Integer read GetCacheMaxAge write SetCacheMaxAge stored IsCacheMaxAgeStored;
property Cyclic: Boolean read GetCyclic write SetCyclic default false;
property DebugTiles: Boolean read FDebugTiles write SetDebugTiles default false;
property DefaultTrackColor: TColor read FDefaultTrackColor write SetDefaultTrackColor default clRed;
property DefaultTrackWidth: Integer read FDefaultTrackWidth write SetDefaultTrackWidth default 1;
property DownloadEngine: TMvCustomDownloadEngine read GetDownloadEngine write SetDownloadEngine;
property DrawingEngine: TMvCustomDrawingEngine read GetDrawingEngine write SetDrawingEngine;
property DrawPreviewTiles: Boolean read GetDrawPreviewTiles write SetDrawPreviewTiles default true;
property Options: TMapViewOptions read FOptions write SetOptions default DefaultMapViewOptions;
property Layers: TMapLayers read GetLayers write SetLayers stored IsLayersStored;
property Font: TFont read FFont write SetFont stored IsFontStored;
property Height default 150;
property InactiveColor: TColor read GetInactiveColor write SetInactiveColor default clWhite;
property MapProvider: String read GetMapProvider write SetMapProvider;
property MapCenter: TMapCenter read FCenter write FCenter;
property POIImage: TBitmap read FPOIImage write SetPOIImage;
property POIImages: TCustomImageList read FPOIImages write SetPOIImages;
property POIImagesWidth: Integer read FPOIImagesWidth write SetPOIImagesWidth default 0;
property POITextBgColor: TColor read FPOITextBgColor write SetPOITextBgColor default clNone;
property PopupMenu;
property UseThreads: boolean read GetUseThreads write SetUseThreads default false;
property Width default 150;
property Zoom: integer read GetZoom write SetZoom default 1;
property ZoomMax: Integer read FZoomMax write SetZoomMax default 19;
property ZoomMin: Integer read FZoomMin write SetZoomMin default 1;
property ZoomToCursor: Boolean read GetZoomToCursor write SetZoomToCursor default True;
property OnCenterMove: TNotifyEvent read GetOnCenterMove write SetOnCenterMove;
property OnZoomChange: TNotifyEvent read GetOnZoomChange write SetOnZoomChange;
property OnChange: TNotifyEvent read GetOnChange write SetOnChange;
property OnDrawGpsPoint: TDrawGpsPointEvent read FOnDrawGpsPoint write FOnDrawGpsPoint;
property OnMouseDown;
property OnMouseEnter;
property OnMouseLeave;
property OnMouseMove;
property OnMouseUp;
end;
{ TGPSTileLayerBase }
TGPSTileLayerBase = class(TGPSObj)
private
FDrawMode: TItemDrawMode;
FMapProvider: String;
FOpacity: Single;
FParentView: TMapView;
FEngine: TMapViewerEngine;
FParentViewChanged: Boolean;
function GetMapProvider: String;
function GetUseThreads: Boolean;
procedure DoTileDownloaded(const TileId: TTileId);
procedure DoDrawTile(const TileId: TTileId; X, Y: Integer; TileImg: TPictureCacheItem);
procedure SetDrawMode(AValue: TItemDrawMode);
procedure SetMapProvider(AValue: String);
procedure SetOpacity(AValue: Single);
procedure SetUseThreads(AValue: Boolean);
procedure SetParentView(AValue: TMapView);
protected
procedure TileDownloaded(const TileId: TTileId); virtual;
procedure DrawTile(const TileId: TTileId; X, Y: Integer; TileImg: TPictureCacheItem); virtual; abstract;
public
constructor Create;
destructor Destroy; override;
procedure GetArea(out Area: TRealArea); override;
procedure Draw(AView: TObject; Area: TRealArea); override;
procedure ParentViewChanged; virtual;
property MapProvider: String read GetMapProvider write SetMapProvider;
property UseThreads: Boolean read GetUseThreads write SetUseThreads;
property DrawMode: TItemDrawMode read FDrawMode write SetDrawMode;
property Opacity: Single read FOpacity write SetOpacity;
end;
{ TGPSTileLayer }
TGPSTileLayer = class(TGPSTileLayerBase)
protected
procedure DrawTile(const TileId: TTileId; X, Y: Integer;
TileImg: TPictureCacheItem); override;
public
procedure Draw(AView: TObject; Area: TRealArea); override;
procedure TileDownloaded(const TileId: TTileId); override;
end;
{ TGPSTileLayerLabels }
TGPSTileLayerLabels = class(TGPSTileLayerBase)
protected
procedure DrawTile(const TileId: TTileId; X, Y: Integer;
TileImg: TPictureCacheItem); override;
public
procedure Draw(AView: TObject; Area: TRealArea); override;
end;
{ TGPSComboLayer }
TGPSComboLayer = class(TGPSObjectList)
FTileLayer: TGPSTileLayer;
public
constructor Create;
destructor Destroy; override;
procedure GetArea(out Area: TRealArea); override;
procedure Draw(AView: TObject; Area: TRealArea); override;
property TileLayer: TGPSTileLayer read FTileLayer;
end;
implementation
uses
GraphMath, FileUtil, LazLoggerBase, Types, Math,
mvJobQueue, mvDLEFpc,
{$IFDEF MSWINDOWS}
mvDLEWin,
{$ENDIF}
mvDE_IntfGraphics;
const
RANGE_Z = (High(Integer) div 4);
BASE_Z_LAYER = Low(Integer);
BASE_Z_AREA = Low(Integer);
BASE_Z_TRACK = BASE_Z_AREA + RANGE_Z;
BASE_Z_POI = BASE_Z_TRACK + RANGE_Z;
_TILELAYERS_ID_ = -42; // OwnerIDs of the tile layers
{ Converts a length given in millimeters to screen pixels }
function mmToPx(AValue: Double): Integer;
begin
Result := round(AValue / 25.4 * ScreenInfo.PixelsPerInchX);
end;
type
{ TDrawObjJob }
TDrawObjJob = class(TJob)
private
AllRun: boolean;
Viewer: TMapView;
FRunning: boolean;
FLst: TGPSObjList;
FStates: Array of integer;
FArea: TRealArea;
protected
function pGetTask: integer; override;
procedure pTaskStarted(aTask: integer); override;
procedure pTaskEnded(aTask: integer; aExcept: Exception); override;
public
procedure ExecuteTask(aTask: integer; FromWaiting: boolean); override;
function Running: boolean;override;
public
constructor Create(aViewer: TMapView; aLst: TGPSObjList; const aArea: TRealArea);
destructor Destroy; override;
end;
{ TMapObjectList }
constructor TMapObjectList.Create(ASingleObj: TObject);
begin
inherited Create(False);
if Assigned(ASingleObj) then
Add(ASingleObj);
end;
constructor TMapObjectList.Create(AList: TMapObjectList);
begin
inherited Create(False);
if Assigned(AList) then
AddList(AList);
end;
class function TMapObjectList.AddListToResult(AList, AResult: TMapObjectList): TMapObjectList;
begin
Result := AResult;
if Assigned(AList) then
if Assigned(AResult) then
begin
Result.AddList(AList);
AList.Free;
end
else
Result := AList;
end;
{ TMapTrackPoint }
function TMapTrackPoint.CreatePoint: TGPSPoint;
var
Trk: TGPSTrack;
begin
Result := inherited CreatePoint;
Trk := GPSTrack;
if Assigned(Trk)
then Trk.Points.Add(Result);
end;
procedure TMapTrackPoint.DestroyPoint;
var
Trk: TGPSTrack;
begin
Trk := GPSTrack;
if Assigned(Trk) and Assigned(FPoint)
then Trk.Points.Remove(FPoint);
end;
procedure TMapTrackPoint.AssignTo(Dest: TPersistent);
begin
inherited AssignTo(Dest);
if Dest is TMapTrackPoint then
TMapTrackPoint(Dest).Mark := Self.Mark;
end;
function TMapTrackPoint.MarkIsStored: Boolean;
begin
Result := FMark <> smNone;
end;
procedure TMapTrackPoint.SetMark(AValue: TSegmentExtraData.TSegmentMark);
begin
if FMark = AValue then
Exit;
FMark := AValue;
if (AValue = smNone) then
FPoint.ExtraData.Free
else
begin
if not Assigned(FPoint.ExtraData)
then FPoint.ExtraData := TSegmentExtraData.Create(AValue)
else TSegmentExtraData(FPoint.ExtraData).Mark := AValue;
end;
end;
function TMapTrackPoint.GPSTrack: TGPSTrack;
begin
Result := Nil;
if Assigned(Collection) and (Collection is TMapTrackPoints) then
with (Collection as TMapTrackPoints) do
if Assigned(MCOwner) and (MCOwner is TMapTrack) then
Result := (MCOwner as TMapTrack).FTrack;
end;
{ TMapCollectionBase }
function TMapCollectionBase.GetView: TMapView;
begin
if Assigned(Layer)
then Result := Layer.View
else Result := Nil;
end;
function TMapCollectionBase.GetLayer: TMapLayer;
begin
Result := Nil;
end;
procedure TMapCollectionBase.FixOrder(APrevIndex, AIndex: Integer);
var
I, T, B: Integer;
begin
T := Min(APrevIndex, AIndex);
B := Max(APrevIndex, AIndex);
if APrevIndex < 0 then
begin
T := AIndex;
B := Pred(Count);
end;
for I := T to B do
Layer.ComboLayer.ChangeZOrder(TMapItem(Items[I]).GPSObj, I + FBaseZ);
end;
procedure TMapCollectionBase.Update(Item: TCollectionItem);
begin
inherited Update(Item);
if Assigned(View) then
View.Invalidate;
end;
{ TMapTrackPoints }
function TMapTrackPoints.GetLayer: TMapLayer;
begin
Result := MCOwner.Layer;
end;
procedure TMapTrackPoints.FixOrder(APrevIndex, AIndex: Integer);
var
I, T, B: Integer;
Trk: TGPSTrack;
O: TGPSPoint;
begin
if Assigned(MCOwner) and (MCOwner is TMapTrack)
then Trk := (MCOwner as TMapTrack).FTrack
else Exit;
T := Min(APrevIndex, AIndex);
B := Max(APrevIndex, AIndex);
if APrevIndex < 0 then
begin
T := AIndex;
B := Pred(Count);
end;
for I := T to B do
begin
O := TGPSPoint(TMapItem(Items[I]).GPSObj);
if Trk.Points.Extract(O) <> Nil then
Trk.Points.Insert(I, O);
end;
end;
{ TMapTrack }
procedure TMapTrack.SetOnDrawTrack(AValue: TMapTrackDrawEvent);
begin
if CompareMem(@FOnDrawTrack, @AValue, SizeOf(TMethod)) then
Exit;
FOnDrawTrack := AValue;
if Assigned(FOnDrawTrack)
then FTrack.OnDrawObj := @DrawTrack
else FTrack.OnDrawObj := Nil;
ItemChanged;
end;
function TMapTrack.GetGPSObj: TGPSObj;
begin
Result := FTrack;
end;
function TMapTrack.GetPoints: TMapTrackPoints;
begin
Result := FPoints;
end;
procedure TMapTrack.SetConnectColor(AValue: TColor);
begin
if FConnectColor=AValue then Exit;
FConnectColor:=AValue;
ItemChanged;
end;
procedure TMapTrack.SetConnectWidth(AValue: Double);
begin
if FConnectWidth=AValue then Exit;
FConnectWidth:=AValue;
ItemChanged;
end;
procedure TMapTrack.SetLineColor(AValue: TColor);
begin
if FLineColor = AValue then Exit;
FLineColor := AValue;
ItemChanged;
end;
procedure TMapTrack.SetLineWidth(AValue: Double);
begin
if FLineWidth = AValue then Exit;
FLineWidth := AValue;
ItemChanged;
end;
procedure TMapTrack.SetPoints(AValue: TMapTrackPoints);
begin
FPoints.Assign(AValue);
end;
procedure TMapTrack.DrawTrack(Sender: TObject; AGPSObj: TGPSObj;
AArea: TRealArea);
begin
if Assigned(FOnDrawTrack) then
FOnDrawTrack(Sender, (Collection as TMapTracks).GetView.DrawingEngine, Self);
end;
constructor TMapTrack.Create(ACollection: TCollection);
begin
inherited Create(ACollection);
FLineColor := clDefault;
FLineWidth := -1;
FConnectColor := clNone;
FConnectWidth := -1;
FVisible := True;
FPoints := TMapTrackPoints.Create(Self, 0);
FTrack := TGPSTrack.Create;
Layer.ComboLayer.Add(FTrack, Pred(_TILELAYERS_ID_), Self.Index + BASE_Z_TRACK);
end;
destructor TMapTrack.Destroy;
begin
FPoints.Free;
if Assigned(FTrack) then
Layer.ComboLayer.Delete(FTrack);
inherited Destroy;
end;
procedure TMapTrack.ItemChanged;
begin
FTrack.Name := Caption;
FTrack.LineColor := LineColor;
FTrack.LineWidth := LineWidth;
FTrack.ConnectColor := ConnectColor;
FTrack.ConnectWidth := ConnectWidth;
FTrack.Visible := Visible;
Changed(False);
end;
function TMapTrack.HitTest(constref Area: TRealArea): TMapObjectList;
begin
Result := Points.HitTest(Area);
if Assigned(Result) then
Result.Add(Self);
end;
{ TMapTracks }
function TMapTracks.GetLayer: TMapLayer;
begin
Result := MCOwner;
end;
{ TMapItem }
function TMapItem.GetView: TMapView;
begin
Result := Layer.View;
end;
function TMapItem.GetLayer: TMapLayer;
begin
if Assigned(Collection)
then Result := (Collection as TMapCollectionBase).Layer
else Result := Nil;
end;
procedure TMapItem.SetCaption(AValue: TCaption);
begin
if FCaption=AValue then Exit;
FCaption:=AValue;
ItemChanged;
end;
procedure TMapItem.SetVisible(AValue: Boolean);
begin
if FVisible=AValue then Exit;
FVisible:=AValue;
ItemChanged;
end;
function TMapItem.GetDisplayName: string;
begin
if FCaption <> ''
then Result := FCaption
else Result := ClassName;
if not FVisible
then Result := Result + ' (Invisible)';
end;
procedure TMapItem.SetIndex(Value: Integer);
var
PrevIndex: Integer;
begin
PrevIndex := Index;
inherited SetIndex(Value);
if (PrevIndex <> Index) and Assigned(Collection) then
TMapCollectionBase(Collection).FixOrder(PrevIndex, Index);
end;
{ TMapCollection }
function TMapCollection.GetItems(Index: Integer): TItemClass;
begin
Result := TItemClass(inherited GetItem(Index));
end;
function TMapCollection.GetFirst: TItemClass;
begin
Result := GetItems(0);
end;
function TMapCollection.GetLast: TItemClass;
begin
Result := GetItems(Pred(Count));
end;
procedure TMapCollection.SetItems(Index: Integer; AValue: TItemClass);
begin
(GetItems(Index) as TPersistent).Assign(AValue);
end;
function TMapCollection.HitTest(constref Area: TRealArea): TMapObjectList;
var
I: TCollectionItem;
begin
Result := Nil;
for I in Self do
Result := TMapObjectList.AddListToResult(TItemClass(I).HitTest(Area), Result);
if Assigned(Result) then
Result.Add(Self);
end;
constructor TMapCollection.Create(AOwner: OT; ABaseZ: Integer);
begin
inherited Create(AOwner, TItemClass);
FMCOwner := AOwner;
FBaseZ := ABaseZ;
end;
{ TGPSComboLayer }
procedure TGPSComboLayer.GetArea(out Area: TRealArea);
begin
FTileLayer.GetArea(Area);
end;
constructor TGPSComboLayer.Create;
begin
inherited Create;
FTileLayer := TGPSTileLayer.Create;
end;
destructor TGPSComboLayer.Destroy;
begin
FTileLayer.Free;
inherited Destroy;
end;
procedure TGPSComboLayer.Draw(AView: TObject; Area: TRealArea);
var
I: Integer;
Objs: TGPSObjList;
begin
inherited Draw(AView, Area);
FTileLayer.Draw(AView, Area);
Objs := GetObjectsInArea(Area);
try
if Objs.Count > 0 then
begin
for I := 0 to Pred(Objs.Count) do
if Objs[I].Visible then
Objs[I].Draw(AView, Area)
end;
finally
FreeAndNil(Objs);
end;
end;
{ TPointsOfInterest }
function TPointsOfInterest.GetLayer: TMapLayer;
begin
Result := MCOwner;
end;
{ TMapPoint }
function TMapPoint.IsDateTimeStored: Boolean;
begin
Result := not (FDateTime = NO_DATE);
end;
function TMapPoint.IsElevationStored: Boolean;
begin
Result := not (FElevation = NO_ELE);
end;
procedure TMapPoint.SetDateTime(AValue: TDateTime);
begin
if FDateTime=AValue then Exit;
FDateTime:=AValue;
ItemChanged;
end;
procedure TMapPoint.SetElevation(AValue: Double);
begin
if FElevation=AValue then Exit;
FElevation:=AValue;
ItemChanged;
end;
function TMapPoint.GetLatLonInDMS: Boolean;
begin
Result := Assigned(View) and (mvoLatLonInDMS in View.Options);
end;
function TMapPoint.GetToScreen: TPoint;
begin
Result := View.LatLonToScreen(RealPoint(Latitude, Longitude));
end;
procedure TMapPoint.SetLatitude(AValue: Double);
begin
if FLatitude = AValue then Exit;
FLatitude := AValue;
ItemChanged;
end;
procedure TMapPoint.SetLongitude(AValue: Double);
begin
if FLongitude = AValue then Exit;
FLongitude := AValue;
ItemChanged;
end;
function TMapPoint.GetGPSObj: TGPSObj;
begin
Result := FPoint;
end;
procedure TMapPoint.ItemChanged;
begin
FPoint.Lon := Longitude;
FPoint.Lat := Latitude;
FPoint.Name := Caption;
FPoint.Visible := Visible;
FPoint.Elevation := Elevation;
FPoint.DateTime := DateTime;
Changed(False);
end;
function TMapPoint.HitTest(constref Area: TRealArea): TMapObjectList;
var
BB: TRealArea;
begin
Result := Nil;
if not Visible then
Exit;
BB := Self.GPSObj.BoundingBox;
if Area.ContainsPoint(BB.TopLeft) and Area.ContainsPoint(BB.BottomRight)
then Result := TMapObjectList.Create(Self);
end;
function TMapPoint.CreatePoint: TGPSPoint;
begin
Result := TGPSPoint.Create(FLongitude, FLatitude);
end;
procedure TMapPoint.DestroyPoint;
begin
end;
constructor TMapPoint.Create(ACollection: TCollection);
begin
inherited Create(ACollection);
FLongitude := View.Center.Lon;
FLatitude := View.Center.Lat;
FVisible := True;
FElevation := NO_ELE;
FDateTime := NO_DATE;
FPoint := CreatePoint;
end;
destructor TMapPoint.Destroy;
begin
DestroyPoint;
inherited Destroy;
end;
procedure TMapPoint.AssignTo(Dest: TPersistent);
begin
if Dest is TMapPoint then
with TMapPoint(Dest) do
begin
Latitude := Self.Latitude;
Longitude := Self.Longitude;
Elevation := Self.Elevation;
DateTime := Self.DateTime;
end
else
inherited AssignTo(Dest);
end;
{ TPointOfInterest }
procedure TPointOfInterest.SetImageIndex(AValue: TImageIndex);
begin
if FImageIndex = AValue then Exit;
FImageIndex := AValue;
ItemChanged;
end;
procedure TPointOfInterest.SetOnDrawPoint(AValue: TPointOfInterestDrawEvent);
begin
if CompareMem(@FOnDrawPoint, @AValue, SizeOf(TMethod)) then
Exit;
FOnDrawPoint := AValue;
if Assigned(FOnDrawPoint)
then FPoint.OnDrawObj := @DrawPoint
else FPoint.OnDrawObj := Nil;
ItemChanged;
end;
procedure TPointOfInterest.DrawPoint(Sender: TObject; AGPSObj: TGPSObj;
AArea: TRealArea);
begin
if Assigned(FOnDrawPoint) then
FOnDrawPoint(Sender, View.DrawingEngine, Self);
end;
procedure TPointOfInterest.ItemChanged;
begin
TGPSPointOfInterest(FPoint).ImageIndex := FImageIndex;
inherited ItemChanged;
end;
function TPointOfInterest.CreatePoint: TGPSPoint;
begin
Result := TGPSPointOfInterest.Create(FLongitude, FLatitude);
Layer.ComboLayer.Add(Result, Pred(_TILELAYERS_ID_), Self.Index + BASE_Z_POI);
end;
procedure TPointOfInterest.DestroyPoint;
begin
if Assigned(FPoint) then
Layer.ComboLayer.Delete(FPoint);
end;
constructor TPointOfInterest.Create(ACollection: TCollection);
begin
inherited Create(ACollection);
FImageIndex := -1;
end;
procedure TPointOfInterest.AssignTo(Dest: TPersistent);
begin
inherited AssignTo(Dest);
if Dest is TPointOfInterest then
TPointOfInterest(Dest).ImageIndex := Self.ImageIndex;
end;
{ TMapCenter }
procedure TMapCenter.SetLongitude(AValue: Double);
begin
if FLongitude = AValue then Exit;
FLongitude := AValue;
SetViewCenter;
end;
procedure TMapCenter.SetLatitude(AValue: Double);
begin
if FLatitude = AValue then Exit;
FLatitude := AValue;
SetViewCenter;
end;
function TMapCenter.GetLatLonInDMS: Boolean;
begin
Result := Assigned(FView) and (mvoLatLonInDMS in FView.Options);
end;
procedure TMapCenter.SetViewCenter;
var
R: TRealPoint;
begin
R.Init(FLongitude, FLatitude);
FView.SetCenter(R);
end;
function TMapCenter.GetOwner: TPersistent;
begin
Result := FView;
end;
constructor TMapCenter.Create(AView: TMapView);
begin
FView := AView;
end;
{ TMapLayer }
//function TMapLayer.GetMapView: TMapView;
//begin
// if Collection is TMapLayers
// then Result := (Collection as TMapLayers).MCOwner //MapView
// else Result := Nil;
//end;
function TMapLayer.GetPointsOfInterest: TPointsOfInterest;
begin
Result := FPointsOfInterest;
end;
function TMapLayer.GetTracks: TMapTracks;
begin
Result := FTracks;
end;
function TMapLayer.GetGPSObj: TGPSObj;
begin
Result := FComboLayer;
end;
function TMapLayer.GetView: TMapView;
begin
if (Collection is TMapLayers)
then Result := (Collection as TMapLayers).View
else Result := Nil;
end;
function TMapLayer.GetLayer: TMapLayer;
begin
Result := Self;
end;
function TMapLayer.GetMapProvider: String;
begin
Result := ComboLayer.TileLayer.MapProvider
end;
function TMapLayer.GetUseThreads: Boolean;
begin
Result := FUseThreads;
end;
procedure TMapLayer.SetDrawMode(AValue: TItemDrawMode);
begin
if FDrawMode=AValue then Exit;
FDrawMode:=AValue;
ItemChanged;
end;
procedure TMapLayer.SetMapProvider(AValue: String);
var
P: TMapProvider;
LPS, MPS: String;
begin
if FMapProvider = AValue then
Exit;
// Check compat. of provider projection type against the base provider.
if Assigned(View) then
begin
P := View.Engine.MapProviderByName(AValue);
if Assigned(P) and (View.Engine.MapProjectionType <> P.ProjectionType) then
begin
WriteStr(LPS, View.Engine.MapProjectionType);
WriteStr(MPS, P.ProjectionType);
raise EArgumentException.CreateFmt(
'%s has different projection type (%s) from the base map (%s).',
[AValue, LPS, MPS]);
end;
end;
FMapProvider := AValue;
ItemChanged;
end;
procedure TMapLayer.SetOpacity(AValue: Single);
begin
AValue := EnsureRange(AValue, 0.0, 1.0);
if FOpacity = AValue then
Exit;
FOpacity:=AValue;
ItemChanged;
end;
procedure TMapLayer.SetPointsOfInterest(AValue: TPointsOfInterest);
begin
FPointsOfInterest.Assign(AValue);
end;
procedure TMapLayer.SetTracks(AValue: TMapTracks);
begin
FTracks.Assign(AValue);
end;
procedure TMapLayer.SetUseThreads(AValue: Boolean);
begin
if FUseThreads = AValue then
Exit;
FUseThreads := AValue;
ItemChanged;
end;
procedure TMapLayer.ItemChanged;
begin
if Assigned(FComboLayer) then
begin
FComboLayer.TileLayer.MapProvider := FMapProvider;
FComboLayer.TileLayer.UseThreads := FUseThreads;
FComboLayer.TileLayer.DrawMode := FDrawMode;
FComboLayer.TileLayer.Opacity := FOpacity;
FComboLayer.Visible := FVisible;
end;
Changed(False);
end;
constructor TMapLayer.Create(ACollection: TCollection);
begin
inherited Create(ACollection);
FUseThreads := True;
FDrawMode := idmUseOpacity;
FOpacity := 0.25;
FVisible := True;
FTag := 0;
FPointsOfInterest := TPointsOfInterest.Create(Self, BASE_Z_POI);
FTracks := TMapTracks.Create(Self, BASE_Z_TRACK);
FComboLayer := TGPSComboLayer.Create;
View.GPSItems.Add(FComboLayer, _TILELAYERS_ID_, Self.Index + BASE_Z_LAYER);
end;
destructor TMapLayer.Destroy;
begin
FPointsOfInterest.Free;
FTracks.Free;
if Assigned(FComboLayer) then
View.GPSItems.Delete(FComboLayer);
inherited Destroy;
end;
function TMapLayer.HitTest(constref Area: TRealArea): TMapObjectList;
begin
Result := TMapObjectList.AddListToResult(PointsOfInterest.HitTest(Area),
Tracks.HitTest(Area));
end;
procedure TMapLayer.AssignFromGPSList(AList: TGPSObjectList);
procedure AddPoint(APoint: TGPSPoint);
begin
with PointsOfInterest.Add as TMapPoint do
begin
Caption := APoint.Name;
Longitude := APoint.Lon;
Latitude := APoint.Lat;
Elevation := APoint.Elevation;
DateTime := APoint.DateTime;
end;
end;
procedure AddTrack(ATrack: TGPSTrack);
var
I: Integer;
P: TGPSPoint;
begin
with Tracks.Add as TMapTrack do
begin
Caption := ATrack.Name;
for I := 0 to Pred(ATrack.Points.Count) do
with Points.Add as TMapTrackPoint do
begin
P := ATrack.Points[I];
Caption := P.Name;
Longitude := P.Lon;
Latitude := P.Lat;
Elevation := P.Elevation;
DateTime := P.DateTime;
if ATrack.Points[I].ExtraData is TSegmentExtraData then
Mark := TSegmentExtraData(ATrack.Points[I].ExtraData).Mark;
end;
if ATrack.ExtraData is TTrackExtraData then
begin
LineWidth := TTrackExtraData(ATrack.ExtraData).Width;
LineColor := TTrackExtraData(ATrack.ExtraData).Color;
end;
end;
end;
var
I: Integer;
begin
if not Assigned(AList) then
Exit;
PointsOfInterest.Clear;
Tracks.Clear;
for I := 0 to Pred(AList.Count) do
if AList[I] is TGPSPoint then
AddPoint(TGPSPoint(AList[I]))
else if AList[I] is TGPSTrack then
AddTrack(TGPSTrack(AList[I]))
else
{TODO};
end;
{ TMapLayers }
function TMapLayers.GetView: TMapView;
begin
Result := MCOwner;
end;
function TMapLayers.GetLayer: TMapLayer;
begin
Result := Nil;
end;
procedure TMapLayers.FixOrder(APrevIndex, AIndex: Integer);
var
I, T, B: Integer;
begin
T := Min(APrevIndex, AIndex);
B := Max(APrevIndex, AIndex);
if APrevIndex < 0 then
begin
T := AIndex;
B := Pred(Count);
end;
for I := T to B do
View.GPSItems.ChangeZOrder(TMapItem(Items[I]).GPSObj, I + FBaseZ);
end;
{ TDrawObjJob }
function TDrawObjJob.pGetTask: integer;
var
i: integer;
begin
if not(AllRun) and not(Cancelled) then
begin
for i := Low(FStates) to High(FStates) do
if FStates[i]=0 then
begin
result := i+1;
Exit;
end;
AllRun:=True;
end;
Result := ALL_TASK_COMPLETED;
for i := Low(FStates) to High(FStates) do
if FStates[i]=1 then
begin
Result := NO_MORE_TASK;
Exit;
end;
end;
procedure TDrawObjJob.pTaskStarted(aTask: integer);
begin
FRunning := True;
FStates[aTask-1] := 1;
end;
procedure TDrawObjJob.pTaskEnded(aTask: integer; aExcept: Exception);
begin
if Assigned(aExcept) then
FStates[aTask-1] := 3
else
FStates[aTask-1] := 2;
end;
procedure TDrawObjJob.ExecuteTask(aTask: integer; FromWaiting: boolean);
var
iObj: integer;
Obj: TGpsObj;
begin
iObj := aTask-1;
Obj := FLst[iObj];
Viewer.DrawGpsObj(FArea, Obj);
end;
function TDrawObjJob.Running: boolean;
begin
Result := FRunning;
end;
constructor TDrawObjJob.Create(aViewer: TMapView; aLst: TGPSObjList;
const aArea: TRealArea);
begin
FArea := aArea;
FLst := aLst;
SetLength(FStates, FLst.Count);
Viewer := aViewer;
AllRun := false;
Name := 'DrawObj';
end;
destructor TDrawObjJob.Destroy;
begin
inherited Destroy;
FreeAndNil(FLst);
end;
{ TMapView }
procedure TMapView.SetActive(AValue: boolean);
begin
if FActive = AValue then Exit;
if AValue and (MapProvider = '') then
// Raising an exception won't let the component to be loaded
if not (csLoading in ComponentState) then
raise Exception.Create('MapProvider is not selected.');
FActive := AValue;
if FActive then
ActivateEngine
else
Engine.Active := false;
end;
procedure TMapView.SetCacheLocation(AValue: TCacheLocation);
var
NewPath: String;
OldLoc: TCacheLocation;
begin
if FCacheLocation = AValue then
Exit;
OldLoc := FCacheLocation;
FCacheLocation := AValue;
NewPath := CacheDirectory(AValue, CachePath);
if NewPath = Engine.CachePath then
Exit;
ChangeCachePath(OldLoc, NewPath);
end;
procedure TMapView.SetCacheMaxAge(AValue: Integer);
begin
if Engine.CacheMaxAge = AValue then
Exit;
Engine.CacheMaxAge := AValue;
UpdateLayers;
end;
function TMapView.GetCacheOnDisk: boolean;
begin
Result := FCacheOnDisk;
end;
function TMapView.GetCenter: TRealPoint;
begin
Result := Engine.Center;
end;
function TMapView.GetCyclic: Boolean;
begin
Result := Engine.Cyclic;
end;
function TMapView.GetDownloadEngine: TMvCustomDownloadEngine;
begin
if FDownloadEngine = nil then
Result := FBuiltinDownloadEngine
else
Result := FDownloadEngine;
end;
function TMapView.GetDrawingEngine: TMvCustomDrawingEngine;
begin
if FDrawingEngine = nil then
Result := FBuiltinDrawingEngine
else
Result := FDrawingEngine;
end;
function TMapView.GetDrawPreviewTiles: Boolean;
begin
Result := Engine.DrawPreviewTiles;
end;
function TMapView.GetGPSItems: TGPSObjectList;
begin
Result := GetGPSLayer(5);
end;
function TMapView.GetGPSLayer(Layer: Integer): TGPSObjectList;
begin
Result := FGPSItems[Layer mod 10];
end;
function TMapView.GetInactiveColor: TColor;
begin
Result := FPColorToTColor(Engine.BkColor);
end;
function TMapView.GetLayers: TMapLayers;
begin
Result := FLayers;
end;
function TMapView.GetMapProvider: String;
begin
result := Engine.MapProvider;
end;
function TMapView.GetOnCenterMove: TNotifyEvent;
begin
result := Engine.OnCenterMove;
end;
function TMapView.GetOnChange: TNotifyEvent;
begin
Result := Engine.OnChange;
end;
function TMapView.GetOnZoomChange: TNotifyEvent;
begin
Result := Engine.OnZoomChange;
end;
function TMapView.GetUseThreads: boolean;
begin
Result := Engine.UseThreads;
end;
function TMapView.GetZoom: integer;
begin
result := Engine.Zoom;
end;
function TMapView.GetZoomToCursor: Boolean;
begin
Result := Engine.ZoomToCursor;
end;
function TMapView.IsCacheMaxAgeStored: Boolean;
begin
Result := Engine.CacheMaxAge <> MaxInt;
end;
function TMapView.IsCachePathStored: Boolean;
begin
Result := not SameText(CachePath, 'cache/');
end;
function TMapView.IsFontStored: Boolean;
begin
Result := SameText(FFont.Name, 'default') and (FFont.Size = 0) and
(FFont.Style = []) and (FFont.Color = clBlack);
end;
function TMapView.IsLayersStored: Boolean;
begin
Result := True;
end;
procedure TMapView.SetCacheOnDisk(AValue: boolean);
begin
FCacheOnDisk := AValue;
if csDesigning in ComponentState
then Engine.CacheOnDisk := False
else Engine.CacheOnDisk := AValue;
UpdateLayers;
end;
procedure TMapView.SetCachePath(AValue: String);
var
NewPath: String;
begin
if FCachePath = AValue then
Exit;
FCachePath := AValue;
NewPath := CacheDirectory(CacheLocation, AValue);
if NewPath = Engine.CachePath then
Exit;
ChangeCachePath(CacheLocation, NewPath);
end;
procedure TMapView.SetCenter(AValue: TRealPoint);
begin
Engine.Center := AValue;
Invalidate;
end;
procedure TMapView.SetCyclic(AValue: Boolean);
begin
Engine.Cyclic := AValue;
UpdateLayers;
Invalidate;
end;
procedure TMapView.SetDebugTiles(AValue: Boolean);
begin
if FDebugTiles = AValue then exit;
FDebugTiles := AValue;
Invalidate;
end;
procedure TMapView.SetDefaultTrackColor(AValue: TColor);
begin
if FDefaultTrackColor = AValue then exit;
FDefaultTrackColor := AValue;
Invalidate;
end;
procedure TMapView.SetDefaultTrackWidth(AValue: Integer);
begin
if FDefaultTrackWidth = AValue then exit;
FDefaultTrackWidth := AValue;
Invalidate;
end;
procedure TMapView.SetDownloadEngine(AValue: TMvCustomDownloadEngine);
begin
FDownloadEngine := AValue;
FEngine.DownloadEngine := GetDownloadEngine;
UpdateLayers;
end;
procedure TMapView.SetDrawingEngine(AValue: TMvCustomDrawingEngine);
begin
FDrawingEngine := AValue;
if AValue = nil then
begin
FBuiltinDrawingEngine.CreateBuffer(ClientWidth, ClientHeight);
FEngine.CacheItemClass := FBuiltinDrawingEngine.GetCacheItemClass;
end
else begin
FBuiltinDrawingEngine.CreateBuffer(0, 0);
FDrawingEngine.CreateBuffer(ClientWidth, ClientHeight);
FEngine.CacheItemClass := FDrawingEngine.GetCacheItemClass;
end;
UpdateFont(nil);
end;
procedure TMapView.SetDrawPreviewTiles(AValue: Boolean);
begin
Engine.DrawPreviewTiles := AValue;
end;
procedure TMapView.SetFont(AValue: TFont);
begin
FFont.Assign(AValue);
UpdateFont(nil);
end;
procedure TMapView.SetInactiveColor(AValue: TColor);
begin
Engine.BkColor := TColorToFPColor(AValue);
if not IsActive then
Invalidate;
end;
procedure TMapView.SetLayers(const ALayers: TMapLayers);
begin
FLayers.Assign(ALayers);
end;
procedure TMapView.ActivateEngine;
begin
Engine.SetSize(ClientWidth,ClientHeight);
Engine.Active := IsActive;
end;
procedure TMapView.SetMapProvider(AValue: String);
begin
//if AValue = '' then
// raise EArgumentException.Create('Empty map provider is not allowed.');
Engine.MapProvider := AValue;
if AValue = '' then
Active := False;
Invalidate;
end;
procedure TMapView.SetOnCenterMove(AValue: TNotifyEvent);
begin
Engine.OnCenterMove := AValue;
end;
procedure TMapView.SetOnChange(AValue: TNotifyEvent);
begin
Engine.OnChange := AValue;
end;
procedure TMapView.SetOnZoomChange(AValue: TNotifyEvent);
begin
Engine.OnZoomChange := AValue;
end;
procedure TMapView.SetOptions(AValue: TMapViewOptions);
begin
if FOptions = AValue then Exit;
FOptions := AValue;
if Engine.InDrag and not (mvoMouseDragging in FOptions) then
begin
Engine.DragObj.AbortDrag;
Invalidate;
end;
end;
procedure TMapView.SetPOIImage(const AValue: TBitmap);
begin
if FPOIImage = AValue then exit;
FPOIImage := AValue;
Invalidate;
end;
procedure TMapView.SetPOIImages(const AValue: TCustomImageList);
begin
if FPOIImages = AValue then exit;
FPOIImages := AValue;
Invalidate;
end;
procedure TMapView.SetPOIImagesWidth(AValue: Integer);
begin
if FPOIImagesWidth = AValue then exit;
FPOIImagesWidth := AValue;
Invalidate;
end;
procedure TMapView.SetPOITextBgColor(AValue: TColor);
begin
if FPOITextBgColor = AValue then exit;
FPOITextBgColor := AValue;
Invalidate;
end;
procedure TMapView.SetUseThreads(AValue: boolean);
begin
Engine.UseThreads := aValue;
end;
procedure TMapView.SetZoom(AValue: integer);
begin
Engine.Zoom := EnsureRange(AValue, FZoomMin, FZoomMax);
Invalidate;
end;
procedure TMapView.SetZoomMax(AValue: Integer);
begin
if FZoomMax = AValue then Exit;
FZoomMax := EnsureRange(AValue, FZoomMin, 19);
Engine.ZoomMax := FZoomMax;
Zoom := GetZoom;
end;
procedure TMapView.SetZoomMin(AValue: Integer);
begin
if FZoomMin = AValue then Exit;
FZoomMin := EnsureRange(AValue, 1, FZoomMax);
Engine.ZoomMin := FZoomMin;
Zoom := GetZoom;
end;
procedure TMapView.SetZoomToCursor(AValue: Boolean);
begin
Engine.ZoomToCursor := AValue;
end;
function TMapView.DoMouseWheel(Shift: TShiftState; WheelDelta: Integer;
MousePos: TPoint): Boolean;
begin
Result:=inherited DoMouseWheel(Shift, WheelDelta, MousePos);
if IsActive and (mvoMouseZooming in FOptions) then
begin
Engine.MouseWheel(self,Shift,WheelDelta,MousePos,Result);
Invalidate;
end;
end;
procedure TMapView.MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
inherited MouseDown(Button, Shift, X, Y);
if IsActive and (mvoMouseDragging in FOptions) then
if Button = mbLeft then
begin
Engine.MouseDown(self,Button,Shift,X,Y);
Invalidate;
end;
end;
procedure TMapView.MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
inherited MouseUp(Button, Shift, X, Y);
if IsActive then
if Button = mbLeft then
begin
Engine.MouseUp(self,Button,Shift,X,Y);
Engine.Redraw;
Invalidate;
end;
end;
procedure TMapView.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
inherited MouseMove(Shift, X, Y);
if IsActive then
begin
Engine.MouseMove(self,Shift,X,Y);
if Engine.InDrag
then Invalidate;
end;
end;
procedure TMapView.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited;
if (AComponent = FPOIImages) and (Operation = opRemove) then
FPOIImages := nil;
end;
procedure TMapView.DblClick;
begin
inherited DblClick;
if IsActive then
begin
Engine.DblClick(self);
Invalidate;
end;
end;
procedure TMapView.DoOnResize;
begin
inherited DoOnResize;
//cancel all rendering threads
Engine.CancelCurrentDrawing;
DrawingEngine.CreateBuffer(ClientWidth, ClientHeight);
if IsActive then
begin
Engine.SetSize(ClientWidth, ClientHeight);
Invalidate;
end;
end;
procedure TMapView.Paint;
const
FREE_DRAG = 0; //(TILE_SIZE * TILE_SIZE) div 4;
procedure DrawCenter;
var
C: TPoint;
begin
C := Point(ClientWidth div 2, ClientHeight div 2);
Canvas.Pen.Color := clBlack;
Canvas.Pen.Width := 1;
Canvas.Line(C.X, C.Y - 15, C.X, C.Y + 15);
Canvas.Line(C.X - 15, C.Y, C.X + 15, C.Y);
end;
procedure InactiveDraw;
begin
Canvas.Brush.Color := InactiveColor;
Canvas.Brush.Style := bsSolid;
Canvas.FillRect(0, 0, ClientWidth, ClientHeight);
end;
procedure FullRedraw;
var
W: Integer;
begin
Engine.Redraw;
W := Canvas.Width;
if Cyclic then
W := Min(1 shl Zoom * TILE_SIZE, W);
DrawObjects(Default(TTileId), 0, 0, W - 1, Canvas.Height);
DrawingEngine.PaintToCanvas(Canvas);
if DebugTiles then
DrawCenter;
end;
procedure DragDraw;
var
O: TPoint;
begin
O := Point(Engine.DragObj.OfsX, Engine.DragObj.OfsY);
// Free drag up to half of the tile
if ((O.X * O.X + O.Y * O.Y) < FREE_DRAG) then
begin
DrawingEngine.PaintToCanvas(Canvas);
DrawingEngine.PaintToCanvas(Canvas, O);
if DebugTiles then
DrawCenter;
end
else
FullRedraw;
end;
begin
inherited Paint;
if IsActive
then if Engine.InDrag
then DragDraw
else FullRedraw
else
InactiveDraw;
end;
procedure TMapView.OnGPSItemsModified(Sender: TObject; objs: TGPSObjList;
Adding: boolean);
var
Area, objArea, visArea: TRealArea;
begin
if Adding and Assigned(Objs) then
begin
objArea := GetAreaOf(Objs);
visArea := GetVisibleArea;
if hasIntersectArea(objArea, visArea) then
begin
Area := IntersectArea(objArea, visArea);
Invalidate;
end
end
else
Engine.Redraw;
end;
procedure TMapView.StartDragging(X, Y: Integer);
begin
Engine.DragObj.MouseDown(Engine, X, Y);
end;
procedure TMapView.EndDragging(X, Y: Integer);
var
Drag: Boolean;
begin
Drag := Engine.InDrag;
Engine.DragObj.MouseUp(X, Y);
if Drag then
Invalidate;
end;
procedure TMapView.AbortDragging;
begin
Engine.DragObj.AbortDrag;
end;
function TMapView.TrackLineColor(AColor: TColor; ExtraData: TObject): TColor;
begin
if AColor = clDefault then
begin
Result := ColorToRGB(FDefaultTrackColor);
if (ExtraData <> Nil) and ExtraData.InheritsFrom(TDrawingExtraData) then
Result := TDrawingExtraData(ExtraData).Color;
end
else
Result := ColorToRGB(AColor);
end;
function TMapView.TrackLineWidth(AWidth: Double; ExtraData: TObject): Integer;
begin
if AWidth = -1 then
begin
Result := FDefaultTrackWidth;
if (ExtraData <> Nil) and ExtraData.InheritsFrom(TTrackExtraData) then
Result := mmToPx(TTrackExtraData(ExtraData).Width);
end
else
Result := mmToPx(AWidth);
if Result < 1 then
Result := 1;
end;
procedure TMapView.DrawTrack(const Area: TRealArea; trk: TGPSTrack);
var
I, L, T, WS: Integer;
ClipRect: TRect;
iPt1, iPt2, iPt3, iPt4: TPoint;
ToEast, EndSegm, ConnSegm: Boolean;
pt1, pt2: TRealPoint;
trkColor, connColor: TColor;
trkWidth, connWidth: Integer;
procedure ClipDrawLine(P1, P2: TPoint); inline;
begin
if not ClipLineToRect(ClipRect, P1, P2) then
DrawingEngine.Line(P1.X, P1.Y, P2.X, P2.Y);
end;
begin
if not trk.Visible or (trk.Points.Count = 0) then
exit;
// Determine track color
trkColor := TrackLineColor(trk.LineColor, trk.ExtraData);
// Determine track width
trkWidth := TrackLineWidth(trk.LineWidth, trk.ExtraData);
ConnSegm := trk.ConnectColor <> clNone;
if ConnSegm then
begin
if trk.ConnectColor = clDefault
then connColor := trkColor
else connColor := TrackLineColor(trk.ConnectColor, trk.ExtraData);
if trk.ConnectWidth < 0.01
then connWidth := trkWidth
else connWidth := TrackLineWidth(trk.ConnectWidth, trk.ExtraData);
end;
DrawingEngine.PenColor := trkColor;
DrawingEngine.PenWidth := trkWidth;
// Clipping rectangle
if Cyclic then
ClipRect := Rect(0, 0, ClientWidth, ClientHeight)
else
begin
L := Max(0, Engine.MapLeft);
T := Max(0, Engine.MapTop);
WS := ZoomFactor(Zoom) * TILE_SIZE;
ClipRect := Rect(L, T, Min(Engine.MapLeft + WS, ClientWidth),
Min(Engine.MapTop + WS, ClientHeight));
end;
pt1 := trk.Points[0].RealPoint;
iPt1 := Engine.LatLonToScreen(pt1);
EndSegm := TSegmentExtraData.MarkOf(trk.Points[0].ExtraData) = smEnd;
for I := 1 to Pred(trk.Points.Count) do
begin
pt2 := trk.Points[I].RealPoint;
iPt2 := Engine.LatLonToScreen(pt2);
ToEast := GoingEast(pt1.Lon, pt2.Lon); // Eastwards?
iPt2 := CyclicPointOf(iPt2, iPt1.X, ToEast); // Nearest iPt2 to iPt1
// Rightmost cyclic copy of the segment
if ToEast then
begin
iPt3 := CyclicPointOf(iPt1, ClipRect.Right); // Left point
iPt4 := (iPt2 - iPt1); // delta to the right point
end
else
begin
iPt3 := CyclicPointOf(iPt2, ClipRect.Right); // Left point
iPt4 := (iPt1 - iPt2); // delta to the right point
end;
if EndSegm and ConnSegm then
begin
DrawingEngine.PenColor := connColor;
DrawingEngine.PenWidth := connWidth;
end;
if not EndSegm or ConnSegm then
// Draw all copies of the segment, right to left
repeat
ClipDrawLine(iPt3, iPt3 + iPt4);
iPt3 := CyclicPointOf(iPt3, Pred(iPt3.X), False); // Next left cyclic iPt3
until Max(iPt3.X, iPt3.X + iPt4.X) < ClipRect.Left;
if EndSegm and ConnSegm then
begin
DrawingEngine.PenColor := trkColor;
DrawingEngine.PenWidth := trkWidth;
end;
pt1 := pt2;
iPt1 := iPt2;
EndSegm := TSegmentExtraData.MarkOf(trk.Points[I].ExtraData) = smEnd;
end;
end;
procedure TMapView.DrawArea(const Area: TRealArea; ar: TGPSArea);
var
Pts: array of TPoint;
I, C: Integer;
NoFill: Boolean;
WS: Int64;
begin
if not ar.Visible or (ar.Points.Count = 0) then
Exit;
if Cyclic then
begin
WS := ZoomFactor(Zoom) * TILE_SIZE;
if (WS < ClientWidth) then
begin
{TODO Draw multiple copies of the area}
Exit; // Not implemented, exit
end;
end;
{TODO Fix drawing when the area crosses the date line, see DrawTrack}
C := ar.Points.Count;
NoFill := (ar.FillColor = clNone);
if NoFill then
Inc(C);
SetLength(Pts, C);
for I := 0 to Pred(ar.Points.Count) do
Pts[I] := Engine.LatLonToScreen(ar.Points[I].RealPoint);
if NoFill then
Pts[Pred(C)] := Pts[0];
if ar.LineColor = clNone then
DrawingEngine.PenStyle := psClear
else
begin
DrawingEngine.PenStyle := psSolid;
DrawingEngine.PenColor := TrackLineColor(ar.LineColor, ar.ExtraData);
DrawingEngine.PenWidth := TrackLineWidth(ar.LineWidth, ar.ExtraData);
end;
if NoFill then
DrawingEngine.Polyline(Pts)
else
begin
DrawingEngine.BrushStyle := bsSolid;
DrawingEngine.BrushColor := ar.FillColor;
DrawingEngine.Polygon(Pts);
end;
end;
procedure TMapView.DrawPointOfInterest(const Area: TRealArea; APt: TGPSPointOfInterest);
var
pt: TPoint;
ptCyc: TPointArray;
ptColor: TColor;
extent: TSize;
s: String;
bmp: TBitmap;
w, h: Integer;
procedure DrawOne(pt: TPoint);
begin
if Assigned(bmp) then
DrawingEngine.DrawBitmap(pt.X - w div 2, pt.Y - h, bmp, true)
else
begin
// ... or as cross
ptColor := clRed;
if (APt.ExtraData <> nil) and APt.ExtraData.InheritsFrom(TDrawingExtraData) then
ptColor := TDrawingExtraData(APt.ExtraData).Color;
DrawingEngine.PenColor := ptColor;
DrawingEngine.PenWidth := 3;
DrawingEngine.Line(pt.X, pt.Y - 5, pt.X, pt.Y + 5);
DrawingEngine.Line(pt.X - 5, pt.Y, pt.X + 5, pt.Y);
pt.Y := pt.Y + 5;
end;
if FPOITextBgColor = clNone then
DrawingEngine.BrushStyle := bsClear
else
begin
DrawingEngine.BrushStyle := bsSolid;
DrawingEngine.BrushColor := FPOITextBgColor;
end;
DrawingEngine.TextOut(pt.X - extent.CX div 2, pt.Y + 5, s);
end;
begin
pt := Engine.LatLonToScreen(APt.RealPoint);
bmp := Nil;
try
// Draw point as symbol from image list ...
if Assigned(FPOIImages) and (APt.ImageIndex <> -1) and (APt.ImageIndex < FPOIImages.Count) then
begin
bmp := TBitmap.Create;
FPOIImages.GetBitmap(APt.ImageIndex, bmp);
{$IF LCL_FullVersion >= 2000000}
w := FPOIImages.WidthForPPI[FPOIImagesWidth, Font.PixelsPerInch];
h := FPOIImages.HeightForPPI[FPOIImagesWidth, Font.PixelsPerInch];
{$ELSE}
w := FPOIImages.Width;
h := FPOIImages.Height;
{$IFEND}
end;
// Draw point text
s := APt.Name;
if FPOITextBgColor <> clNone then
s := ' ' + s + ' ';
extent := DrawingEngine.TextExtent(s);
if Cyclic then
begin
ptCyc := CyclicPointsOf(pt);
for pt in ptCyc do
DrawOne(pt);
end
else
DrawOne(pt);
finally
bmp.Free;
end;
end;
procedure TMapView.DrawPt(const Area: TRealArea; APt: TGPSPoint);
var
Pt: TPoint;
PtCyc: TPointArray;
PtColor: TColor;
extent: TSize;
s: String;
procedure DrawOne(Pt: TPoint);
begin
// Draw point marker
if Assigned(FPOIImage) and not (FPOIImage.Empty) then
DrawingEngine.DrawBitmap(Pt.X - FPOIImage.Width div 2, Pt.Y - FPOIImage.Height, FPOIImage, true)
else begin
DrawingEngine.PenColor := ptColor;
DrawingEngine.PenWidth := 3;
DrawingEngine.Line(Pt.X, Pt.Y - 5, Pt.X, Pt.Y + 5);
DrawingEngine.Line(Pt.X - 5, Pt.Y, Pt.X + 5, Pt.Y);
Pt.Y := Pt.Y + 5;
end;
// Draw point text
s := APt.Name;
if FPOITextBgColor = clNone then
DrawingEngine.BrushStyle := bsClear
else begin
DrawingEngine.BrushStyle := bsSolid;
DrawingEngine.BrushColor := FPOITextBgColor;
s := ' ' + s + ' ';
end;
extent := DrawingEngine.TextExtent(s);
DrawingEngine.Textout(Pt.X - extent.CX div 2, Pt.Y + 5, s);
end;
begin
if Assigned(FOnDrawGpsPoint) then begin
FOnDrawGpsPoint(Self, DrawingEngine, APt);
exit;
end;
Pt := Engine.LatLonToScreen(APt.RealPoint);
PtColor := clRed;
if APt.ExtraData <> nil then
begin
if APt.ExtraData.inheritsFrom(TDrawingExtraData) then
PtColor := TDrawingExtraData(APt.ExtraData).Color;
end;
PtCyc := CyclicPointsOf(Pt);
for Pt in PtCyc do
DrawOne(Pt);
end;
procedure TMapView.DrawGpsObj(const Area: TRealArea; AObj: TGPSObj);
begin
GPSItems.Lock;
try
AObj.Draw(Self, Area);
finally
GPSItems.Unlock;
end;
end;
function TMapView.GetCacheMaxAge: Integer;
begin
Result := Engine.CacheMaxAge;
end;
procedure TMapView.CallAsyncInvalidate;
Begin
if not(AsyncInvalidate) then
begin
AsyncInvalidate := true;
Engine.Jobqueue.QueueAsyncCall(@DoAsyncInvalidate, 0);
end;
end;
procedure TMapView.DrawObjects(const TileId: TTileId;
aLeft, aTop,aRight,aBottom: integer);
var
Area: TRealArea;
lst: TGPSObjList;
I, J: Integer;
begin
Area.TopLeft := Engine.ScreenToLatLon(Point(aLeft, aTop));
Area.BottomRight := Engine.ScreenToLatLon(Point(aRight, aBottom));
for J := 0 to High(FGPSItems) do
if FGPSItems[J].Visible and (FGPSItems[J].Count > 0) then
begin
lst := FGPSItems[J].GetObjectsInArea(Area);
try
if lst.Count > 0 then
begin
for I := 0 to Pred(lst.Count) do
if lst[I].Visible then
DrawGpsObj(Area, lst[I]);
end;
finally
FreeAndNil(Lst);
end;
end;
end;
procedure TMapView.DoAsyncInvalidate(Data: PtrInt);
Begin
Invalidate;
AsyncInvalidate := false;
end;
procedure TMapView.DoDrawStretchedTile(const TileId: TTileID; X, Y: Integer;
TileImg: TPictureCacheItem; const R: TRect);
begin
if Assigned(TileImg) then
DrawingEngine.DrawScaledCacheItem(Rect(X, Y, X + TILE_SIZE, Y + TILE_SIZE), R, TileImg)
else
DrawingEngine.FillPixels(X, Y, X + TILE_SIZE, Y + TILE_SIZE, InactiveColor);
if FDebugTiles then
DoDrawTileInfo(TileID, X, Y);
end;
procedure TMapView.DoDrawTile(const TileId: TTileId; X, Y: integer;
TileImg: TPictureCacheItem);
begin
if Assigned(TileImg) then
DrawingEngine.DrawCacheItem(X, Y, TileImg)
else
DrawingEngine.FillPixels(X, Y, X + TILE_SIZE, Y + TILE_SIZE, InactiveColor);
if FDebugTiles then
DoDrawTileInfo(TileID, X, Y);
end;
procedure TMapView.DoDrawTileInfo(const TileID: TTileID; X, Y: Integer);
begin
DrawingEngine.PenColor := clGray;
DrawingEngine.PenWidth := 1;
DrawingEngine.Line(X, Y, X, Y + TILE_SIZE);
DrawingEngine.Line(X, Y, X + TILE_SIZE, Y);
DrawingEngine.Line(X + TILE_SIZE, Y, X + TILE_SIZE, Y + TILE_SIZE);
DrawingEngine.Line(X, Y + TILE_SIZE, X + TILE_SIZE, Y + TILE_SIZE);
end;
procedure TMapView.DoEraseBackground(const R: TRect);
begin
DrawingEngine.FillPixels(R.Left, R.Top, R.Right, R.Bottom, InactiveColor);
end;
procedure TMapView.DoTileDownloaded(const TileId: TTileId);
begin
// TODO: Include tile information to optimize redraw.
CallAsyncInvalidate;
end;
function TMapView.IsActive: Boolean;
begin
Result := FActive
end;
constructor TMapView.Create(AOwner: TComponent);
var
I: Integer;
begin
inherited Create(AOwner);
Width := 150;
Height := 150;
FLayers := CreateLayers;
FActive := false;
FOptions := DefaultMapViewOptions;
FDefaultTrackColor := clRed;
FDefaultTrackWidth := 1;
for I := 0 to High(FGPSItems) do
begin
FGPSItems[I] := TGPSObjectList.Create;
FGPSItems[I].OnModified := @OnGPSItemsModified;
end;
{$IFDEF MSWindows}
FBuiltinDownloadEngine := TMvDEWin.Create(self);
{$ELSE}
FBuiltinDownloadEngine := TMvDEFpc.Create(self);
{$ENDIF}
FBuiltinDownloadEngine.Name := 'BuiltInDLE';
FCacheLocation := clProfile;
FEngine := TMapViewerEngine.Create(self);
FEngine.BkColor := colWhite;
{FEngine.}CachePath := 'cache/';
{FEngine.}CacheOnDisk := true;
FEngine.OnDrawTile := @DoDrawTile;
FEngine.OnDrawStretchedTile := @DoDrawStretchedTile;
FEngine.OnEraseBackground := @DoEraseBackground;
FEngine.OnTileDownloaded := @DoTileDownloaded;
FEngine.DrawPreviewTiles := True;
FEngine.DrawTitleInGuiThread := false;
FEngine.DownloadEngine := FBuiltinDownloadEngine;
FEngine.ZoomToCursor := True;
FBuiltinDrawingEngine := TMvIntfGraphicsDrawingEngine.Create(self);
FEngine.CacheItemClass := FBuiltinDrawingEngine.GetCacheItemClass;
FBuiltinDrawingEngine.Name := 'BuiltInDE';
FBuiltinDrawingEngine.CreateBuffer(Width, Height);
FFont := TFont.Create;
FFont.Name := 'default';
FFont.Size := 0;
FFont.Style := [];
FFont.Color := clBlack;
FFont.OnChange := @UpdateFont;
FPOIImage := TBitmap.Create;
FPOIImage.OnChange := @UpdateImage;
FPOITextBgColor := clNone;
FCenter := TMapCenter.Create(Self);
FCenter.Longitude := 0.0;
FCenter.Latitude := 0.0;
FZoomMin := 1;
FZoomMax := 19;
Zoom := 1;
end;
destructor TMapView.Destroy;
var
I: Integer;
begin
Active := False;
Engine.Jobqueue.RemoveAsyncCalls(Self);
FFont.Free;
FreeAndNil(FPOIImage);
FLayers.Free;
for I := 0 to High(FGPSItems) do
FreeAndNil(FGPSItems[I]);
FCenter.Free;
inherited Destroy;
end;
function TMapView.CyclicPointOf(APoint: TPoint; ARefX: LongInt;
Eastwards: Boolean): TPoint;
var
WorldSize: Int64;
begin
Result := APoint;
WorldSize := ZoomFactor(Zoom) * TILE_SIZE;
if Eastwards then
begin
while Result.X < ARefX do
Inc(Result.X, WorldSize);
while Result.X >= ARefX + WorldSize do
Dec(Result.X, WorldSize);
end
else
begin
while Result.X > ARefX do
Dec(Result.X, WorldSize);
while Result.X <= ARefX - WorldSize do
Inc(Result.X, WorldSize);
end;
end;
function TMapView.CyclicPointsOf(APoint: TPoint): TPointArray;
var
I, R, L, WorldSize, CanvasWidth: LongInt;
begin
Result := Default(TPointArray);
if not Cyclic then
begin
SetLength(Result, 1);
Result[0] := APoint;
end
else
begin
WorldSize := ZoomFactor(Zoom) * TILE_SIZE;
CanvasWidth := Canvas.Width;
SetLength(Result, 1{APoint} + (1{Round} + CanvasWidth div WorldSize));
Result[0] := APoint;
I := 1; R := APoint.X + WorldSize; L := APoint.X - WorldSize;
while (R < CanvasWidth) or (L >= 0) do
begin
if R < CanvasWidth then
begin
Result[I].Y := APoint.Y;
Result[I].X := R;
Inc(I);
end;
if L >= 0 then
begin
Result[I].Y := APoint.Y;
Result[I].X := L;
Inc(I);
end;
Inc(R, WorldSize);
Dec(L, WorldSize);
end;
if I < Length(Result) then
SetLength(Result, I);
end;
end;
procedure TMapView.SaveToFile(AClass: TRasterImageClass; const AFileName: String);
var
stream: TFileStream;
begin
stream := TFileStream.Create(AFileName, fmCreate + fmShareDenyNone);
try
SaveToStream(AClass, stream);
finally
stream.Free;
end;
end;
function TMapView.SaveToImage(AClass: TRasterImageClass): TRasterImage;
begin
Result := DrawingEngine.SaveToImage(AClass);
end;
procedure TMapView.SaveToStream(AClass: TRasterImageClass; AStream: TStream);
var
img: TRasterImage;
begin
img := SaveToImage(AClass);
try
img.SaveToStream(AStream);
finally
img.Free;
end;
end;
function TMapView.ScreenToLatLon(aPt: TPoint): TRealPoint;
begin
Result := Engine.ScreenToLatLon(aPt);
end;
function TMapView.ScreenToLonLat(aPt: TPoint): TRealPoint;
begin
Result := Engine.ScreenToLatLon(aPt);
end;
function TMapView.LatLonToScreen(aPt: TRealPoint): TPoint;
begin
Result := Engine.LatLonToScreen(aPt);
end;
function TMapView.LatLonToScreen(Lat, Lon: Double): TPoint;
begin
Result := LatLonToScreen(RealPoint(Lat, Lon));
end;
function TMapView.LonLatToScreen(aPt: TRealPoint): TPoint;
begin
Result := Engine.LatLonToScreen(aPt);
end;
procedure TMapView.GetMapProviders(lstProviders: TStrings);
var
L: TStringList;
begin
L := TStringList.Create;
try
Engine.GetMapProviders(L);
L.Sort;
lstProviders.Assign(L);
finally
L.Free;
end;
// Engine.GetMapProviders(lstProviders);
end;
procedure TMapView.WaitEndOfRendering;
begin
Engine.Jobqueue.WaitAllJobTerminated(Engine);
end;
function TMapView.ObjsAtScreenPt(X, Y: Integer; ATolerance: Integer = -1): TGPSObjarray;
const
DELTA = 3;
var
rArea: TRealArea;
gpsList: TGPSObjList;
i, J: Integer;
L: TGPSObjectList;
begin
Result := nil;
if ATolerance = -1 then
ATolerance := DELTA;
// Define area of +/-ATolerance pixels around the screen point
rArea.TopLeft := ScreenToLatLon(Point(X-ATolerance, Y-ATolerance));
rArea.BottomRight := ScreenToLatLon(Point(X+ATolerance, Y+ATolerance));
// Collect Objects in this are
for J := 0 to 9 do
begin
gpsList := FGPSItems[J].GetObjectsInArea(rArea);
try
SetLength(Result, gpsList.Count);
for i := 0 to gpsList.Count-1 do
if gpsList[i] is TGPSPoint then
Result[i] := gpsList[i];
finally
gpsList.Free;
end;
end;
end;
procedure TMapView.CenterOnObj(obj: TGPSObj);
var
Area: TRealArea;
Pt: TRealPoint;
begin
obj.GetArea(Area);
Pt.Lon := (Area.TopLeft.Lon + Area.BottomRight.Lon) /2;
Pt.Lat := (Area.TopLeft.Lat + Area.BottomRight.Lat) /2;
Center := Pt;
end;
procedure TMapView.ZoomOnObj(obj: TGPSObj);
var
Area: TRealArea;
begin
obj.GetArea(Area);
Engine.ZoomOnArea(Area);
end;
procedure TMapView.ZoomOnArea(const aArea: TRealArea);
begin
Engine.ZoomOnArea(aArea);
end;
procedure TMapView.Redraw;
begin
Invalidate;
end;
function TMapView.GetVisibleArea: TRealArea;
var
mapWidth: Int64;
begin
Result.TopLeft := Engine.ScreenToLatLon(Point(0, 0));
Result.BottomRight := Engine.ScreenToLatLon(Point(Width, Height));
if Cyclic then
begin
mapWidth := ZoomFactor(Engine.Zoom) * TILE_SIZE;
if Width >= mapWidth then
begin
Result.TopLeft.Lon := -180;
Result.BottomRight.Lon := 180;
end;
end;
end;
procedure TMapView.ClearBuffer;
begin
DrawingEngine.CreateBuffer(ClientWidth, ClientHeight); // ???
end;
procedure TMapView.UpdateFont(Sender: TObject);
begin
if SameText(FFont.Name, 'default') then
DrawingEngine.FontName := Screen.SystemFont.Name
else
DrawingEngine.FontName := FFont.Name;
if FFont.Size = 0 then
DrawingEngine.FontSize := Screen.SystemFont.Size
else
DrawingEngine.FontSize := FFont.Size;
DrawingEngine.FontStyle := FFont.Style;
DrawingEngine.FontColor := ColorToRGB(FFont.Color);
Engine.Redraw;
end;
procedure TMapView.UpdateImage(Sender: TObject);
begin
Engine.Redraw;
end;
function TMapView.CreateLayers: TMapLayers;
begin
Result := TMapLayers.Create(Self, BASE_Z_LAYER);
end;
procedure TMapView.UpdateLayers;
var
I: Integer;
begin
for I := 0 to Pred(FLayers.Count) do
FLayers[I].ComboLayer.TileLayer.ParentViewChanged;
end;
procedure TMapView.ChangeCachePath(AOldLoc: TCacheLocation; ANewPath: String);
var
OldPath: String;
begin
OldPath := Engine.CachePath;
FCacheFullPath := ANewPath;
//ForceDirectories(ANewPath);
Engine.CachePath := ANewPath;
UpdateLayers;
if AOldLoc = clTemp then
DeleteDirectory(OldPath, False);
end;
class function TMapView.CacheDirectory(ALoc: TCacheLocation;
ACustomPath: String): String;
const
LazMVCacheFolder: String = '.lazmapcache/';
begin
case ALoc of
clProfile: Result := Concat(GetUserDir, LazMVCacheFolder);
clTemp: Result := Concat(GetTempDir(True), LazMVCacheFolder);
otherwise
Result := ACustomPath;
end;
end;
{ TGPSTileLayerBase }
function TGPSTileLayerBase.GetMapProvider: String;
begin
Result := FEngine.MapProvider;
end;
function TGPSTileLayerBase.GetUseThreads: Boolean;
begin
Result := FEngine.UseThreads;
end;
procedure TGPSTileLayerBase.SetParentView(AValue: TMapView);
begin
if (FParentView = AValue) and not FParentViewChanged then
Exit;
FEngine.Active := False;
FParentView := AValue;
if not Assigned(FParentView) then
Exit;
FEngine.DownloadEngine := FParentView.DownloadEngine;
FEngine.CacheItemClass := FParentView.Engine.CacheItemClass;
FEngine.CachePath := FParentView.Engine.CachePath;
FEngine.CacheOnDisk := FParentView.Engine.CacheOnDisk;
FEngine.CacheMaxAge := FParentView.Engine.CacheMaxAge;
FEngine.Cyclic := FParentView.Engine.Cyclic;
FEngine.CopyMapWindowFrom(FParentView.Engine);
FEngine.Active := True;
end;
procedure TGPSTileLayerBase.DoTileDownloaded(const TileId: TTileId);
begin
TileDownLoaded(TileId);
if Assigned(FParentView) then
FParentView.Redraw;
end;
procedure TGPSTileLayerBase.DoDrawTile(const TileId: TTileId; X, Y: Integer;
TileImg: TPictureCacheItem);
begin
DrawTile(TileId, X, Y, TileImg);
end;
procedure TGPSTileLayerBase.SetDrawMode(AValue: TItemDrawMode);
begin
if FDrawMode = AValue then Exit;
FDrawMode := AValue;
if Assigned(FParentView) then
FParentView.Redraw;
end;
procedure TGPSTileLayerBase.TileDownloaded(const TileId: TTileId);
begin
; // Intentionally empty
end;
procedure TGPSTileLayerBase.SetMapProvider(AValue: String);
begin
if AValue = FMapProvider then
Exit;
FMapProvider := AValue;
FEngine.MapProvider := FMapProvider;
end;
procedure TGPSTileLayerBase.SetOpacity(AValue: Single);
begin
if FOpacity = AValue then Exit;
FOpacity := AValue;
if Assigned(FParentView) then
FParentView.Redraw;
end;
procedure TGPSTileLayerBase.SetUseThreads(AValue: Boolean);
begin
FEngine.UseThreads := AValue;
end;
constructor TGPSTileLayerBase.Create;
begin
inherited;
FEngine := TMapViewerEngine.Create(Nil);
FMapProvider := FEngine.MapProvider;
FEngine.OnTileDownloaded := @DoTileDownloaded;
FEngine.OnDrawTile := @DoDrawTile;
end;
destructor TGPSTileLayerBase.Destroy;
begin
FEngine.Free;
inherited Destroy;
end;
procedure TGPSTileLayerBase.GetArea(out Area: TRealArea);
begin
if Assigned(FParentView)
then Area := FParentView.GetVisibleArea // Always over visible area
else Area.Init(-180.0, 90.0, 180.0, -90.0); // Worldwide
end;
procedure TGPSTileLayerBase.Draw(AView: TObject; Area: TRealArea);
begin
SetParentView(AView as TMapView);
FEngine.CopyMapWindowFrom(FParentView.Engine);
end;
procedure TGPSTileLayerBase.ParentViewChanged;
begin
FParentViewChanged := True;
end;
{ TGPSTileLayerLabels }
procedure TGPSTileLayerLabels.DrawTile(const TileId: TTileId; X, Y: Integer;
TileImg: TPictureCacheItem);
begin
; // Intentionally blank
end;
procedure TGPSTileLayerLabels.Draw(AView: TObject; Area: TRealArea);
var
V: TMapView;
PtTL, PtBR, Pt0, Pt: TPoint;
X, Y, Z, W, H: Integer;
S: String;
extent: TSize;
begin
inherited Draw(AView, Area);
V := FParentView;
PtTL := V.Engine.LatLonToWorldScreen(Area.TopLeft);
PtBR := V.Engine.LatLonToWorldScreen(Area.BottomRight);
X := -PtTL.X div TILE_SIZE;
Y := -PtTL.Y div TILE_SIZE;
Pt0 := Point(V.Engine.MapLeft + X * TILE_SIZE,
V.Engine.MapTop + Y * TILE_SIZE);
Pt := Pt0;
H := Y + (PtBR.Y - PtTL.Y) div TILE_SIZE;
while Y <= H do
begin
X := -PtTL.X div TILE_SIZE;
W := X + (PtBR.X - PtTL.X) div TILE_SIZE;
while X <= W do
begin
Z := V.Zoom;
V.DrawingEngine.BrushStyle := bsSolid;
V.DrawingEngine.BrushColor := clCream;
S := Format(' %d-%d-%d ', [X, Y, Z]);
extent := V.DrawingEngine.TextExtent(S);
V.DrawingEngine.TextOut(Pt.X + (TILE_SIZE - extent.CX) div 2,
Pt.Y + (TILE_SIZE - extent.CY) div 2, S);
Inc(Pt.X, TILE_SIZE);
Inc(X);
end;
Pt.X := Pt0.X;
Inc(Pt.Y, TILE_SIZE);
Inc(Y);
end;
end;
{ TGPSTileLayer }
procedure TGPSTileLayer.DrawTile(const TileId: TTileId; X, Y: Integer;
TileImg: TPictureCacheItem);
begin
if not Assigned(FParentView) or not Assigned(TileImg) then
Exit;
FParentView.DrawingEngine.DrawCacheItem(X, Y, TileImg, FDrawMode, FOpacity);
end;
procedure TGPSTileLayer.Draw(AView: TObject; Area: TRealArea);
begin
inherited Draw(AView, Area);
FEngine.Redraw;
end;
procedure TGPSTileLayer.TileDownloaded(const TileId: TTileId);
begin
FEngine.Redraw;
end;
end.