LazMapViewer: Added option TMapViewOption.mvoLatLonInDMS to show/edit Latitude and Longitude in deg-min-secs.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@9270 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
parent
78ce108950
commit
3f0fa39576
@ -222,6 +222,7 @@ function LatToStr(ALatitude: Double; DMS: Boolean; AFormatSettings: TFormatSetti
|
||||
function LonToStr(ALongitude: Double; DMS: Boolean): String;
|
||||
function LonToStr(ALongitude: Double; DMS: Boolean; AFormatSettings: TFormatSettings): String;
|
||||
function TryStrToGps(const AValue: String; out ADeg: Double): Boolean;
|
||||
function TryStrDMSToDeg(const AValue: String; out ADeg: Double): Boolean;
|
||||
|
||||
procedure SplitGps(AValue: Double; out ADegs, AMins, ASecs: Double);
|
||||
|
||||
@ -1648,6 +1649,138 @@ begin
|
||||
ADeg := sgn * (abs(ADeg) + mins / 60 + secs / 3600);
|
||||
end;
|
||||
|
||||
{ Convert Lat/Lon string to degrees.
|
||||
|
||||
Recognized formats:
|
||||
|
||||
Degrees, Minutes and Seconds:
|
||||
DDD°MM'SS.S"
|
||||
32°18'23.1"N 122°36'52.5"W
|
||||
|
||||
Degrees and Decimal Minutes:
|
||||
DDD° MM.MMM'
|
||||
32°18.385'N 122°36.875'W
|
||||
|
||||
Decimal Degrees:
|
||||
DDD.DDDDD°
|
||||
32.30642°N 122.61458°W
|
||||
+32.30642 -122.61458
|
||||
}
|
||||
function TryStrDMSToDeg(const AValue: String; out ADeg: Double): Boolean;
|
||||
const
|
||||
NUMERIC_CHARS = ['0'..'9', '.', ',', '-', '+'];
|
||||
WS_CHARS = [' '];
|
||||
var
|
||||
I, Len, N: Integer;
|
||||
S: String;
|
||||
D, Minutes, Seconds: Double;
|
||||
R: Word;
|
||||
Last, Neg: Boolean;
|
||||
|
||||
function EOL: Boolean; inline;
|
||||
begin
|
||||
Result := Len < I;
|
||||
end;
|
||||
|
||||
procedure SkipWS; inline;
|
||||
begin
|
||||
while not EOL and (AValue[I] in WS_CHARS) do
|
||||
Inc(I);
|
||||
end;
|
||||
|
||||
function NextNum: String;
|
||||
begin
|
||||
Result := '';
|
||||
SkipWS;
|
||||
while not EOL and (AValue[I] in NUMERIC_CHARS) do
|
||||
begin
|
||||
if AValue[I] = ','
|
||||
then Result := Result + '.'
|
||||
else Result := Result + AValue[I];
|
||||
Inc(I);
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
Result := False;
|
||||
ADeg := NaN;
|
||||
Len := Length(AValue);
|
||||
I := 1;
|
||||
|
||||
// Degrees
|
||||
S := NextNum;
|
||||
if S = '' then
|
||||
Exit;
|
||||
Val(S, ADeg, R);
|
||||
if R > 0 then
|
||||
Exit;
|
||||
|
||||
// It must be the only part if negative or fractional
|
||||
Neg := ADeg < 0.0;
|
||||
Last := Neg or (Frac(ADeg) > 0.0);
|
||||
|
||||
// Eat the degree symbol if present
|
||||
if not EOL and (Utf8CodePointLen(@AValue[I], 2, False) = 2)
|
||||
and (AValue[I] = #$c2) and (AValue[I + 1] = #$b0)
|
||||
then
|
||||
Inc(I, 2);
|
||||
|
||||
Minutes := 0.0;
|
||||
Seconds := 0.0;
|
||||
|
||||
N := 1;
|
||||
while not (EOL or Last) and (N < 3) do
|
||||
begin
|
||||
S := NextNum;
|
||||
if S = '' then
|
||||
Break
|
||||
else
|
||||
begin
|
||||
Val(S, D, R);
|
||||
// Invalid part or negative one
|
||||
if (R > 0) or (D < 0.0) then
|
||||
Exit;
|
||||
// No more parts when fractional
|
||||
Last := Frac(D) > 0.0;
|
||||
if not EOL then
|
||||
case AValue[I] of
|
||||
'''': // Munutes suffix
|
||||
begin
|
||||
Minutes := D;
|
||||
Inc(I); // Eat the '
|
||||
end;
|
||||
'"': // Seconds suffix
|
||||
begin
|
||||
Seconds := D;
|
||||
Last := True; // Last part
|
||||
Inc(I); // Eat the "
|
||||
end;
|
||||
otherwise
|
||||
if N = 1
|
||||
then Minutes := D
|
||||
else Seconds := D;
|
||||
end;
|
||||
end;
|
||||
Inc(N);
|
||||
end;
|
||||
|
||||
// Merge parts
|
||||
ADeg := ADeg + Minutes / 60 + Seconds / 3600;
|
||||
|
||||
// Check for N-S and E-W designators
|
||||
SkipWS;
|
||||
if not (EOL or Neg) and (AValue[I] in ['S', 's', 'W', 'w', 'N', 'n', 'E', 'e']) then
|
||||
begin
|
||||
if AValue[I] in ['S', 's', 'W', 'w']
|
||||
then ADeg := -1 * ADeg;
|
||||
Inc(I);
|
||||
end;
|
||||
SkipWS;
|
||||
|
||||
// It must be entirely consumed
|
||||
Result := EOL;
|
||||
end;
|
||||
|
||||
// https://stackoverflow.com/questions/73608975/pascal-delphi-11-formula-for-distance-in-meters-between-two-decimal-gps-point
|
||||
function HaversineDist(Lat1, Lon1, Lat2, Lon2, Radius: Double): Double;
|
||||
var
|
||||
|
@ -113,31 +113,13 @@ procedure TLayersPropertyEditForm.LoadFromFile(AFileName: String;
|
||||
var
|
||||
List: TGpsObjectList;
|
||||
LBounds: TRealArea;
|
||||
I: Integer;
|
||||
|
||||
procedure AddPoint(APoint: TGPSPoint);
|
||||
begin
|
||||
with ALayer.PointsOfInterest.Add as TPointOfInterest do
|
||||
begin
|
||||
Caption := APoint.Name;
|
||||
Longitude := APoint.Lon;
|
||||
Latitude := APoint.Lat;
|
||||
Elevation := APoint.Elevation;
|
||||
DateTime := APoint.DateTime;
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
with TGpxReader.Create do
|
||||
try
|
||||
List := TGPSObjectList.Create;
|
||||
try
|
||||
LoadFromFile(AFileName, List, LBounds);
|
||||
for I := 0 to Pred(List.Count) do
|
||||
if List[I] is TGPSPoint then
|
||||
AddPoint(TGPSPoint(List[I]))
|
||||
else
|
||||
{TODO};
|
||||
ALayer.AssignFromGPSList(List);
|
||||
finally
|
||||
List.Free;
|
||||
end;
|
||||
|
@ -35,7 +35,8 @@ Type
|
||||
TMapViewOption =
|
||||
(
|
||||
mvoMouseDragging, // Allow dragging of the map with the mouse
|
||||
mvoMouseZooming // Allow zooming into 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;
|
||||
@ -111,6 +112,7 @@ type
|
||||
public
|
||||
constructor Create(ACollection: TCollection); override;
|
||||
destructor Destroy; override;
|
||||
procedure AssignFromGPSList(AList: TGPSObjectList);
|
||||
property MapView: TMapView read GetMapView;
|
||||
property ComboLayer: TGPSComboLayer read FComboLayer;
|
||||
published
|
||||
@ -136,6 +138,7 @@ type
|
||||
FLatitude: Double;
|
||||
FLongitude: Double;
|
||||
FView: TMapView;
|
||||
function GetLatLonInDMS: Boolean;
|
||||
procedure SetLatitude(AValue: Double);
|
||||
procedure SetLongitude(AValue: Double);
|
||||
procedure SetViewCenter;
|
||||
@ -143,6 +146,7 @@ type
|
||||
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;
|
||||
@ -159,6 +163,7 @@ type
|
||||
FLongitude: Double;
|
||||
FOnDrawPoint: TPointOfInterestDrawEvent;
|
||||
FPoint: TGPSPointOfInterest;
|
||||
function GetLatLonInDMS: Boolean;
|
||||
function GetLayer: TMapLayer;
|
||||
function GetMapView: TMapView;
|
||||
function IsDateTimeStored: Boolean;
|
||||
@ -178,6 +183,7 @@ type
|
||||
destructor Destroy; override;
|
||||
property MapView: TMapView read GetMapView;
|
||||
property Layer: TMapLayer read GetLayer;
|
||||
property LatLonInDMS: Boolean read GetLatLonInDMS;
|
||||
published
|
||||
property Longitude: Double read FLongitude write SetLongitude;
|
||||
property Latitude: Double read FLatitude write SetLatitude;
|
||||
@ -640,6 +646,11 @@ begin
|
||||
else Result := Nil;
|
||||
end;
|
||||
|
||||
function TPointOfInterest.GetLatLonInDMS: Boolean;
|
||||
begin
|
||||
Result := Assigned(MapView) and (mvoLatLonInDMS in MapView.Options);
|
||||
end;
|
||||
|
||||
procedure TPointOfInterest.SetImageIndex(AValue: TImageIndex);
|
||||
begin
|
||||
if FImageIndex = AValue then Exit;
|
||||
@ -737,6 +748,11 @@ begin
|
||||
SetViewCenter;
|
||||
end;
|
||||
|
||||
function TMapCenter.GetLatLonInDMS: Boolean;
|
||||
begin
|
||||
Result := Assigned(FView) and (mvoLatLonInDMS in FView.Options);
|
||||
end;
|
||||
|
||||
procedure TMapCenter.SetViewCenter;
|
||||
var
|
||||
R: TRealPoint;
|
||||
@ -877,6 +893,33 @@ begin
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TMapLayer.AssignFromGPSList(AList: TGPSObjectList);
|
||||
|
||||
procedure AddPoint(APoint: TGPSPoint);
|
||||
begin
|
||||
with PointsOfInterest.Add as TPointOfInterest do
|
||||
begin
|
||||
Caption := APoint.Name;
|
||||
Longitude := APoint.Lon;
|
||||
Latitude := APoint.Lat;
|
||||
Elevation := APoint.Elevation;
|
||||
DateTime := APoint.DateTime;
|
||||
end;
|
||||
end;
|
||||
|
||||
var
|
||||
I: Integer;
|
||||
begin
|
||||
if not Assigned(AList) then
|
||||
Exit;
|
||||
PointsOfInterest.Clear;
|
||||
for I := 0 to Pred(AList.Count) do
|
||||
if AList[I] is TGPSPoint then
|
||||
AddPoint(TGPSPoint(AList[I]))
|
||||
else
|
||||
{TODO};
|
||||
end;
|
||||
|
||||
{ TMapLayers }
|
||||
|
||||
procedure TMapLayers.Update(Item: TCollectionItem);
|
||||
|
@ -63,12 +63,23 @@ type
|
||||
procedure SetValue(const NewValue: AnsiString); override;
|
||||
end;
|
||||
|
||||
{ TLatLonDMSPropertyEditor }
|
||||
|
||||
TLatLonDMSPropertyEditor = class(TFloatPropertyEditor)
|
||||
private
|
||||
function UseDMS: Boolean;
|
||||
public
|
||||
function GetValue: string; override;
|
||||
procedure SetValue(const NewValue: AnsiString); override;
|
||||
end;
|
||||
|
||||
procedure Register;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
Dialogs, IDEWindowIntf, mvMapViewer, mvGpsObj, mvLayersPropEditForm;
|
||||
Dialogs, IDEWindowIntf, mvMapViewer, mvGpsObj, mvLayersPropEditForm, mvEngine,
|
||||
StrUtils;
|
||||
|
||||
const
|
||||
NONE = '(none)';
|
||||
@ -110,6 +121,35 @@ begin
|
||||
else inherited SetValue(NewValue);
|
||||
end;
|
||||
|
||||
{ TLatLonDMSPropertyEditor }
|
||||
|
||||
function TLatLonDMSPropertyEditor.UseDMS: Boolean;
|
||||
var
|
||||
Inst: TPersistent;
|
||||
begin
|
||||
Inst := GetComponent(0);
|
||||
Result := (Inst is TPointOfInterest) and TPointOfInterest(Inst).LatLonInDMS
|
||||
or (Inst is TMapCenter) and TMapCenter(Inst).LatLonInDMS;
|
||||
end;
|
||||
|
||||
function TLatLonDMSPropertyEditor.GetValue: string;
|
||||
begin
|
||||
if not UseDMS
|
||||
then Result := inherited GetValue
|
||||
else if StartsText('Lat', GetName)
|
||||
then Result := LatToStr(GetFloatValue, True)
|
||||
else Result := LonToStr(GetFloatValue, True);
|
||||
end;
|
||||
|
||||
procedure TLatLonDMSPropertyEditor.SetValue(const NewValue: AnsiString);
|
||||
var
|
||||
Deg: Double;
|
||||
begin
|
||||
if UseDMS and TryStrDMSToDeg(NewValue, Deg)
|
||||
then SetFloatValue(Deg)
|
||||
else inherited SetValue(NewValue);
|
||||
end;
|
||||
|
||||
{ TPointDateTimePropertyEditor }
|
||||
|
||||
function TPointDateTimePropertyEditor.GetValue: string;
|
||||
@ -211,6 +251,15 @@ begin
|
||||
TPointOfInterest, 'DateTime', TPointDateTimePropertyEditor);
|
||||
RegisterPropertyEditor(TypeInfo(Double),
|
||||
TPointOfInterest, 'Elevation', TPointElevationPropertyEditor);
|
||||
|
||||
RegisterPropertyEditor(TypeInfo(Double),
|
||||
TPointOfInterest,'Latitude',TLatLonDMSPropertyEditor);
|
||||
RegisterPropertyEditor(TypeInfo(Double),
|
||||
TPointOfInterest,'Longitude',TLatLonDMSPropertyEditor);
|
||||
RegisterPropertyEditor(TypeInfo(Double),
|
||||
TMapCenter,'Latitude',TLatLonDMSPropertyEditor);
|
||||
RegisterPropertyEditor(TypeInfo(Double),
|
||||
TMapCenter,'Longitude',TLatLonDMSPropertyEditor);
|
||||
end;
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user