LazMapViewer: Fixed DrawPointOfInterest, DrawTrack issues with opacity. Added interactive TMapView point editor into IDE designer (double-click view to show)

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@9385 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
alpine-a110 2024-07-16 16:44:07 +00:00
parent b026ed55c2
commit df4eeaaf58
5 changed files with 1806 additions and 31 deletions

View File

@ -3,27 +3,20 @@ inherited LayersPropertyEditForm: TLayersPropertyEditForm
Height = 343
Top = 250
Width = 320
Caption = 'LayersPropertyEditForm'
Caption = 'MapView Layers'
ClientHeight = 343
ClientWidth = 320
inherited ToolBar1: TToolBar
Height = 52
Width = 320
inherited DividerToolButton: TToolButton
Height = 50
end
inherited MoveUpButton: TToolButton
Left = 116
end
inherited MoveDownButton: TToolButton
Left = 171
Height = 40
end
end
inherited CollectionListBox: TListBox
Height = 244
Top = 52
Height = 254
Width = 320
OnClick = CollectionListBoxClick
TopIndex = -1
end
object Panel1: TPanel[2]
Left = 0

View File

@ -29,7 +29,7 @@ interface
uses
Classes, SysUtils, Controls, GraphType, Graphics, FPImage, IntfGraphics,
Forms, ImgList, LCLVersion, fgl,
MvTypes, MvGeoMath, MvGPSObj, MvCache, MvExtraData, MvEngine, MvMapProvider,
MvTypes, MvGeoMath, MvGPSObj, mvDragObj, MvCache, MvExtraData, MvEngine, MvMapProvider,
MvDownloadEngine, MvDrawingEngine;
Type
@ -39,6 +39,7 @@ Type
TMapViewOption =
(
mvoEditorEnabled, // Point/Track editor enabled
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
@ -65,6 +66,7 @@ type
TMapAreas = class;
TGPSTileLayer = class;
TGPSComboLayer = class;
TMapEditMark = class;
TPointOfInterestDrawEvent = procedure(Sender: TObject;
ADrawer: TMvCustomDrawingEngine; APoint: TPointOfInterest) of object;
@ -104,7 +106,7 @@ type
function HitTest(constref Area: TRealArea): TMapObjectList; virtual; abstract;
property View: TMapView read GetView;
property Layer: TMapLayer read GetLayer;
PROPERTY GPSObj: TGPSObj read GetGPSObj;
property GPSObj: TGPSObj read GetGPSObj;
published
property Caption: TCaption read FCaption write SetCaption;
property Visible: Boolean read FVisible write SetVisible default True;
@ -419,6 +421,11 @@ type
end;
// Additional (ooCustom) map observer operations
TMapObserverCustomOperation = (mooSelectionCompleted, mooStartDrag, mooDrag,
mooEndDrag, mooIsDirty);
PMapObserverCustomOperation = ^TMapObserverCustomOperation;
{ TMapView }
TMapView = class(TCustomControl)
@ -428,6 +435,11 @@ type
FCenter: TMapCenter;
FDownloadEngine: TMvCustomDownloadEngine;
FBuiltinDownloadEngine: TMvCustomDownloadEngine;
FOnEditDrag: TNotifyEvent;
FOnEditEndDrag: TNotifyEvent;
FOnEditIsDirty: TNotifyEvent;
FOnEditSelectionCompleted: TNotifyEvent;
FOnEditStartDrag: TNotifyEvent;
FEngine: TMapViewerEngine;
FBuiltinDrawingEngine: TMvCustomDrawingEngine;
FDrawingEngine: TMvCustomDrawingEngine;
@ -450,6 +462,8 @@ type
FZoomMin: Integer;
FBeforeDrawObjectsEvent: TNotifyEvent;
FAfterDrawObjectsEvent: TNotifyEvent;
FEditMark: TMapEditMark;
FDragger: TDragObj;
procedure CallAsyncInvalidate;
procedure DoAsyncInvalidate({%H-}Data: PtrInt);
procedure DrawObjects(const {%H-}TileId: TTileId; aLeft, aTop, aRight,aBottom: integer);
@ -521,7 +535,7 @@ type
function DoMouseWheel(Shift: TShiftState; WheelDelta: Integer;
MousePos: TPoint): Boolean; override;
procedure DoOnResize; override;
function IsActive: Boolean;
function IsActive: Boolean; inline;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
@ -535,6 +549,18 @@ type
function CreateLayers: TMapLayers; virtual;
procedure UpdateLayers;
procedure CreateEditor;
procedure DoneEditor;
function EditingEnabled: Boolean; inline;
function DraggingEnabled: Boolean; inline;
procedure DoEditSelectionCompleted(Sender: TObject);
procedure DoEditStartDrag(Sender: TObject);
procedure DoEditDrag(Sender: TObject);
procedure DoEditEndDrag(Sender: TObject);
procedure DoEditIsDirty(Sender: TObject);
procedure ChangeCachePath(AOldLoc: TCacheLocation; ANewPath: String);
class function CacheDirectory(ALoc: TCacheLocation; ACustomPath: String): String;
@ -576,6 +602,7 @@ type
property Engine: TMapViewerEngine read FEngine;
property GPSItems: TGPSObjectList read GetGPSItems;
property GPSLayer[L: Integer]: TGPSObjectList read GetGPSLayer;
property EditMark: TMapEditMark read FEditMark;
published
property Active: boolean read FActive write SetActive default false;
property Align;
@ -620,6 +647,11 @@ type
property OnMouseLeave;
property OnMouseMove;
property OnMouseUp;
property OnEditSelectionCompleted: TNotifyEvent read FOnEditSelectionCompleted write FOnEditSelectionCompleted;
property OnEditStartDrag: TNotifyEvent read FOnEditStartDrag write FOnEditStartDrag;
property OnEditDrag: TNotifyEvent read FOnEditDrag write FOnEditDrag;
property OnEditEndDrag: TNotifyEvent read FOnEditEndDrag write FOnEditEndDrag;
property OnEditIsDirty: TNotifyEvent read FOnEditIsDirty write FOnEditIsDirty;
end;
{ TGPSTileLayerBase }
@ -690,6 +722,115 @@ type
property TileLayer: TGPSTileLayer read FTileLayer;
end;
{ TMapEditMark }
TMapEditMark = class(TGPSObj)
private
FMapView: TMapView;
FOnDirty: TNotifyEvent;
FOnDrag: TNotifyEvent;
FOnEndDrag: TNotifyEvent;
FOnSelectionCompleted: TNotifyEvent;
FOnStartDrag: TNotifyEvent;
FRealPt: TRealPoint;
FPt: TPoint;
FSelection: TMapObjectList;
FLit: TMapObjectList;
FOrigins: TRealPointArray;
FDragStarted: Boolean;
FMarquee: Boolean;
FMarqueeRect: TRect;
FDirty: Boolean;
FTruncSelection: Boolean;
FClearSelection: Boolean;
function GetCurrentArea: TMapArea;
function GetCurrentPoint: TMapPoint;
function GetCurrentTrack: TMapTrack;
function GetCursorShape: TCursor;
function GetHasSelection: Boolean;
function AroundPt(X, Y: Integer; APt: TPoint): Boolean;
procedure SelectFromMarquee;
procedure MarkDirty;
public
constructor Create(AMapView: TMapView);
destructor Destroy; override;
procedure GetArea(out Area: TRealArea); override;
procedure Draw({%H-}AView: TObject; {%H-}Area: TRealArea); override;
procedure UpdateFrom(AObjs: TMapObjectList); virtual;
function ClickableAt({%H-}X, {%H-}Y: Integer): Boolean;
function ClickAt(X, Y: Integer) : Boolean;
procedure ClearSelection;
procedure CompleteSelection;
procedure Select(APoint: TMapPoint; ClearFirst: Boolean = False);
function IsSelected(AObj: TObject): Boolean;
procedure DoStartDrag(Sender: TDragObj);
procedure DoDrag(Sender: TDragObj);
procedure DoEndDrag(Sender: TDragObj);
property Lon: Double read FRealPt.Lon write FRealPt.Lon;
property Lat: Double read FRealPt.Lat write FRealPt.Lat;
property RealPt: TRealPoint read FRealPt write FRealPt;
property CursorShape: TCursor read GetCursorShape;
property HasSelection: Boolean read GetHasSelection;
property CurrentPoint: TMapPoint read GetCurrentPoint;
property CurrentTrack: TMapTrack read GetCurrentTrack;
property CurrentArea: TMapArea read GetCurrentArea;
property Selection: TMapObjectList read FSelection;
property Dirty: Boolean read FDirty write FDirty;
property OnStartDrag: TNotifyEvent read FOnStartDrag write FOnStartDrag;
property OnDrag: TNotifyEvent read FOnDrag write FOnDrag;
property OnEndDrag: TNotifyEvent read FOnEndDrag write FOnEndDrag;
property OnDirty: TNotifyEvent read FOnDirty write FOnDirty;
property OnSelectionCompleted: TNotifyEvent read FOnSelectionCompleted write FOnSelectionCompleted;
end;
{ TMapEditorListFilterEnumerator }
generic TMapEditorListFilterEnumerator<T: class> = class
private
type
TItemClass = T;
private
FList: TMapObjectList;
FCurrent: TItemClass;
FIndex: Integer;
public
constructor Create(AList: TMapObjectList);
function MoveNext: Boolean;
property Current: TItemClass read FCurrent;
function GetEnumerator: TMapEditorListFilterEnumerator;
function Skip(ANum: Integer = 1): TMapEditorListFilterEnumerator;
end;
TMapEditorPointsFilterEnumerator = specialize TMapEditorListFilterEnumerator<TMapPoint>;
TMapEditorTracksFilterEnumerator = specialize TMapEditorListFilterEnumerator<TMapTrack>;
TMapEditorAreasFilterEnumerator = specialize TMapEditorListFilterEnumerator<TMapArea>;
{ TMapEditorList }
TMapEditorList = class helper for TMapObjectList
private
function GetAreasOnly: TMapEditorAreasFilterEnumerator;
function GetPointsOnly: TMapEditorPointsFilterEnumerator;
function GetTracksOnly: TMapEditorTracksFilterEnumerator;
public
function IndexOfObj(const Item: TObject; out Index: Integer): Boolean;
function DelIfPresent(const Item: TObject): Boolean;
function AddIfNotPresent(const Item: TObject): Boolean; inline;
property Points: TMapEditorPointsFilterEnumerator read GetPointsOnly;
property Tracks: TMapEditorTracksFilterEnumerator read GetTracksOnly;
property Areas: TMapEditorAreasFilterEnumerator read GetAreasOnly;
end;
implementation
uses
@ -708,6 +849,7 @@ const
BASE_Z_POI = BASE_Z_TRACK + RANGE_Z;
_TILELAYERS_ID_ = -42; // OwnerIDs of the tile layers
_MAPEDITOR_ID_ = -42;
{ Converts a length given in millimeters to screen pixels }
function mmToPx(AValue: Double): Integer;
@ -881,6 +1023,9 @@ end;
function TMapArea.HitTest(constref Area: TRealArea): TMapObjectList;
begin
Result := Nil;
if not Visible then
Exit;
Result := Points.HitTest(Area);
if Assigned(Result) then
Result.Add(Self);
@ -1177,6 +1322,9 @@ end;
function TMapTrack.HitTest(constref Area: TRealArea): TMapObjectList;
begin
Result := Nil;
if not Visible then
Exit;
Result := Points.HitTest(Area);
if Assigned(Result) then
Result.Add(Self);
@ -1692,6 +1840,9 @@ end;
function TMapLayer.HitTest(constref Area: TRealArea): TMapObjectList;
begin
Result := Nil;
if not Visible then
Exit;
Result := PointsOfInterest.HitTest(Area);
Result := TMapObjectList.AddListToResult(Tracks.HitTest(Area), Result);
Result := TMapObjectList.AddListToResult(Areas.HitTest(Area), Result);
@ -2243,18 +2394,33 @@ 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
if EditingEnabled then
begin
if (Button = mbLeft) and FEditMark.ClickableAt(X, Y) then
begin
Engine.MouseDown(self,Button,Shift,X,Y);
Invalidate;
end;
FEditMark.ClickAt(X, Y);
FDragger.MouseDown(FEditMark, X, Y);
end
// With editor enabled, dragging is with the middle button
else if (Button = mbMiddle) and DraggingEnabled then
StartDragging(X, Y);
end
else
// With editor disabled, dragging is with the left button
if IsActive and DraggingEnabled 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
@ -2262,17 +2428,54 @@ begin
Engine.Redraw;
Invalidate;
end;
if EditingEnabled then
begin
FDragger.MouseUp(X, Y);
FEditMark.CompleteSelection;
if (Button = mbMiddle)
then EndDragging(X, Y)
else AbortDragging;
end;
end;
procedure TMapView.MouseMove(Shift: TShiftState; X, Y: Integer);
procedure EditorMM;
const
DELTA = 5;
var
A: TRealArea;
Hits: TMapObjectList;
begin
A.TopLeft := ScreenToLatLon(Point(X - DELTA, Y - DELTA));
A.BottomRight := ScreenToLatLon(Point(X + DELTA, Y + DELTA));
FDragger.MouseMove(X, Y);
// Update hits
if not FDragger.InDrag then
begin
Hits := Layers.HitTest(A);
try
FEditMark.UpdateFrom(Hits);
Screen.Cursor := FEditMark.CursorShape;
finally
Hits.Free;
end;
end;
end;
begin
inherited MouseMove(Shift, X, Y);
if IsActive then
begin
Engine.MouseMove(self,Shift,X,Y);
if Engine.InDrag
then Invalidate;
end;
if EditingEnabled then
EditorMM
end;
procedure TMapView.Notification(AComponent: TComponent; Operation: TOperation);
@ -2451,6 +2654,7 @@ var
trkColor, connColor: TColor;
trkWidth, connWidth: Integer;
OldOpacity: Single;
OldPenStyle: TPenStyle;
procedure ClipDrawLine(P1, P2: TPoint); inline;
begin
@ -2480,11 +2684,13 @@ begin
end;
OldOpacity := DrawingEngine.Opacity;
OldPenStyle := DrawingEngine.PenStyle;
try
DrawingEngine.Opacity := trk.Opacity;
DrawingEngine.PenColor := trkColor;
DrawingEngine.PenWidth := trkWidth;
DrawingEngine.PenStyle := psSolid;
// Clipping rectangle
if Cyclic then
@ -2544,6 +2750,7 @@ begin
EndSegm := TSegmentExtraData.MarkOf(trk.Points[I].ExtraData) = smEnd;
end;
finally
DrawingEngine.PenStyle := OldPenStyle;
DrawingEngine.Opacity := OldOpacity;
end;
end;
@ -2609,6 +2816,8 @@ var
s: String;
bmp: TBitmap;
w, h: Integer;
OldOpacity: Single;
OldPenStyle: TPenStyle;
procedure DrawOne(pt: TPoint);
begin
@ -2639,8 +2848,13 @@ var
begin
pt := Engine.LatLonToScreen(APt.RealPoint);
OldOpacity := DrawingEngine.Opacity;
OldPenStyle := DrawingEngine.PenStyle;
bmp := Nil;
try
DrawingEngine.Opacity := 1.0;
DrawingEngine.PenStyle := psSolid;
// Draw point as symbol from image list ...
if Assigned(FPOIImages) and (APt.ImageIndex <> -1) and (APt.ImageIndex < FPOIImages.Count) then
begin
@ -2670,6 +2884,8 @@ begin
DrawOne(pt);
finally
bmp.Free;
DrawingEngine.Opacity := OldOpacity;
DrawingEngine.PenStyle := OldPenStyle;
end;
end;
@ -2840,6 +3056,7 @@ var
I: Integer;
begin
inherited Create(AOwner);
Width := 150;
Height := 150;
@ -2902,6 +3119,9 @@ begin
FZoomMin := 1;
FZoomMax := 19;
Zoom := 1;
CreateEditor;
end;
destructor TMapView.Destroy;
@ -2909,6 +3129,7 @@ var
I: Integer;
begin
Active := False;
DoneEditor;
Engine.Jobqueue.RemoveAsyncCalls(Self);
FFont.Free;
FreeAndNil(FPOIImage);
@ -3202,6 +3423,91 @@ begin
FLayers[I].ComboLayer.TileLayer.ParentViewChanged;
end;
procedure TMapView.CreateEditor;
begin
if Assigned(FEditMark) then
DoneEditor;
FEditMark := TMapEditMark.Create(Self);
FEditMark.UpdateFrom(Nil);
FGPSItems[High(FGPSItems)].Add(FEditMark, _MAPEDITOR_ID_, MaxInt);
FEditMark.Visible := True;
FEditMark.OnSelectionCompleted := @DoEditSelectionCompleted;
FEditMark.OnStartDrag := @DoEditStartDrag;
FEditMark.OnDrag := @DoEditDrag;
FEditMark.OnEndDrag := @DoEditEndDrag;
FEditMark.OnDirty := @DoEditIsDirty;
FDragger := TDragObj.Create;
FDragger.OnDrag := @FEditMark.DoDrag;
FDragger.OnEndDrag := @FEditMark.DoEndDrag;
end;
procedure TMapView.DoneEditor;
begin
if not Assigned(FEditMark) then
Exit;
FDragger.Free;
FGPSItems[High(FGPSItems)].Delete(FEditMark);
FEditMark := Nil;
end;
function TMapView.EditingEnabled: Boolean;
begin
Result := IsActive and (mvoEditorEnabled in Options);
end;
function TMapView.DraggingEnabled: Boolean;
begin
Result := IsActive and (mvoMouseDragging in FOptions);
end;
procedure TMapView.DoEditSelectionCompleted(Sender: TObject);
var
What: TMapObserverCustomOperation = mooSelectionCompleted;
begin
FPONotifyObservers(Self, ooCustom, @What);
if Assigned(FOnEditSelectionCompleted) then
FOnEditSelectionCompleted(Self);
end;
procedure TMapView.DoEditStartDrag(Sender: TObject);
var
What: TMapObserverCustomOperation = mooStartDrag;
begin
FPONotifyObservers(Self, ooCustom, @What);
if Assigned(FOnEditStartDrag) then
FOnEditStartDrag(Self);
end;
procedure TMapView.DoEditDrag(Sender: TObject);
var
What: TMapObserverCustomOperation = mooDrag;
begin
FPONotifyObservers(Self, ooCustom, @What);
if Assigned(FOnEditDrag) then
FOnEditDrag(Self);
end;
procedure TMapView.DoEditEndDrag(Sender: TObject);
var
What: TMapObserverCustomOperation = mooEndDrag;
begin
FPONotifyObservers(Self, ooCustom, @What);
if Assigned(FOnEditEndDrag) then
FOnEditEndDrag(Self);
end;
procedure TMapView.DoEditIsDirty(Sender: TObject);
var
What: TMapObserverCustomOperation = mooIsDirty;
begin
FPONotifyObservers(Self, ooCustom, @What);
if Assigned(FOnEditIsDirty) then
FOnEditIsDirty(Self);
end;
procedure TMapView.ChangeCachePath(AOldLoc: TCacheLocation; ANewPath: String);
var
OldPath: String;
@ -3407,5 +3713,410 @@ begin
FEngine.Redraw;
end;
{ TMapEditorListFilterEnumerator }
constructor TMapEditorListFilterEnumerator.Create(AList: TMapObjectList);
begin
FList := AList;
FIndex := -1;
end;
function TMapEditorListFilterEnumerator.MoveNext: Boolean;
begin
repeat Inc(FIndex);
until (FIndex >= FList.Count) or (FList[FIndex] is TItemClass);
Result := FIndex < FList.Count;
if Result then
FCurrent := TItemClass(FList[FIndex]);
end;
function TMapEditorListFilterEnumerator.GetEnumerator: TMapEditorListFilterEnumerator;
begin
Result := Self;
end;
function TMapEditorListFilterEnumerator.Skip(ANum: Integer
): TMapEditorListFilterEnumerator;
var
I: Integer;
begin
for I := 1 to ANum do
MoveNext;
Result := Self;
end;
{ TMapEditMark }
function TMapEditMark.GetCursorShape: TCursor;
begin
if Assigned(FLit)
then Result := crHandPoint
else Result := crDefault;
end;
function TMapEditMark.GetCurrentPoint: TMapPoint;
begin
if HasSelection
then Result := TMapPoint(FSelection[0])
else Result := Nil;
end;
function TMapEditMark.GetCurrentArea: TMapArea;
var
A: TMapArea;
Col: TCollection = Nil;
begin
Result := Nil;
if Assigned(GetCurrentPoint) then
Col := GetCurrentPoint.Collection;
for A in FSelection.Areas do
if Col = A.Points then
Exit(A);
end;
function TMapEditMark.GetCurrentTrack: TMapTrack;
var
T: TMapTrack;
Col: TCollection = Nil;
begin
Result := Nil;
if Assigned(GetCurrentPoint) then
Col := GetCurrentPoint.Collection;
for T in FSelection.Tracks do
if Col = T.Points then
Exit(T);
end;
function TMapEditMark.GetHasSelection: Boolean;
begin
Result := (FSelection.Count > 0);
end;
procedure TMapEditMark.CompleteSelection;
var
P: TMapPoint;
T: TObject;
begin
if FClearSelection then
begin
FSelection.Clear;
FClearSelection := False;
FTruncSelection := False;
end
else if FTruncSelection and (FSelection.Count > 1) then
begin
// Find the container track/area for the first point
T := CurrentTrack;
if T = Nil then
T := CurrentArea;
// Delete everything except the first point
FSelection.DeleteRange(1, FSelection.Count - 1);
// Re-insert the container track/area
if Assigned(T) then
FSelection.Add(T);
FTruncSelection := False;
end;
if Assigned(FOnSelectionCompleted) then
FOnSelectionCompleted(Self);
end;
procedure TMapEditMark.Select(APoint: TMapPoint; ClearFirst: Boolean);
begin
if ClearFirst then
FSelection.Clear;
FSelection.Insert(0, APoint);
FMapView.Invalidate;
end;
function TMapEditMark.IsSelected(AObj: TObject): Boolean;
begin
Result := FSelection.IndexOf(AObj) >= 0;
end;
function TMapEditMark.AroundPt(X, Y: Integer; APt: TPoint): Boolean;
begin
Result := InRange(X, APt.X - 5, APt.X + 5) and
InRange(Y, APt.Y - 5, APt.Y + 5);
end;
procedure TMapEditMark.SelectFromMarquee;
var
Hits: TMapObjectList;
RA: TRealArea;
O: TObject;
begin
RA.TopLeft := FMapView.ScreenToLatLon(FMarqueeRect.TopLeft);
RA.BottomRight := FMapView.ScreenToLatLon(FMarqueeRect.BottomRight);
Hits := FMapView.Layers.HitTest(RA);
if Assigned(Hits) then
try
for O in Hits do
FSelection.AddIfNotPresent(O);
FMapView.Invalidate;
finally
Hits.Free;
end;
end;
procedure TMapEditMark.MarkDirty;
begin
if not FDirty then
begin
FDirty := True;
if Assigned(FOnDirty) then
FOnDirty(Self);
end;
end;
constructor TMapEditMark.Create(AMapView: TMapView);
begin
FMapView := AMapView;
FSelection := TMapObjectList.Create;
end;
destructor TMapEditMark.Destroy;
begin
FSelection.Free;
inherited Destroy;
end;
procedure TMapEditMark.GetArea(out Area: TRealArea);
begin
Area.Init(FRealPt, FRealPt);
end;
procedure TMapEditMark.Draw(AView: TObject; Area: TRealArea);
var
View: TMapView;
DE: TMvCustomDrawingEngine;
Trk: TMapTrack;
Ar: TMapArea;
TrkPoint: TMapPoint;
I: Integer;
procedure MarkMP(P: TMapPoint);
begin
with View.LatLonToScreen(P.Latitude, P.Longitude) do
DE.Ellipse(X - 4, Y - 4, X + 4, Y + 4);
end;
begin
View := TMapView(AView);
DE := View.DrawingEngine;
FPt := View.LatLonToScreen(RealPt);
DE.PenStyle := psSolid;
DE.PenColor := clRed;
DE.PenWidth := 3;
DE.BrushColor := clGray;
DE.BrushStyle := bsSolid;
if Assigned(FLit) then
DE.Rectangle(FPt.X - 5, FPt.Y - 5, FPt.X + 5, FPt.Y + 5);
if HasSelection then
begin
DE.PenWidth := 2;
DE.BrushStyle := bsClear;
for Trk in FSelection.Tracks do
for I := 0 to Pred(Trk.Points.Count) do
MarkMP(Trk.Points[I]);
for Ar in FSelection.Areas do
for I := 0 to Pred(Ar.Points.Count) do
MarkMP(Ar.Points[I]);
DE.PenWidth := 2;
DE.BrushColor := clBlack;
DE.BrushStyle := bsSolid;
for TrkPoint in FSelection.Points.Skip(1) do
with View.LatLonToScreen(TrkPoint.Latitude, TrkPoint.Longitude) do
DE.Rectangle(X - 5, Y - 5, X + 5, Y + 5);
DE.BrushColor := clLime;
TrkPoint := (FSelection[0] as TMapPoint);
with View.LatLonToScreen(TrkPoint.Latitude, TrkPoint.Longitude) do
DE.Rectangle(X - 5, Y - 5, X + 5, Y + 5);
end;
if FMarquee then
begin
DE.PenStyle := psSolid;
DE.PenWidth := 1;
DE.PenColor := clGray;
DE.BrushStyle := bsClear;
with FMarqueeRect do
DE.Rectangle(Left, Top, Right, Bottom);
end;
end;
procedure TMapEditMark.UpdateFrom(AObjs: TMapObjectList);
var
I: Integer;
begin
if Assigned(AObjs) and (AObjs.Count > 0) and (AObjs[0] is TMapPoint) then
begin
// Same point?
if Assigned(FLit) and (AObjs[0] = FLit[0]) then
Exit;
Lat := TMapPoint(AObjs[0]).Latitude;
Lon := TMapPoint(AObjs[0]).Longitude;
FLit.Free;
FLit := TMapObjectList.Create(AObjs);
end
else
FreeAndNil(FLit);
FMapView.Invalidate;
end;
function TMapEditMark.ClickableAt(X, Y: Integer): Boolean;
begin
Result := True; //Visible;
end;
function TMapEditMark.ClickAt(X, Y: Integer): Boolean;
var
O: TObject;
H: Boolean;
begin
Result := True;
if Assigned(FLit) and AroundPt(X, Y, FPt) then
begin
FTruncSelection := not (ssCtrl in GetKeyShiftState);
H := FSelection.DelIfPresent(FLit[0]);
FTruncSelection := not H and FTruncSelection;
FSelection.Insert(0, FLit[0]);
for O in FLit do
FSelection.AddIfNotPresent(O);
FreeAndNil(FLit);
end
else
FClearSelection := not (ssCtrl in GetKeyShiftState);
end;
procedure TMapEditMark.ClearSelection;
begin
FClearSelection := True;
CompleteSelection;
end;
procedure TMapEditMark.DoStartDrag(Sender: TDragObj);
var
I: Integer = 0;
P: TMapPoint;
begin
FLit := Nil;
CompleteSelection;
SetLength(FOrigins, FSelection.Count);
for P in FSelection.Points do
begin
FOrigins[I] := RealPoint(P.Latitude, P.Longitude);
Inc(I);
end;
SetLength(FOrigins, I); // why bother?
FMarquee := not AroundPt(Sender.StartX, Sender.StartY, FPt);
if FMarquee then
begin
FMarqueeRect := Rect(Sender.StartX, Sender.StartY, Sender.EndX, Sender.EndY);
Self.RealPt := FMapView.Center; // keep it in view
FMapView.Invalidate;
end;
FDragStarted := True;
if Assigned(FOnStartDrag) then
FOnStartDrag(Self);
end;
procedure TMapEditMark.DoDrag(Sender: TDragObj);
var
I: Integer = 0;
RPt: TRealPoint;
MapPoint: TMapPoint;
begin
if not FDragStarted then
DoStartDrag(Sender);
if FMarquee then
begin
FMarqueeRect := Rect(Sender.StartX, Sender.StartY, Sender.EndX, Sender.EndY);
FMarqueeRect.NormalizeRect;
FMapView.Invalidate;
Exit;
end;
for MapPoint in FSelection.Points do
begin
MarkDirty;
Rpt := FMapView.ScreenToLatLon(FMapView.LatLonToScreen(FOrigins[I]) +
Point(Sender.OfsX, Sender.OfsY));
MapPoint.Longitude := RPt.Lon;
MapPoint.Latitude := RPt.Lat;
Inc(I);
end;
//FMapView.Invalidate; // No need to
if Assigned(FOnDrag) then
FOnDrag(Self);
end;
procedure TMapEditMark.DoEndDrag(Sender: TDragObj);
begin
if FMarquee then
begin
SelectFromMarquee;
FMarquee := False;
end;
SetLength(FOrigins, 0);
FDragStarted := False;
if Assigned(FOnEndDrag) then
FOnEndDrag(Self);
end;
{ TMapEditorList }
function TMapEditorList.GetAreasOnly: TMapEditorAreasFilterEnumerator;
begin
Result := TMapEditorAreasFilterEnumerator.Create(Self);
end;
function TMapEditorList.GetPointsOnly: TMapEditorPointsFilterEnumerator;
begin
Result := TMapEditorPointsFilterEnumerator.Create(Self);
end;
function TMapEditorList.GetTracksOnly: TMapEditorTracksFilterEnumerator;
begin
Result := TMapEditorTracksFilterEnumerator.Create(Self);
end;
function TMapEditorList.IndexOfObj(const Item: TObject; out Index: Integer
): Boolean;
begin
Index := IndexOf(Item);
Result := Index >= 0;
end;
function TMapEditorList.DelIfPresent(const Item: TObject): Boolean;
var
I: Integer;
begin
Result := False;
while IndexOfObj(Item, I) do
begin
Result := True;
Delete(I);
end;
end;
function TMapEditorList.AddIfNotPresent(const Item: TObject): Boolean;
begin
Result := IndexOf(Item) < 0;
if Result then
Add(Item);
end;
end.

View File

@ -0,0 +1,380 @@
object MapViewerPathEditForm: TMapViewerPathEditForm
Left = 370
Height = 90
Top = 31
Width = 292
BorderStyle = bsToolWindow
Caption = 'MapViewer Path Editor'
ClientHeight = 90
ClientWidth = 292
OnClose = FormClose
OnCreate = FormCreate
OnDestroy = FormDestroy
OnShow = FormShow
object pnlFrame: TPanel
Left = 0
Height = 90
Top = 0
Width = 292
Align = alClient
BevelColor = clRed
ClientHeight = 90
ClientWidth = 292
TabOrder = 0
object pnlTools: TPanel
Left = 1
Height = 33
Top = 1
Width = 290
Align = alTop
ClientHeight = 33
ClientWidth = 290
ParentShowHint = False
ShowHint = True
TabOrder = 0
object btnSelect: TSpeedButton
AnchorSideTop.Control = pnlTools
AnchorSideBottom.Control = pnlTools
AnchorSideBottom.Side = asrBottom
Left = 2
Height = 27
Top = 3
Width = 26
Action = actSelect
AllowAllUp = True
Anchors = [akTop, akLeft, akBottom]
BorderSpacing.Left = 2
BorderSpacing.Top = 2
BorderSpacing.Bottom = 2
Images = ilImages
ImageIndex = 0
ShowCaption = False
end
object btnNewTP: TSpeedButton
AnchorSideLeft.Control = btnSelect
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = pnlTools
AnchorSideBottom.Control = pnlTools
AnchorSideBottom.Side = asrBottom
Left = 28
Height = 27
Top = 3
Width = 26
Action = actNewTP
Anchors = [akTop, akLeft, akBottom]
BorderSpacing.Top = 2
BorderSpacing.Bottom = 2
Images = ilImages
ImageIndex = 1
ShowCaption = False
end
object btnDelTP: TSpeedButton
AnchorSideLeft.Control = btnNewTP
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = pnlTools
AnchorSideBottom.Control = pnlTools
AnchorSideBottom.Side = asrBottom
Left = 54
Height = 27
Top = 3
Width = 26
Action = actDelTP
Anchors = [akTop, akLeft, akBottom]
BorderSpacing.Top = 2
BorderSpacing.Bottom = 2
Images = ilImages
ImageIndex = 2
ShowCaption = False
end
object btnNewPOI: TSpeedButton
AnchorSideLeft.Control = btnDelTP
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = pnlTools
AnchorSideBottom.Control = pnlTools
AnchorSideBottom.Side = asrBottom
Left = 82
Height = 27
Top = 3
Width = 26
Action = actNewPOI
AllowAllUp = True
Anchors = [akTop, akLeft, akBottom]
BorderSpacing.Left = 2
BorderSpacing.Top = 2
BorderSpacing.Bottom = 2
Images = ilImages
ImageIndex = 5
ShowCaption = False
end
object btnNewArea: TSpeedButton
AnchorSideLeft.Control = btnNewPOI
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = pnlTools
AnchorSideBottom.Control = pnlTools
AnchorSideBottom.Side = asrBottom
Left = 108
Height = 27
Top = 3
Width = 26
Action = actNewArea
AllowAllUp = True
Anchors = [akTop, akLeft, akBottom]
BorderSpacing.Top = 2
BorderSpacing.Bottom = 2
Images = ilImages
ImageIndex = 6
ShowCaption = False
end
object btnNewTrack: TSpeedButton
AnchorSideLeft.Control = btnNewArea
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = pnlTools
AnchorSideBottom.Control = pnlTools
AnchorSideBottom.Side = asrBottom
Left = 134
Height = 27
Top = 3
Width = 26
Action = actNewTrack
AllowAllUp = True
Anchors = [akTop, akLeft, akBottom]
BorderSpacing.Top = 2
BorderSpacing.Bottom = 2
Images = ilImages
ImageIndex = 3
ShowCaption = False
end
object btnZoomIn: TSpeedButton
AnchorSideLeft.Control = btnNewTrack
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = pnlTools
AnchorSideBottom.Control = pnlTools
AnchorSideBottom.Side = asrBottom
Left = 162
Height = 27
Top = 3
Width = 26
Action = actZoomIn
Anchors = [akTop, akLeft, akBottom]
BorderSpacing.Left = 2
BorderSpacing.Top = 2
BorderSpacing.Bottom = 2
Images = ilImages
ImageIndex = 7
ShowCaption = False
end
object btnZoomOut: TSpeedButton
AnchorSideLeft.Control = btnZoomIn
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = pnlTools
AnchorSideBottom.Control = pnlTools
AnchorSideBottom.Side = asrBottom
Left = 188
Height = 27
Top = 3
Width = 26
Action = actZoomOut
Anchors = [akTop, akLeft, akBottom]
BorderSpacing.Top = 2
BorderSpacing.Bottom = 2
Images = ilImages
ImageIndex = 8
ShowCaption = False
end
end
object lblSelectedPt: TLabel
AnchorSideLeft.Control = pnlFrame
AnchorSideTop.Side = asrCenter
Left = 1
Height = 16
Top = 39
Width = 82
Alignment = taRightJustify
Anchors = [akTop, akLeft, akRight]
AutoSize = False
BorderSpacing.Right = 5
Caption = 'Selection'
ParentColor = False
end
object lblSelectedLayer: TLabel
AnchorSideLeft.Control = pnlFrame
AnchorSideTop.Side = asrCenter
Left = 1
Height = 16
Top = 63
Width = 82
Alignment = taRightJustify
Anchors = [akTop, akLeft, akRight]
AutoSize = False
BorderSpacing.Right = 5
Caption = 'On Layer'
ParentColor = False
end
object cbSelectedPt: TEdit
AnchorSideTop.Control = pnlTools
AnchorSideTop.Side = asrBottom
AnchorSideRight.Control = pnlFrame
AnchorSideRight.Side = asrBottom
Left = 87
Height = 22
Top = 37
Width = 201
Anchors = [akTop, akRight]
AutoSize = False
BorderSpacing.Around = 3
BorderStyle = bsNone
ParentColor = True
ParentShowHint = False
ReadOnly = True
ShowHint = True
TabOrder = 1
end
object cbSelectedLayer: TEdit
AnchorSideTop.Control = cbSelectedPt
AnchorSideTop.Side = asrBottom
AnchorSideRight.Control = pnlFrame
AnchorSideRight.Side = asrBottom
Left = 87
Height = 22
Top = 62
Width = 201
Anchors = [akTop, akRight]
AutoSize = False
BorderSpacing.Around = 3
BorderStyle = bsNone
ParentColor = True
ParentShowHint = False
ReadOnly = True
ShowHint = True
TabOrder = 2
end
end
object alEditActions: TActionList
Images = ilImages
Left = 120
Top = 40
object actZoomIn: TAction
Caption = 'Zoom In'
Hint = 'Zoom In'
ImageIndex = 7
OnExecute = actZoomInExecute
end
object actZoomOut: TAction
Caption = 'actZoomOut'
Hint = 'Zoom Out'
ImageIndex = 8
OnExecute = actZoomOutExecute
end
object actNewPOI: TAction
AutoCheck = True
Caption = 'New POI'
Hint = 'New POI from current point'
ImageIndex = 5
OnExecute = actNewPOIExecute
end
object actNewTrack: TAction
AutoCheck = True
Caption = 'New Track'
Hint = 'New Track from selected'
ImageIndex = 3
OnExecute = actNewTrackExecute
end
object actNewArea: TAction
AutoCheck = True
Caption = 'New Area'
Hint = 'New Area from selected'
ImageIndex = 6
OnExecute = actNewAreaExecute
end
object actNewTP: TAction
Caption = 'New Track Point'
Hint = 'Insert new point'
ImageIndex = 1
OnExecute = actNewTPExecute
end
object actDelTP: TAction
Caption = 'Del Track Point'
Hint = 'Delete current point'
ImageIndex = 2
OnExecute = actDelTPExecute
end
object actSelect: TAction
AutoCheck = True
Caption = 'Select'
Checked = True
Hint = 'Select tool'
ImageIndex = 0
OnExecute = actSelectExecute
end
end
object ilImages: TImageList
Left = 192
Top = 40
Bitmap = {
4C7A090000001000000010000000E60700000000000078DAED580D6C13F7154F
0BEA87A05D3F0085323588064A23489DB1585BE9A4691B5369629385B8B4DBD4
40B795765AA5314D8CD0A1DA673B1FE2435508495ADA8EB13A6D4D69853A8203
1B29049A94C0285D52C850B77EC449C02626B1E32538F6DBEF5D7CE1E2F8CEE7
6B577553FFD24F77F7FEEFF7BEFEEFDD39C9C810D7358220F4020F67E85BD758
AD56DAB66D5BA8BCBCBC06F753D3E5DB6CB6D8C8C808B95CAE604545C549D8C8
4C974F58B1588C5A5A5A2288C30F1BDF4A972FADF3E7CF13E218B2DBED4FEAE1
F3EAEFEFA7EAEA6AAEC91ED4E6C674F9BC229108EDDDBB7708B19C73381C59E9
F2A5D5DEDE1E713A9D03D0FB7E3A7CAE436767A788FDFBF7137A24867C7EAB85
1F8D4609BA23959595A7366FDE7C480264BBD5F83E9F4F04AFC3870F5F41EEBB
B4D6BFAFAF8F7D8677ECD831C8BD100E87C567F4C21DA9F8ECB3AAAA2A84332F
46CCE7BABABAC4183C1ECF086C6C4FD5FFE00CC1CE4A16E06AAAA9A911630806
8384DA8760FB2615FF61C02297710C1D1D1D140A8544DB2AE79F01FF3724917D
1371F7211FEE1F87D66102EF5AA004D807740317800E602B90A3C6E53E874E23
7002F994E2BA10BE6FC57511B001F002EB393F05DFCF020D4AEF10E433936B2D
41BEE7114C24874A7E8A7CB7DB3D45BAD7C34F76AF75A9C5EFB1151E3F20988E
693CBF4AE080948BDCB646FE54E010CE6F931E3E2FF4EA1CD8B808CCD5C3E77C
13EB9108B57AF05E2ABEC7666A4951875CE08C5AFC4ABD10DF13805AAD7C6001
303DEE97BFA31F4BEF2D8DFC0F8161E06C9C9F39A11E29F2FD5F9FFF3A470FC9
91EEFC32479A19BDFC64F75A97D6F835CE7FB5DB4D53EA1CBD2FD63B7A9E98E0
C7E97DACD6DEF347843F5565FE6F430C0DF158A2B576EF9A316EEF6A7E16E5CE
9E7AB538EAED3DC5D08B483680E781D1F873B8AEDCBB34552EF5F6DE12990D09
A36C5B6B3D6A1D3D2FC8F99CBBE63391E77B15B13A7BCFDA54F35FEFEC7D54C6
1D46BD9EC6F5DF523D785FCEC777FA2ED9AC64D63B7A5F8AEB46107391188FBD
AF10CF57E2F25D09F3CFEF0BFEADF32FCCDE93105DCBB9D73A7B7E3A212E8777
559DC3FB07EE0FA5F96FB217967884C27D4D82A91BEF9D0B408747306F6DB4AE
509D7FF736CB8DD06D044E34D9CCA54D56D3C23F5714DCDA642D58045B1B20F7
C2CE7AA2E4F38FF7FEB31EBBA9A1D9FADDA4BDD958BE7CA6DA371EB27E8FB378
B6D6EFECA4BC0553B8B17AF9F57AF863F19B8E34D94D457A67D763373F8077F5
875C33DD366CA62D880DDF7FCB143D7CAE3DCEFE1062D9A43786B71C857310C3
C503D6A2B9BAF3100AAB904BA55E7E9363C57DE0BFAB977FC06ACE452F9EF90C
BD2000B55AF9FBAD0F2E68B65AA68B7E6DE61AC83E3E6835DFA195CF7D83EB30
7096F9B09799F105AC0FEEEC2FE8CCBAD40D7C0A2C4F97CFBC0FB22E91883BFD
9FC8F7725FA6C2C52E3AB1B881427CBDB7810AB4F2999BDB409488452E2A4AE0
2F6FC90D10A323EBD203E37C17B5C73965B9BB691AFC6FE467C432A9A724DB13
642E1A61598E9BAEE3E7EC46BA5ED473D1B026BE46D9577C6DB5E63E3D163FE7
C43E95CE9ACF1836A6E3FA74E259ABF529F7A542AF3DA8852FD9607F8825C857
3957EA53D106B8F23EFD3CD74F8C74B3C5401B2D86E8C992BC688881FBF61203
9599EFA69BD4B8967BC95862887A2D79314A8E68F74306CA4FCA5D4C77633FC0
7A6B9705E988FB6DF2FFE3151147F734D353A6CB928DC0AA5C5A30896F88FE55
E25E38FB1A85BA774FC0C573AF8A7BAC839C0EC9B9ABF2E81E2946F6CBFA2DAF
6FA0D5F7DD45E7DF3F4503FE8F4459F3AB47AEE68278C77D7F831E97E41C2FEB
AE5E9A4DE60533A8F3641B75BDD72ECA784FD2433D7F2EF1B9B64A7CC69AFBB3
45996F22BF4C56F75F4872AE15EB1E7B63A36883B9C7DFDC38397E03FD4CE217
1B68BE24E73A73AD12EBD7879AAE5D161AE7279E01CEE5A0B4C775665FD2F9F1
BD9C0BDDA6C4F31B8B217A49B977C6B9D0A1ECA43DB484EE471F0C2972790F3A
AA3D6CA065F0319CC8455F5F419D0BB4CCD0CA3C2A86AFD1716E5E345A924769
FD6F1DBE56238E1803DCB57A661975FA1D23E34BBCDE796CAE1BA034609EF0F7
5941E6E32CA7D13EA2C8A7EA800EEB2684708BC8F73712753F47F4C97665F4BA
92C650F58359EB581EEDAAA5D13382323AAA2836F44FC518C25DBB69A06D135D
3EBE5E11C1D35B5463089CA8205FF3AF15E16FD94091CB5D936270AF9CF3C8D1
D22C0A1C13C8E7F9A522FC7FF90D0D9E799E5877C26FED47BE4E117F27F9F73D
41BED74B1511385846AC6BFDCEEDEB24EE9EA2D96680026F3C45BE177FA408FF
9F7E4C83CD55C4BA5C3389FFC20F6750C47B8AFCDB0BC9B7E57B8A08EC5A43AC
FB2BC3CDE3BEB72CFD9A19A0D03E2B5DDE59AA0AD6615DB9EF32C3B4377F9F37
8DB4E2A17937AC4B38FB79C0923470CB7F6B1683C7BF3D6BA8CDE808B71A4F0F
B51A438C709BF16F90D9794F8D1B7ED768196ACB1F843E2503EFC14E8922B7D5
1853E28EDB804EA28DB19827FB7D67C56C11936DE40F0C1E35CC94F862BE497C
29F1C772310AE3B1B7E6BF972E9F6B7AD57F7E3091A304792D65FCC1B4F9A8C1
D5F88DA73F5BFC467BDAF56BCDB7E93DBF309FDFC92533E43DC03DA1A57F1803
3B73BA2F3A164EFAFFD0988DFC01452EFBDD99F351C03E9F0242F6D96436B8AF
B837C26DF9A7F85CC7CE16F7C897631E7466CF0CD8B3DF57B39172469F99372B
20CCFF3BDBE817B2B7EA9A73D8602E3D9373DDFFEBFC2BDBD036FFAA3634CC7F
0A7CE9E75F7BFCC9E73FC519D8F49EDF57F3FFF9CEFF7F002E39D971
}
end
end

View File

@ -0,0 +1,597 @@
unit mvMapViewerPathEditForm;
{$mode ObjFPC}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls,
Buttons, ActnList, PropEdits, mvMapViewer;
type
{ TMapViewerPathEditForm }
TMapViewerPathEditForm = class(TForm, IFPObserver)
actDelTP: TAction;
actNewArea: TAction;
actSelect: TAction;
actNewTP: TAction;
actNewTrack: TAction;
actNewPOI: TAction;
actZoomOut: TAction;
actZoomIn: TAction;
alEditActions: TActionList;
cbSelectedLayer: TEdit;
cbSelectedPt: TEdit;
ilImages: TImageList;
lblSelectedPt: TLabel;
lblSelectedLayer: TLabel;
pnlFrame: TPanel;
pnlTools: TPanel;
btnSelect: TSpeedButton;
btnNewTP: TSpeedButton;
btnDelTP: TSpeedButton;
btnNewPOI: TSpeedButton;
btnNewArea: TSpeedButton;
btnNewTrack: TSpeedButton;
btnZoomIn: TSpeedButton;
btnZoomOut: TSpeedButton;
procedure actDelTPExecute(Sender: TObject);
procedure actNewAreaExecute(Sender: TObject);
procedure actNewPOIExecute(Sender: TObject);
procedure actNewTPExecute(Sender: TObject);
procedure actNewTrackExecute(Sender: TObject);
procedure actSelectExecute(Sender: TObject);
procedure actZoomInExecute(Sender: TObject);
procedure actZoomOutExecute(Sender: TObject);
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormShow(Sender: TObject);
private
FMapLayer: TMapLayer;
FMapView: TMapView;
FInternalSelect: Boolean;
procedure OnModified(Sender: TObject);
procedure OnModifiedWithName(Sender: TObject; PropName: ShortString);
procedure OnRefreshPropertyValues;
procedure OnSetSelection(const ASelection: TPersistentSelectionList);
procedure SetMapLayer(AValue: TMapLayer);
procedure SetMapView(AValue: TMapView);
procedure FPOObservedChanged(ASender: TObject; Operation: TFPObservedOperation; Data: Pointer);
procedure SelectInOI(AView: TMapView; ForceUpdate: Boolean);
procedure UpdateControls;
public
property MapView: TMapView read FMapView write SetMapView;
property MapLayer: TMapLayer read FMapLayer write SetMapLayer;
end;
var
MapViewerPathEditForm: TMapViewerPathEditForm;
implementation
uses mvTypes, Math;
type
TPersistentAccess = class(TPersistent);
{$R *.lfm}
{ TMapViewerPathEditForm }
procedure TMapViewerPathEditForm.FormCreate(Sender: TObject);
begin
if Assigned(GlobalDesignHook) then
begin
GlobalDesignHook.AddHandlerModified(@OnModified);
GlobalDesignHook.AddHandlerModifiedWithName(@OnModifiedWithName);
GlobalDesignHook.AddHandlerRefreshPropertyValues(@OnRefreshPropertyValues);
GlobalDesignHook.AddHandlerSetSelection(@OnSetSelection);
end;
end;
procedure TMapViewerPathEditForm.FormClose(Sender: TObject;
var CloseAction: TCloseAction);
begin
CloseAction := caHide;
SetMapView(Nil);
end;
procedure TMapViewerPathEditForm.actSelectExecute(Sender: TObject);
begin
; //
end;
procedure TMapViewerPathEditForm.actNewPOIExecute(Sender: TObject);
var
CP: TMapPoint;
NP: TPointOfInterest;
R: TRealPoint;
V: TMapView;
begin
V := MapView;
if not Assigned(MapLayer) or not V.EditMark.HasSelection then
Exit;
CP := MapView.EditMark.CurrentPoint;
NP := MapLayer.PointsOfInterest.Add as TPointOfInterest;
NP.Assign(CP);
if CP is TPointOfInterest then
begin
NP.Caption := TPointOfInterest(CP).Caption;
NP.ImageIndex := TPointOfInterest(CP).ImageIndex;
end;
R := MapView.ScreenToLatLon(CP.ToScreen + Point(10, 10));
NP.Latitude := R.Lat;
NP.Longitude := R.Lon;
MapView.EditMark.ClearSelection;
MapView.EditMark.Selection.Insert(0, NP);
try
if Assigned(GlobalDesignHook) then
GlobalDesignHook.PersistentAdded(NP, True);
finally
MapView := V;
SelectInOI(V, False);
end;
UpdateControls;
end;
procedure TMapViewerPathEditForm.actNewTrackExecute(Sender: TObject);
var
P, NP: TMapPoint;
T, NT: TMapTrack;
R: TRealPoint;
I: Integer;
V: TMapView;
begin
V := MapView;
if not Assigned(MapLayer) or not V.EditMark.HasSelection or
(V.EditMark.Selection.Count < 2) then
Exit;
T := MapView.EditMark.CurrentTrack;
NT := MapLayer.Tracks.Add as TMapTrack;
for P in V.EditMark.Selection.Points do
begin
NP := NT.Points.Add as TMapPoint;
NP.Assign(P);
R := MapView.ScreenToLatLon(P.ToScreen + Point(10, 10));
NP.Latitude := R.Lat;
NP.Longitude := R.Lon;
end;
MapView.EditMark.ClearSelection;
for I := 0 to Pred(NT.Points.Count) do
begin
MapView.EditMark.Selection.Add(NT.Points);
MapView.EditMark.Selection.Insert(0, NT.Points[I]);
end;
if Assigned(T) then
begin // NT.Assign(T)?
NT.LineColor := T.LineColor;
NT.LineWidth := T.LineWidth;
NT.ConnectColor := T.ConnectColor;
NT.ConnectWidth := T.ConnectWidth;
NT.Opacity := T.Opacity;
end;
try
if Assigned(GlobalDesignHook) then
GlobalDesignHook.PersistentAdded(NT, True);
finally
MapView := V;
SelectInOI(V, False);
end;
UpdateControls;
end;
procedure TMapViewerPathEditForm.actNewAreaExecute(Sender: TObject);
var
P, NP: TMapPoint;
A, NA: TMapArea;
R: TRealPoint;
I: Integer;
V: TMapView;
begin
V := MapView;
if not Assigned(MapLayer) or not V.EditMark.HasSelection or
(V.EditMark.Selection.Count < 3) then
Exit;
A := MapView.EditMark.CurrentArea;
NA := MapLayer.Areas.Add as TMapArea;
for P in V.EditMark.Selection.Points do
begin
NP := NA.Points.Add as TMapPoint;
NP.Assign(P);
R := MapView.ScreenToLatLon(P.ToScreen + Point(10, 10));
NP.Latitude := R.Lat;
NP.Longitude := R.Lon;
end;
MapView.EditMark.ClearSelection;
for I := 0 to Pred(NA.Points.Count) do
begin
MapView.EditMark.Selection.Add(NA.Points);
MapView.EditMark.Selection.Insert(0, NA.Points[I]);
end;
if Assigned(A) then
begin // NA.Assign(A)?
NA.LineColor := A.LineColor;
NA.LineWidth := A.LineWidth;
NA.FillColor := A.FillColor;
NA.Opacity := A.Opacity;
end;
try
if Assigned(GlobalDesignHook) then
GlobalDesignHook.PersistentAdded(NA, True);
finally
MapView := V;
SelectInOI(V, False);
end;
UpdateControls;
end;
procedure TMapViewerPathEditForm.actNewTPExecute(Sender: TObject);
var
P, N: TMapPoint;
L: TMapTrack;
A: TMapArea;
C: TCollection;
I, I1, I2, J: Integer;
V: TMapView;
procedure InsertPt(J: Integer; ANext: TMapPoint);
var
PN: TMapPoint;
begin
PN := C.Insert(J) as TMapPoint;
PN.Latitude := (P.Latitude + ANext.Latitude) / 2;
PN.Longitude := (P.Longitude + ANext.Longitude) / 2;
MapView.EditMark.Selection.Add(C);
MapView.EditMark.Selection.Insert(0, PN);
if Assigned(GlobalDesignHook) then
GlobalDesignHook.PersistentAdded(PN, True);
end;
begin
P := MapView.EditMark.CurrentPoint;
L := MapView.EditMark.CurrentTrack;
if Assigned(L) then
C := L.Points
else
begin
A := MapView.EditMark.CurrentArea;
if not Assigned(A) then
Exit;
C := A.Points;
end;
I := P.Index;
if I < 0 then
Exit;
I1 := (I + 1) mod C.Count; // Next point index
if I > 0
then I2 := Pred(I) // Prev point index
else I2 := Pred(C.Count);
V := MapView;
try
// If the next point on track/area is selected
// then insert before next
if MapView.EditMark.IsSelected(C.Items[I1]) then
InsertPt(I1, C.Items[I1] as TMapPoint)
// If the prev point on track/area is selected
// then insert before current
else if MapView.EditMark.IsSelected(C.Items[I2]) then
InsertPt(I, C.Items[I2] as TMapPoint)
// If the current point is not the last
// then insert before next(last)
else if I < Pred(C.Count) then
InsertPt(I1, C.Items[I1] as TMapPoint)
// else insert before current
else
InsertPt(I, C.Items[I2] as TMapPoint);
finally
MapView := V;
SelectInOI(V, False);
end;
UpdateControls;
end;
procedure TMapViewerPathEditForm.actDelTPExecute(Sender: TObject);
var
Pt: TMapPoint;
PtCol: TCollection;
P: TPersistent;
begin
Pt := MapView.EditMark.CurrentPoint;
PtCol := Pt.Collection;
if not (Pt is TPointOfInterest) then
if PtCol.Count < 3 then
; // TODO Just 3 points left?
if MessageDlg('Confirm deletion', 'Are you sure you want to delete ''' +
Pt.DisplayName + '''?', mtConfirmation, mbYesNo, 0 ) <> mrYes then
Exit;
// Exclude from the selection
MapView.EditMark.Selection.DelIfPresent(PtCol);
MapView.EditMark.Selection.DelIfPresent(Pt);
// Delete from the object inspector
if Assigned(GlobalDesignHook) then
begin
GlobalDesignHook.Unselect(Pt);
P := Pt; GlobalDesignHook.DeletePersistent(P);
end;
UpdateControls;
end;
procedure TMapViewerPathEditForm.actZoomInExecute(Sender: TObject);
begin
MapView.Zoom := MapView.Zoom + 1;
UpdateControls;
end;
procedure TMapViewerPathEditForm.actZoomOutExecute(Sender: TObject);
begin
MapView.Zoom := MapView.Zoom - 1;
UpdateControls;
end;
procedure TMapViewerPathEditForm.FormDestroy(Sender: TObject);
begin
if Assigned(GlobalDesignHook) then
GlobalDesignHook.RemoveAllHandlersForObject(Self);
end;
procedure TMapViewerPathEditForm.FormShow(Sender: TObject);
begin
if Assigned(FMapView) then
FMapView.ControlStyle := FMapView.ControlStyle + [csDesignInteractive]
end;
procedure TMapViewerPathEditForm.OnModified(Sender: TObject);
begin
;
end;
procedure TMapViewerPathEditForm.OnModifiedWithName(Sender: TObject;
PropName: ShortString);
begin
;
end;
procedure TMapViewerPathEditForm.OnRefreshPropertyValues;
begin
;
end;
procedure TMapViewerPathEditForm.OnSetSelection(
const ASelection: TPersistentSelectionList);
var
I, LC: Integer;
V: TMapView = Nil;
L: TMapLayer = Nil;
L2: TMapLayer = Nil;
PtCnt: Integer = 0;
P: TMapPoint = Nil;
function GetMV(ANested: TPersistent): TMapView;
begin
Result := Nil;
while Assigned(ANested) do
if ANested is TMapView
then Exit(TMapView(ANested))
else ANested := TPersistentAccess(ANested).GetOwner;
end;
function GetLA(ANested: TPersistent): TMapLayer;
begin
Result := Nil;
while Assigned(ANested) do
if ANested is TMapLayer
then Exit(TMapLayer(ANested))
else ANested := TPersistentAccess(ANested).GetOwner;
end;
begin
// Try to find the the containing map view
for I := 0 to Pred(ASelection.Count) do
begin
V := GetMV(ASelection[I]);
if Assigned(V) then
Break;
end;
SetMapView(V);
if Assigned(V) then
begin
LC := 0;
for I := 0 to Pred(ASelection.Count) do
begin
// If not internal select (i.e. from designer tree view)
if not FInternalSelect and (ASelection[I] is TMapPoint) then
begin
Inc(PtCnt);
if not Assigned(P) then
begin
P := TMapPoint(ASelection[I]);
V.EditMark.Select(P, True); // TODO Make it visible?
end
else
V.EditMark.Select(TMapPoint(ASelection[I]), False);
end;
L2 := GetLA(ASelection[I]); // Containing layer
if Assigned(L2) and L2.Visible and (L <> L2) then
begin
Inc(LC);
L := L2;
end;
end;
if LC = 1 // Just one layer?
then MapLayer := L // Yes, assign it
else MapLayer := Nil; // Multiple layers or no layer
// From the designer and no points into?
if not FInternalSelect and not Assigned(P) then
V.EditMark.ClearSelection;
end;
UpdateControls;
end;
procedure TMapViewerPathEditForm.SetMapLayer(AValue: TMapLayer);
begin
if FMapLayer = AValue then Exit;
FMapLayer := AValue;
end;
procedure TMapViewerPathEditForm.SetMapView(AValue: TMapView);
begin
if FMapView = AValue then
Exit;
// Detach the old FMapView
if Assigned(FMapView) then
begin
FMapView.FPODetachObserver(Self);
FMapView.ControlStyle := FMapView.ControlStyle - [csDesignInteractive];
end;
// Attach the new FMapView
if Assigned(AValue) then
begin
AValue.FPOAttachObserver(Self);
if Visible then
AValue.ControlStyle := AValue.ControlStyle + [csDesignInteractive];
end;
FMapView := AValue;
UpdateControls;
end;
procedure TMapViewerPathEditForm.FPOObservedChanged(ASender: TObject;
Operation: TFPObservedOperation; Data: Pointer);
var
What: TMapObserverCustomOperation;
V: TMapView;
procedure Clicked;
begin
if actSelect.Checked then
Exit;
//...
end;
begin
if Operation <> ooCustom then
Exit;
V := ASender as TMapView;
What := PMapObserverCustomOperation(Data)^;
case What of
mooSelectionCompleted:
SelectInOI(V, False);
mooStartDrag:
Clicked;
mooIsDirty: ;
mooDrag: ;
mooEndDrag:
if V.EditMark.Dirty then
begin
if Assigned(GlobalDesignHook) then
GlobalDesignHook.Modified(Self, 'Layers');
V.EditMark.Dirty := False;
end;
end;
end;
procedure TMapViewerPathEditForm.SelectInOI(AView: TMapView; ForceUpdate: Boolean);
var
I: Integer = 0;
Sel: TPersistentSelectionList;
P: TMapPoint;
begin
if not Assigned(GlobalDesignHook) or not Assigned(AView) then
Exit;
if AView.EditMark.Selection.Count < 1 then
begin
UpdateControls;
Exit;
end;
// select in OI
FInternalSelect := True;
Sel := TPersistentSelectionList.Create;
Sel.ForceUpdate := ForceUpdate;
try
for P in AView.EditMark.Selection.Points do
begin
Sel.Add(P);
Inc(I);
end;
GlobalDesignHook.SetSelection(Sel);
finally
Sel.Free;
FInternalSelect := False;
end;
end;
procedure TMapViewerPathEditForm.UpdateControls;
var
P: TMapPoint;
PtTxt: String;
PtCnt: Integer = 0;
HaveView, HaveLayer, HaveSel, HavePt: Boolean;
begin
HaveView := Assigned(MapView);
HaveSel := HaveView and MapView.EditMark.HasSelection;
HavePt := HaveSel and (MapView.EditMark.Selection[0] is TMapPoint);
HaveLayer := Assigned(MapLayer) and HaveSel;
if HaveView
then Caption := MapView.Name + ': ' + TMapView.ClassName
else Caption := TMapView.ClassName + ' (Not selected)';
// Update layer name
if HaveLayer
then cbSelectedLayer.Text := MapLayer.DisplayName
else cbSelectedLayer.Text := '(none)';
cbSelectedLayer.Hint := cbSelectedLayer.Text;
// Update currently selected point
PtTxt := '(none)';
if HavePt then
begin
for P in MapView.EditMark.Selection.Points do
Inc(PtCnt);
P := TMapPoint(MapView.EditMark.Selection[0]);
if PtCnt > 0 then
begin
PtTxt := P.DisplayName;
if PtTxt <> P.ClassName then
PtTxt := PtTxt + ': ' + P.ClassName;
if PtCnt > 1 then
PtTxt := PtTxt + Format(' +%d more', [PtCnt - 1]);
end;
end;
cbSelectedPt.Text := PtTxt;
cbSelectedPt.Hint := PtTxt;
// Update actions
actZoomIn.Enabled := HaveView and (MapView.Zoom < MapView.ZoomMax);
actZoomOut.Enabled := HaveView and (MapView.Zoom > MapView.ZoomMin);
actNewPOI.Enabled := HaveView and HaveLayer;
actNewTrack.Enabled := HaveView and HaveLayer and InRange(PtCnt, 2, 10);
actNewArea.Enabled := HaveView and HaveLayer and InRange(PtCnt, 3, 10);
actNewTP.Enabled := HaveView and HavePt;
actDelTP.Enabled := HaveView and HavePt;
actSelect.Enabled := HaveView;
end;
end.

View File

@ -17,10 +17,24 @@ unit
interface
uses
Classes, Forms, SysUtils, PropEdits, GraphPropEdits, ImgList, mvMapProvider;
Classes, Forms, SysUtils, PropEdits, GraphPropEdits, ComponentEditors,
ImgList, mvMapProvider;
type
{ TMapViewComponentEditor }
TMapViewComponentEditor = class(TComponentEditor)
FOldDirtyHandler: TNotifyEvent;
public
constructor Create(AComponent: TComponent;
ADesigner: TComponentEditorDesigner); override;
procedure ExecuteVerb(Index: Integer); override;
function GetVerb(Index: Integer): String; override;
function GetVerbCount: Integer; override;
procedure DoEdit;
end;
{ TMapLayersPropertyEditor }
TMapLayersPropertyEditor = class(TCollectionPropertyEditor)
@ -30,6 +44,15 @@ type
override;
end;
{ TMapTrackPointsPropertyEditor }
TMapTrackPointsPropertyEditor = class(TCollectionPropertyEditor)
public
class function ShowCollectionEditor(ACollection: TCollection;
OwnerPersistent: TPersistent; const PropName: String): TCustomForm;
override;
end;
{ TMapProviderPropertyEditor }
TMapProviderPropertyEditor = class(TStringPropertyEditor)
@ -78,12 +101,78 @@ procedure Register;
implementation
uses
Dialogs, IDEWindowIntf,
StrUtils,
mvGeoMath, mvMapViewer, mvGpsObj, mvLayersPropEditForm, mvEngine;
UITypes, Dialogs, IDEWindowIntf, LazIDEIntf, StrUtils, mvGeoMath, mvMapViewer,
mvGpsObj, mvLayersPropEditForm, mvEngine, mvMapViewerPathEditForm;
const
NONE = '(none)';
sNONE = '(none)';
sEDITMapView = 'Edit MapView Points';
{ TMapTrackPointsPropertyEditor }
class function TMapTrackPointsPropertyEditor.ShowCollectionEditor(
ACollection: TCollection; OwnerPersistent: TPersistent; const PropName: String
): TCustomForm;
begin
Result := inherited ShowCollectionEditor(ACollection, OwnerPersistent, PropName
);
end;
{ TMapViewComponentEditor }
constructor TMapViewComponentEditor.Create(AComponent: TComponent;
ADesigner: TComponentEditorDesigner);
begin
inherited Create(AComponent, ADesigner);
end;
procedure TMapViewComponentEditor.ExecuteVerb(Index: Integer);
begin
case Index of
0: DoEdit;
end;
end;
function TMapViewComponentEditor.GetVerb(Index: Integer): String;
begin
Result := '';
case Index of
0: Result := sEDITMapView;
end;
end;
function TMapViewComponentEditor.GetVerbCount: Integer;
begin
Result := 1;
end;
procedure TMapViewComponentEditor.DoEdit;
var
V: TMapView;
P: TPoint;
begin
if not Assigned(MapViewerPathEditForm) then
MapViewerPathEditForm := TMapViewerPathEditForm.Create(
LazarusIDE.OwningComponent);
V := GetComponent as TMapView;
if not (mvoEditorEnabled in V.Options) then
case QuestionDlg(V.ClassName,
V.Name + ' doesn''t have mvoEditorEnabled set in Options.' + LineEnding +
LineEnding +
'Do you want to enable the editor?',
mtWarning,
[mrYes, '&Enable and continue', mrCancel, '&Cancel', 'IsDefault', 'IsCancel'], 0 )
of
mrYes: V.Options := V.Options + [mvoEditorEnabled];
otherwise
Exit;
end;
MapViewerPathEditForm.MapView := V;
P := V.ControlToScreen(Point(V.Width, 0));
MapViewerPathEditForm.Left := P.X;
MapViewerPathEditForm.Top := P.Y;
MapViewerPathEditForm.Show;
end;
{ TMapLayersPropertyEditor }
@ -112,12 +201,12 @@ function TPointElevationPropertyEditor.GetValue: string;
begin
Result := inherited GetValue;
if GetFloatValue = NO_ELE then
Result := NONE;
Result := sNONE;
end;
procedure TPointElevationPropertyEditor.SetValue(const NewValue: AnsiString);
begin
if (NewValue = NONE) or (NewValue = '')
if (NewValue = sNONE) or (NewValue = '')
then inherited SetFloatValue(NO_ELE)
else inherited SetValue(NewValue);
end;
@ -157,12 +246,12 @@ function TPointDateTimePropertyEditor.GetValue: string;
begin
Result := inherited GetValue;
if GetFloatValue = NO_DATE then
Result := NONE;
Result := sNONE;
end;
procedure TPointDateTimePropertyEditor.SetValue(const NewValue: AnsiString);
begin
if (NewValue = NONE) or (NewValue = '')
if (NewValue = sNONE) or (NewValue = '')
then inherited SetFloatValue(NO_DATE)
else inherited SetValue(NewValue);
end;
@ -212,7 +301,7 @@ begin
Exit;
MV.Engine.GetMapProviders(Providers);
//if not (Inst is TMapView) then
Proc(NONE);
Proc(sNONE);
Providers.Sort;
for S in Providers do
// TODO: When filtered it is not clear what is the full list.
@ -227,21 +316,26 @@ function TMapProviderPropertyEditor.GetValue: AnsiString;
begin
Result := inherited GetValue;
if Result = '' then
Result := NONE;
Result := sNONE;
end;
procedure TMapProviderPropertyEditor.SetValue(const NewValue: AnsiString);
begin
if NewValue = NONE
if NewValue = sNONE
then inherited SetValue('')
else inherited SetValue(NewValue);
end;
procedure Register;
begin
RegisterComponentEditor(TMapView, TMapViewComponentEditor);
RegisterPropertyEditor(TypeInfo(TMapLayers),
TMapView, 'Layers', TMapLayersPropertyEditor);
RegisterPropertyEditor(TypeInfo(TMapTrackPoints),
TMapTrack, 'Points', TMapTrackPointsPropertyEditor);
RegisterPropertyEditor(TypeInfo(String),
TMapView,'MapProvider',TMapProviderPropertyEditor);
RegisterPropertyEditor(TypeInfo(String),