LazMapViewer: Add GreatCirclePainterPlugin by Ekkehard Domning. Refactor some calculation routines in mvGeoMath

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@9609 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2025-01-29 17:38:26 +00:00
parent 320d413df5
commit 06b87dfade
8 changed files with 2205 additions and 28 deletions

View File

@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="12"/>
<PathDelim Value="\"/>
<General>
<SessionStorage Value="InProjectDir"/>
<Title Value="greatcircle_demo"/>
<Scaled Value="True"/>
<ResourceType Value="res"/>
<UseXPManifest Value="True"/>
<XPManifest>
<DpiAware Value="True"/>
</XPManifest>
<Icon Value="0"/>
</General>
<BuildModes>
<Item Name="Default" Default="True"/>
<Item Name="Debug">
<CompilerOptions>
<Version Value="11"/>
<PathDelim Value="\"/>
<Target>
<Filename Value="greatcircle_demo"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<Parsing>
<SyntaxOptions>
<IncludeAssertionCode Value="True"/>
</SyntaxOptions>
</Parsing>
<CodeGeneration>
<Checks>
<IOChecks Value="True"/>
<RangeChecks Value="True"/>
<OverflowChecks Value="True"/>
<StackChecks Value="True"/>
</Checks>
<VerifyObjMethodCallValidity Value="True"/>
</CodeGeneration>
<Linking>
<Debugging>
<DebugInfoType Value="dsDwarf3"/>
<UseHeaptrc Value="True"/>
<TrashVariables Value="True"/>
<UseExternalDbgSyms Value="True"/>
</Debugging>
<Options>
<Win32>
<GraphicApplication Value="True"/>
</Win32>
</Options>
</Linking>
</CompilerOptions>
</Item>
<Item Name="Release">
<CompilerOptions>
<Version Value="11"/>
<PathDelim Value="\"/>
<Target>
<Filename Value="greatcircle_demo"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<CodeGeneration>
<SmartLinkUnit Value="True"/>
<Optimizations>
<OptimizationLevel Value="3"/>
</Optimizations>
</CodeGeneration>
<Linking>
<Debugging>
<GenerateDebugInfo Value="False"/>
<RunWithoutDebug Value="True"/>
</Debugging>
<LinkSmart Value="True"/>
<Options>
<Win32>
<GraphicApplication Value="True"/>
</Win32>
</Options>
</Linking>
</CompilerOptions>
</Item>
</BuildModes>
<PublishOptions>
<Version Value="2"/>
<UseFileFilters Value="True"/>
</PublishOptions>
<RunParams>
<FormatVersion Value="2"/>
</RunParams>
<RequiredPackages>
<Item>
<PackageName Value="lazMapViewerPkg"/>
</Item>
<Item>
<PackageName Value="LCL"/>
</Item>
</RequiredPackages>
<Units>
<Unit>
<Filename Value="greatcircle_demo.lpr"/>
<IsPartOfProject Value="True"/>
</Unit>
<Unit>
<Filename Value="main.pas"/>
<IsPartOfProject Value="True"/>
<ComponentName Value="MainForm"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
</Unit>
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="11"/>
<PathDelim Value="\"/>
<Target>
<Filename Value="greatcircle_demo"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<Linking>
<Debugging>
<DebugInfoType Value="dsDwarf3"/>
</Debugging>
<Options>
<Win32>
<GraphicApplication Value="True"/>
</Win32>
</Options>
</Linking>
</CompilerOptions>
<Debugging>
<Exceptions>
<Item>
<Name Value="EAbort"/>
</Item>
<Item>
<Name Value="ECodetoolError"/>
</Item>
<Item>
<Name Value="EFOpenError"/>
</Item>
</Exceptions>
</Debugging>
</CONFIG>

View File

@ -0,0 +1,24 @@
program greatcircle_demo;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}
cthreads,
{$ENDIF}
{$IFDEF HASAMIGA}
athreads,
{$ENDIF}
Interfaces, // this includes the LCL widgetset
Forms, main;
{$R *.res}
begin
RequireDerivedFormResource:=True;
Application.Scaled:=True;
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
Application.Run;
end.

View File

@ -0,0 +1,542 @@
object MainForm: TMainForm
Left = 324
Height = 493
Top = 119
Width = 872
Caption = 'Great Circle Plugin Demo'
ClientHeight = 493
ClientWidth = 872
LCLVersion = '4.99.0.0'
OnActivate = FormActivate
OnCreate = FormCreate
object ParamsPanel: TPanel
Left = 8
Height = 454
Top = 8
Width = 200
Align = alLeft
BorderSpacing.Around = 8
BevelOuter = bvNone
ClientHeight = 454
ClientWidth = 200
TabOrder = 0
object cbCyclicMap: TCheckBox
AnchorSideLeft.Control = ParamsPanel
AnchorSideTop.Control = ParamsPanel
Left = 0
Height = 19
Top = 0
Width = 77
Caption = 'Cyclic Map'
Checked = True
State = cbChecked
TabOrder = 0
OnChange = cbCyclicMapChange
end
object cbZOrder: TComboBox
AnchorSideLeft.Control = tbSegmentLength
AnchorSideTop.Control = cbCyclicMap
AnchorSideTop.Side = asrBottom
AnchorSideRight.Control = ParamsPanel
AnchorSideRight.Side = asrBottom
Left = 92
Height = 23
Top = 27
Width = 108
Anchors = [akTop, akLeft, akRight]
BorderSpacing.Top = 8
ItemHeight = 15
ItemIndex = 0
Items.Strings = (
'Canvas'
'In front of markers'
'Behind markers'
)
Style = csDropDownList
TabOrder = 1
Text = 'Canvas'
OnChange = cbZOrderChange
end
object lblZOrder: TLabel
AnchorSideLeft.Control = ParamsPanel
AnchorSideTop.Control = cbZOrder
AnchorSideTop.Side = asrCenter
AnchorSideRight.Control = Label2
AnchorSideRight.Side = asrBottom
Left = 42
Height = 15
Top = 31
Width = 42
Anchors = [akTop, akRight]
BorderSpacing.Top = 4
Caption = 'Z-Order'
end
object tbSegmentLength: TTrackBar
AnchorSideLeft.Control = Label2
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = cbZOrder
AnchorSideTop.Side = asrBottom
AnchorSideRight.Control = ParamsPanel
AnchorSideRight.Side = asrBottom
Left = 92
Height = 25
Top = 58
Width = 108
Position = 0
OnChange = tbSegmentLengthChange
Anchors = [akTop, akLeft, akRight]
BorderSpacing.Left = 8
BorderSpacing.Top = 8
TabOrder = 2
end
object Label2: TLabel
AnchorSideLeft.Control = ParamsPanel
AnchorSideTop.Control = tbSegmentLength
Left = 0
Height = 15
Top = 58
Width = 84
Caption = 'Segment length'
end
object gbPresets: TGroupBox
AnchorSideLeft.Control = ParamsPanel
AnchorSideTop.Control = GroupBox3
AnchorSideTop.Side = asrBottom
AnchorSideRight.Control = ParamsPanel
AnchorSideRight.Side = asrBottom
Left = 0
Height = 161
Top = 294
Width = 200
Anchors = [akTop, akLeft, akRight]
AutoSize = True
BorderSpacing.Top = 8
Caption = 'Presets'
ChildSizing.LeftRightSpacing = 16
ChildSizing.TopBottomSpacing = 8
ChildSizing.EnlargeHorizontal = crsHomogenousChildResize
ChildSizing.Layout = cclLeftToRightThenTopToBottom
ChildSizing.ControlsPerLine = 1
ClientHeight = 141
ClientWidth = 196
TabOrder = 6
object btnPresetPolar: TButton
Left = 16
Height = 25
Top = 8
Width = 164
Caption = 'Polar'
TabOrder = 0
OnClick = btnPresetPolarClick
end
object btnPreseEquator: TButton
Left = 16
Height = 25
Top = 33
Width = 164
Caption = 'Equator'
TabOrder = 1
OnClick = btnPreseEquatorClick
end
object btnPresetPorto: TButton
Left = 16
Height = 25
Top = 58
Width = 164
Caption = 'Porto-PoS'
TabOrder = 2
OnClick = btnPresetPortoClick
end
object btnPresetLongestSeaWay: TButton
Left = 16
Height = 25
Top = 83
Width = 164
Caption = 'Longest sea way'
TabOrder = 3
OnClick = btnPresetLongestSeaWayClick
end
object btnLongestEarthWay: TButton
Left = 16
Height = 25
Top = 108
Width = 164
Caption = 'Longest earth way'
TabOrder = 4
OnClick = btnLongestEarthWayClick
end
end
object cgOptions: TCheckGroup
AnchorSideLeft.Control = ParamsPanel
AnchorSideTop.Control = tbSegmentLength
AnchorSideTop.Side = asrBottom
AnchorSideRight.Control = ParamsPanel
AnchorSideRight.Side = asrBottom
Left = 0
Height = 89
Top = 83
Width = 200
Anchors = [akTop, akLeft, akRight]
AutoFill = True
AutoSize = True
Caption = 'Options'
ChildSizing.LeftRightSpacing = 6
ChildSizing.TopBottomSpacing = 6
ChildSizing.EnlargeHorizontal = crsHomogenousChildResize
ChildSizing.EnlargeVertical = crsHomogenousChildResize
ChildSizing.ShrinkHorizontal = crsScaleChilds
ChildSizing.ShrinkVertical = crsScaleChilds
ChildSizing.Layout = cclLeftToRightThenTopToBottom
ChildSizing.ControlsPerLine = 1
ClientHeight = 69
ClientWidth = 196
Items.Strings = (
'Mark Start'
'Mark Center'
'Mark Destination'
)
TabOrder = 3
OnItemClick = cgOptionsItemClick
Data = {
03000000020202
}
end
object gbOrthodromePen: TGroupBox
AnchorSideLeft.Control = ParamsPanel
AnchorSideTop.Control = cgOptions
AnchorSideTop.Side = asrBottom
AnchorSideRight.Control = ParamsPanel
AnchorSideRight.Side = asrBottom
Left = 0
Height = 49
Top = 180
Width = 200
Anchors = [akTop, akLeft, akRight]
AutoSize = True
BorderSpacing.Top = 8
Caption = 'Orthodrome Pen'
ClientHeight = 29
ClientWidth = 196
TabOrder = 4
object lblOrthodromePenWidth: TLabel
AnchorSideLeft.Control = gbOrthodromePen
AnchorSideTop.Control = seOrthodromePenWidth
AnchorSideTop.Side = asrCenter
Left = 16
Height = 15
Top = 4
Width = 32
BorderSpacing.Left = 16
Caption = 'Width'
end
object seOrthodromePenWidth: TSpinEdit
AnchorSideLeft.Control = lblOrthodromePenWidth
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = clbOrthodromePenColor
AnchorSideTop.Side = asrCenter
Left = 56
Height = 23
Top = 0
Width = 64
Alignment = taRightJustify
BorderSpacing.Left = 8
BorderSpacing.Right = 8
BorderSpacing.Bottom = 6
MaxValue = 10
MinValue = 1
TabOrder = 0
Value = 3
OnChange = seOrthodromePenWidthChange
end
object clbOrthodromePenColor: TColorButton
AnchorSideLeft.Control = seOrthodromePenWidth
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = seOrthodromePenWidth
AnchorSideRight.Control = gbOrthodromePen
AnchorSideRight.Side = asrBottom
Left = 128
Height = 25
Top = -1
Width = 60
Anchors = [akLeft, akRight]
BorderSpacing.Right = 8
BorderSpacing.Bottom = 8
BorderWidth = 2
ButtonColorSize = 16
ButtonColor = clPurple
OnColorChanged = clbOrthodromePenColorColorChanged
end
end
object GroupBox3: TGroupBox
AnchorSideLeft.Control = ParamsPanel
AnchorSideTop.Control = gbOrthodromePen
AnchorSideTop.Side = asrBottom
AnchorSideRight.Control = ParamsPanel
AnchorSideRight.Side = asrBottom
Left = 0
Height = 49
Top = 237
Width = 200
Anchors = [akTop, akLeft, akRight]
AutoSize = True
BorderSpacing.Top = 8
Caption = 'Great Circle Pen'
ClientHeight = 29
ClientWidth = 196
TabOrder = 5
object lblGreatCirclePenWidth: TLabel
AnchorSideLeft.Control = GroupBox3
AnchorSideTop.Control = seGreatCirclePenWidth
AnchorSideTop.Side = asrCenter
Left = 16
Height = 15
Top = 4
Width = 32
BorderSpacing.Left = 16
Caption = 'Width'
end
object seGreatCirclePenWidth: TSpinEdit
AnchorSideLeft.Control = lblGreatCirclePenWidth
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = GroupBox3
Left = 56
Height = 23
Top = 0
Width = 64
Alignment = taRightJustify
BorderSpacing.Left = 8
BorderSpacing.Right = 8
BorderSpacing.Bottom = 6
MaxValue = 10
MinValue = 1
TabOrder = 0
Value = 1
OnChange = seGreatCirclePenWidthChange
end
object clbGreatCirclePenColor: TColorButton
AnchorSideLeft.Control = seGreatCirclePenWidth
AnchorSideLeft.Side = asrBottom
AnchorSideRight.Control = GroupBox3
AnchorSideRight.Side = asrBottom
Left = 128
Height = 25
Top = 0
Width = 60
Anchors = [akTop, akLeft, akRight]
BorderSpacing.Right = 8
BorderWidth = 2
ButtonColorSize = 16
ButtonColor = clBlack
OnColorChanged = clbGreatCirclePenColorColorChanged
end
end
end
object StatusBar: TStatusBar
Left = 0
Height = 23
Top = 470
Width = 872
Panels = <
item
Bevel = pbRaised
Text = 'Orthodrome (km)'
Width = 100
end
item
Width = 60
end
item
Text = 'Rest (km)'
Width = 60
end
item
Width = 60
end
item
Bevel = pbRaised
Text = 'Start'
Width = 40
end
item
Width = 120
end
item
Bevel = pbRaised
Text = 'Destination'
Width = 70
end
item
Width = 120
end
item
Text = 'Bearing'
Width = 50
end
item
Width = 50
end
item
Bevel = pbRaised
Text = 'Pt. Cnt'
Width = 50
end
item
Width = 50
end
item
Width = 50
end>
SimplePanel = False
end
object MapPanel: TPanel
Left = 216
Height = 470
Top = 0
Width = 656
Align = alClient
BevelOuter = bvNone
Caption = 'MapPanel'
ClientHeight = 470
ClientWidth = 656
TabOrder = 2
object MapView: TMapView
Left = 0
Height = 443
Top = 27
Width = 656
Align = alClient
Cyclic = True
DownloadEngine = MapView.BuiltInDLE
DrawingEngine = MapView.BuiltInDE
Layers = <>
Font.Color = clBlack
MapProvider = 'OpenStreetMap Standard'
PluginManager = MvPluginManager
POIImages = ImageList
end
object lblInfo: TLabel
Left = 6
Height = 15
Top = 6
Width = 644
Align = alTop
Alignment = taCenter
BorderSpacing.Around = 6
Caption = 'Drag start or destination points with left mouse button'
end
end
object MvPluginManager: TMvPluginManager
Left = 320
Top = 40
object MvPluginManagerDraggableMarkerPlugin1: TDraggableMarkerPlugin
DraggableMarkerMovedEvent = MvPluginManagerDraggableMarkerPlugin1DraggableMarkerMovedEvent
end
end
object PolarPopupMenu: TPopupMenu
Left = 248
Top = 328
object MenuItem7: TMenuItem
Tag = 1
Caption = 'Antipodes E / NZ'
OnClick = PoleMenuItemClick
end
object MenuItem1: TMenuItem
Tag = 2
Caption = 'Start North, Dest South'
OnClick = PoleMenuItemClick
end
object MenuItem2: TMenuItem
Tag = 3
Caption = 'Dest North, Start South'
OnClick = PoleMenuItemClick
end
object MenuItem3: TMenuItem
Tag = 4
Caption = 'Start North, Dest free'
OnClick = PoleMenuItemClick
end
object MenuItem4: TMenuItem
Tag = 5
Caption = 'Start South, Dest free'
OnClick = PoleMenuItemClick
end
object MenuItem5: TMenuItem
Tag = 6
Caption = 'Dest North, Start free'
OnClick = PoleMenuItemClick
end
object MenuItem6: TMenuItem
Tag = 7
Caption = 'Dest South, Start Free'
OnClick = PoleMenuItemClick
end
end
object ImageList: TImageList
Height = 24
Scaled = True
Width = 24
Left = 432
Top = 40
Bitmap = {
4C7A0200000018000000180000003B0700000000000078DACD566D6C54551A3E
2DADA4C114C1C50F3E5AB4884BA5441713547691163F881451A844851A2BD568
94862EFC80988E49811A7E18D94D2C89BF201A49955ADDD276415A2A0B65DB5A
ECCED0292DED0CC5763A9D99FB7D673A1F9D79F63DF78EAED9CD2D1430D9499E
E4E4DEF33EE7BDCFFBCE7B1E000C93606262229D904DF803E18F49F075762291
48C735E2AD40B1D3A2D1E83255536D82229CB60BF6E1165F4B98C31EB00F8BAA
D8120C052B684F1EDF3B55FE6030582C6BB2BDDE571F29BC5C88BC9E3CE4D873
90F3630EF22EE4A1D05E88E323C723B22ADB69EF9629E49DA1EBFA9F3B840E6C
746F44BA331DAC87813908FF22FC4068279C6348FF3E1D9B7EDC848EB10EF018
1E7B2D4D42E3A1975DB26B74B56B3552FA52C0FA89AB8FD04BB848E84E9E719E
708621B52515F9EDF9700BEED15028B47932AD78CD4893536557CB1206F70071
B892B89C3CC34EE84A7EC359C2698694EF52B0C3BE23A1A8CA49E2C8B2E2D783
7A69DD581DE65D9A67E6CC79AF323C36F618BED0BF801017204C08F85CFC1C2B
9C2B0C8D582BE13B8679CDF350375497A05A6CB3E2A73EA9DD3DB41B698E34B0
4B1437C8B06078011C3107FEFB670FD931FF87F960DFD3BE530C694D69D8EDD8
0D5111BFB2E277492E77517F9159472733B47F5F781F56BF3D57F698F9133F6B
6478A9FD25B804D7A0157F8FD8132EE82900BB90D499FAA6516FB4E46F141B0D
FDB93E9C7FCDD935E8F1F784ADF87BC55EE5D98BCF827532F30CFA8ECFA4CF2C
F98F788F80B5D0BE93840686B5E7D6E252E0926CC53F228D74BFDEFB7AC2E8BD
0EB30F370C6EB0E45FEF586F6AF377423D4349674962441CB960C5AF69DABE6A
773532CF659ABDC1CFF927C307C31F6028328448226260283C049BDB06D64CEF
4F98B9673664A2BAAF1AC45169C51F8944FEE40C383D2B3B579A7DF10FB3C733
DA3250E028C0CEC19DD839B01305DD05C868CD30756934735FD9BA124E9FD3C3
39ACF8E3F17826CDB343C7AE1E8BA536A79ADA9E4EF6786B72DD9CACE789FF70
A77E9B8A5A776D8CC7728EC966442C165B48DF78A2B2B712B79FBADDE439F92B
70AD9B4C4DD8DF1866D4CF40A5BD92EB728262B3AF67C6D1BEE557C42B3F955E
28C5F4A6E926D7CF386EF2B26F19A67F331D25ED257CF68CF098A9CC6741144A
2EFA2E4AF967F20D2E03DF24F135A1966155F32A38C61CB2244BA5539DFFA4E3
6DA467D94060405ADCB418EC1833F19589450D8BD0EFEF97694F39DF7B237718
C5CDA2B9FE979A811A64D567817DC90CF0F5D181A3D074EDAF342F67DFE81D99
3C638E5FF6B71D701CC0CCBA99C8FC3A1355F62AD0B30E7AF7BB9BE1FED5BDF0
7B9A8BE76DDDB6784577459CEEDE767A967B2BB893F75A2ACDF5755EC9EBE2E0
6BFEEC56F127CFB88D788B38F8FA56F1FEE6FE47D56C92A49E1E18D086BBBAB4
7067A716EEEF578725496B09064337E57F1445B3B7B7EB91FDFB83D8BE3D8837
DED0B1658B86E262153B76C868695122B2ACDD90FFE9EBD370F060082525116C
DD1AC12BAF8C63D3A6109E7B4E47418186C71F57B07CB984B23209DDDDCAF5FB
9FD0F8CB1E8F365A55358E37DF8CE2EDB7A378EBADA871CEABAF86F1E28B21AC
5DAB63D52A95F8652C5D2AD2D9228686D4EBF23FA4C9A9C3874389D2D228DE7B
2F86F2F23869318177DE8919676CDE3C8EC2C220D6ACD1B0628582BC3C098B16
09B0D9C404C54EEE7FF460695B9B8E77DF0D1B3973DE3D7B12A8AE06BABA1208
06417318686D9D205D4278E209050F3F2C61F16281BE258086067952FF238A6A
ED9123BC86E3D8B62D4A358D61FFFE38BCDEFFBD7B5DAE385E784133F81F7C50
4076B61F7BF78A1045CDD2FF0C0F6BEE0F3FD4515414C26BAF458C6FA8AF8F5B
DEEF870E8DFF92FFDCB97ED22F00B75BB1F43F83835A78D72E1DEBD7070D9DB7
6E0DC36E4F58F29F3D1BFB45FF7BEFF563C3063F9C4EC5D2FFB85C9AB26B9786
679ED1F1FCF341A31FCF9CB1CEBFBE3E82DC5C11F7DD17C05D77F9B071A31F7D
7DAAA5FFF17AB5EEBD7BD504EFBDA79FD68D5EDFB72F6CC9BF7DBB6E68B36041
0077DE39463D26243C1E7552FF5353A360F56AD9E80D7E4E7EBE86C3872354E3
046231201A053C9E043EF9649CEA2A62E1C200EEB9C787F9F3C7F0E9A7E235FD
CFE5CBAAA7B858C6238FC878F451997A5CC6934F2AD4B33A3EFE781C1F7D14A2
DEE27D63EA3277AECFC8FDA9A77CE8ED55AEED7F54FD505393125BB244C0430F
8946FD962D33B174A984254B443CF08060E4FD33F7CC995ED4D549311E7BBDFE
E7E041896A173072CCC909E0FEFBCD755656C0E8C5BBEFF661D6AC31AAAB97FE
23C294FD0FCD939FCACB45E2F353EFF90C8D39E79C393ECC9E3D86CC4C2FEEB8
C38BD252DEF3EAD4FD8F2096389DAAB46E9D9FBE9FF3999C3366789191318AF4
F451AACB181C0E459624F9C6FC8FAA970D0E2A526EAE1769691E4C9BE6414A8A
078C8DD07F6A14FDFD8A4C7B6EDEFFD4709D460D5E0EBE3E7A94F7A27E6BFC8F
5F6D3B7040209D3CA49107555502E8D9ADF53FA27ADE660BC42B2A8438CDC8DF
C6FF78551707DDEBFF37FEE7DF82F5EDA1
}
end
end

View File

@ -0,0 +1,300 @@
unit main;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls,
ComCtrls, Menus, Spin,
mvMapViewer, mvTypes, mvGPSObj, mvPluginCommon,
mvGeoMath, mvPlugins, mvGreatCirclePainterPlugin;
type
{ TMainForm }
TMainForm = class(TForm)
btnPresetPolar: TButton;
btnPreseEquator: TButton;
btnPresetPorto: TButton;
btnPresetLongestSeaWay: TButton;
btnLongestEarthWay: TButton;
cbCyclicMap: TCheckBox;
cgOptions: TCheckGroup;
clbOrthodromePenColor: TColorButton;
clbGreatCirclePenColor: TColorButton;
cbZOrder: TComboBox;
gbPresets: TGroupBox;
gbOrthodromePen: TGroupBox;
GroupBox3: TGroupBox;
ImageList: TImageList;
lblInfo: TLabel;
lblZOrder: TLabel;
Label2: TLabel;
lblOrthodromePenWidth: TLabel;
lblGreatCirclePenWidth: TLabel;
MapView: TMapView;
MenuItem1: TMenuItem;
MenuItem2: TMenuItem;
MenuItem3: TMenuItem;
MenuItem4: TMenuItem;
MenuItem5: TMenuItem;
MenuItem6: TMenuItem;
MenuItem7: TMenuItem;
MvPluginManager: TMvPluginManager;
MvPluginManagerDraggableMarkerPlugin1: TDraggableMarkerPlugin;
ParamsPanel: TPanel;
MapPanel: TPanel;
PolarPopupMenu: TPopupMenu;
seOrthodromePenWidth: TSpinEdit;
seGreatCirclePenWidth: TSpinEdit;
StatusBar: TStatusBar;
tbSegmentLength: TTrackBar;
procedure btnPresetPolarClick(Sender: TObject);
procedure btnPreseEquatorClick(Sender: TObject);
procedure btnPresetPortoClick(Sender: TObject);
procedure btnPresetLongestSeaWayClick(Sender: TObject);
procedure btnLongestEarthWayClick(Sender: TObject);
procedure cbCyclicMapChange(Sender: TObject);
procedure cgOptionsItemClick(Sender: TObject; Index: integer);
procedure clbOrthodromePenColorColorChanged(Sender: TObject);
procedure clbGreatCirclePenColorColorChanged(Sender: TObject);
procedure cbZOrderChange(Sender: TObject);
procedure FormActivate(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure PoleMenuItemClick(Sender: TObject);
procedure MvPluginManagerDraggableMarkerPlugin1DraggableMarkerMovedEvent(
Sender: TDraggableMarkerPlugin; AMarker: TGPSPoint;
AOrgPosition: TRealPoint);
procedure seOrthodromePenWidthChange(Sender: TObject);
procedure seGreatCirclePenWidthChange(Sender: TObject);
procedure tbSegmentLengthChange(Sender: TObject);
private
FActivated: Boolean;
FGreatCirclePainterPlugin : TGreatCirclePainterPlugin;
FStartMarker : TGpsPointOfInterest;
FDestinationMarker : TGpsPointOfInterest;
procedure OnGreatCirclePainterGetCoords(Sender : TGreatCirclePainterPlugin;
var FStart, FDestination : TRealPoint);
procedure SetMarkerPositions(const AStartLat, AStartLon, ADestLat, ADestLon : Double);
procedure OnGreatCirclePainterPluginChange(Sender : TObject);
public
end;
var
MainForm: TMainForm;
implementation
{$R *.lfm}
{ TMainForm }
procedure TMainForm.FormCreate(Sender: TObject);
function AddTraditionalMarker(const ALat, ALon: Double;
ACaption: String; const AImgIndex : Integer = -1) : TGpsPointOfInterest;
var
gpsPt: TGpsPointOfInterest;
begin
Result := Nil;
gpsPt := TGpsPointOfInterest.Create(ALon,ALat);
try
gpsPt.Name := ACaption;
gpsPt.ImageIndex := AImgIndex;
MapView.GPSItems.Add(gpsPt, 100);
Result := gpsPt;
gpsPt := Nil;
finally
if Assigned(gpsPt) then
gpsPt.Free;
end;
end;
begin
MapView.Active := true;
FGreatCirclePainterPlugin := TGreatCirclePainterPlugin.Create(MvPluginManager);
MvPluginManager.PluginList.Add(FGreatCirclePainterPlugin);
FGreatCirclePainterPlugin.ZOrder := gcpzCanvas;
FGreatCirclePainterPlugin.OnGetStartAndDestinationCoords:=@OnGreatCirclePainterGetCoords;
FGreatCirclePainterPlugin.OnChange := @OnGreatCirclePainterPluginChange;
FGreatCirclePainterPlugin.MapView := MapView;
FGreatCirclePainterPlugin.GreatCirclePen.Color:= clbGreatCirclePenColor.ButtonColor;
FGreatCirclePainterPlugin.GreatCirclePen.Style:= psSolid;
FGreatCirclePainterPlugin.GreatCirclePen.Width:= seGreatCirclePenWidth.Value;
FGreatCirclePainterPlugin.OrthodromePen.Color:= clbOrthodromePenColor.ButtonColor;
FGreatCirclePainterPlugin.OrthodromePen.Style:= psSolid;
FGreatCirclePainterPlugin.OrthodromePen.Width:= seOrthodromePenWidth.Value;
FStartMarker := AddTraditionalMarker(41.1578,-8.6333, 'Start',0);
FDestinationMarker := AddTraditionalMarker(10.6722,-61.5333, 'Destination',1);
end;
procedure TMainForm.SetMarkerPositions(const AStartLat, AStartLon, ADestLat, ADestLon : Double);
begin
FStartMarker.MoveTo(AStartLon, AStartLat);
FDestinationMarker.MoveTo(ADestLon, ADestLat);
MapView.Invalidate;
end;
procedure TMainForm.OnGreatCirclePainterPluginChange(Sender: TObject);
var
s : String;
begin
s := Format('%1.3f',[FGreatCirclePainterPlugin.OrthodromeDistance / 1000.0]);
StatusBar.Panels[1].Text := s;
s := Format('%1.3f',[(EARTH_CIRCUMFERENCE-FGreatCirclePainterPlugin.OrthodromeDistance) / 1000.0]);
StatusBar.Panels[3].Text := s;
s := Format('%1.6f:%1.6f',[FGreatCirclePainterPlugin.StartLat,FGreatCirclePainterPlugin.StartLon]);
StatusBar.Panels[5].Text := s;
s := Format('%1.6f:%1.6f',[FGreatCirclePainterPlugin.DestinationLat,FGreatCirclePainterPlugin.DestinationLon]);
StatusBar.Panels[7].Text := s;
s := Format('%1.2f°',[FGreatCirclePainterPlugin.InitialBearing]);
StatusBar.Panels[9].Text := s;
StatusBar.Panels[11].Text := IntToStr(FGreatCirclePainterPlugin.GreatCirclePointsCount);
end;
procedure TMainForm.PoleMenuItemClick(Sender: TObject);
begin
if Sender is TMenuItem then
begin
case TMenuItem(Sender).Tag of
1 : SetMarkerPositions(42.496909,-7.026229,-42.496909,172.973771); // Antipodes Spain / New Zealand
2 : SetMarkerPositions(90.0,20.0,-90.0,-90.0); // Both Poles, Start North
3 : SetMarkerPositions(-90.0,40.0,90.0,-100.0); // Both Poles, Start South
4 : SetMarkerPositions(90.0,60.0,48.8582300,2.2945500); // Start North, Dest free
5 : SetMarkerPositions(-90.0,80.0,22.2708100,114.1497900); // Start South, Dest free
6 : SetMarkerPositions(-33.8707000,151.2082800,90.0,-110.0); // Dest North, Start free
7 : SetMarkerPositions(43.6439500,-79.3884000,-90.0,-120.0); // Dest South, Start free
end;
end;
end;
procedure TMainForm.cbCyclicMapChange(Sender: TObject);
begin
MapView.Cyclic := cbCyclicMap.Checked;
MapView.Invalidate;
end;
procedure TMainForm.cgOptionsItemClick(Sender: TObject; Index: integer);
var
opt : TGreatCirclePainterOptions = [];
begin
if cgOptions.Checked[0] then
Include(opt,gcpoMarkStart);
if cgOptions.Checked[1] then
Include(opt,gcpoMarkCenter);
if cgOptions.Checked[2] then
Include(opt,gcpoMarkDestination);
FGreatCirclePainterPlugin.Options := opt;
end;
procedure TMainForm.clbOrthodromePenColorColorChanged(Sender: TObject);
begin
FGreatCirclePainterPlugin.OrthodromePen.Color := clbOrthodromePenColor.ButtonColor;
end;
procedure TMainForm.clbGreatCirclePenColorColorChanged(Sender: TObject);
begin
FGreatCirclePainterPlugin.GreatCirclePen.Color := clbGreatCirclePenColor.ButtonColor;
end;
procedure TMainForm.btnPresetPolarClick(Sender: TObject);
begin
PolarPopupMenu.PopUp;
end;
procedure TMainForm.btnPreseEquatorClick(Sender: TObject);
begin
FStartMarker.MoveTo(0.0,0.0);
FDestinationMarker.MoveTo(100.0,0.0);
MapView.Invalidate;
end;
procedure TMainForm.btnPresetPortoClick(Sender: TObject);
begin
FStartMarker.MoveTo(-8.6333,41.1578);
FDestinationMarker.MoveTo(-61.5333,10.6722);
MapView.Invalidate;
end;
procedure TMainForm.btnPresetLongestSeaWayClick(Sender: TObject);
begin
FStartMarker.MoveTo(DMSToDeg(66,40,0),DMSToDeg(25,17,0));
FDestinationMarker.MoveTo(DMSToDeg(162,14,0),DMSToDeg(58,37,0));
MapView.Invalidate;
end;
procedure TMainForm.btnLongestEarthWayClick(Sender: TObject);
begin
// 2433 N, 11838 E
// 372 N, 855 W
FStartMarker.MoveTo(DMSToDeg(118,38,0),DMSToDeg(23,33,0));
FDestinationMarker.MoveTo(-DMSToDeg(8,55,0),DMSToDeg(37,2,0));
MapView.Invalidate;
end;
procedure TMainForm.cbZOrderChange(Sender: TObject);
begin
case cbZOrder.ItemIndex of
0 : FGreatCirclePainterPlugin.ZOrder := gcpzCanvas;
1 : FGreatCirclePainterPlugin.ZOrder := gcpzInFrontOfMarkers;
2 : FGreatCirclePainterPlugin.ZOrder := gcpzBehindMarkers;
end;
MapView.Invalidate;
end;
procedure TMainForm.FormActivate(Sender: TObject);
begin
if not FActivated then
begin
Constraints.MinHeight := gbPresets.Top + gbPresets.Height +
ParamsPanel.BorderSpacing.Around * 2 + Statusbar.Height;
Constraints.MinWidth := ParamsPanel.Width + ParamsPanel.BorderSpacing.Around * 2;
if Height < Constraints.MinHeight then
Height := 0;
FActivated := true;
end;
end;
procedure TMainForm.MvPluginManagerDraggableMarkerPlugin1DraggableMarkerMovedEvent
(Sender: TDraggableMarkerPlugin; AMarker: TGPSPoint; AOrgPosition: TRealPoint
);
begin
FGreatCirclePainterPlugin.SetStartAndDestination(
FStartMarker.RealPoint, FDestinationMarker.RealPoint
);
end;
procedure TMainForm.seOrthodromePenWidthChange(Sender: TObject);
begin
FGreatCirclePainterPlugin.OrthodromePen.Width := seOrthodromePenWidth.Value;
end;
procedure TMainForm.seGreatCirclePenWidthChange(Sender: TObject);
begin
FGreatCirclePainterPlugin.GreatCirclePen.Width := seGreatCirclePenWidth.Value;
end;
procedure TMainForm.tbSegmentLengthChange(Sender: TObject);
const
SegmentLengths : array[0..10] of Integer = (
1,2,5,10,20,30,50,70,100,150,200
);
begin
FGreatCirclePainterPlugin.SegmentLength := SegmentLengths[tbSegmentLength.Position];
MapView.Invalidate;
end;
procedure TMainForm.OnGreatCirclePainterGetCoords(
Sender: TGreatCirclePainterPlugin; var FStart, FDestination: TRealPoint);
begin
FStart := FStartMarker.RealPoint;
FDestination := FDestinationMarker.RealPoint;
end;
end.

View File

@ -8,7 +8,7 @@
<Version Value="11"/>
<SearchPaths>
<IncludeFiles Value="source"/>
<OtherUnitFiles Value="source;source/addons/plugins;source/addons/plugins/spreadmarkers;source/addons/plugins/grids;source/addons/plugins/scale"/>
<OtherUnitFiles Value="source;source/addons/plugins;source/addons/plugins/spreadmarkers;source/addons/plugins/grids;source/addons/plugins/scale;source/addons/plugins/greatcircle"/>
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<Other>
@ -23,7 +23,7 @@
FPC 3.2.0 or newer required."/>
<License Value="Modified LGPL with linking exception, like FreePascal RTL/FCL and Lazarus LCL"/>
<Version Minor="2" Release="7"/>
<Files Count="33">
<Files Count="34">
<Item1>
<Filename Value="source/mvcache.pas"/>
<UnitName Value="mvCache"/>
@ -157,6 +157,10 @@ FPC 3.2.0 or newer required."/>
<Filename Value="source/mvengine_mapreg.inc"/>
<Type Value="Include"/>
</Item33>
<Item34>
<Filename Value="source/addons/plugins/greatcircle/mvgreatcirclepainterplugin.pas"/>
<UnitName Value="mvGreatCirclePainterPlugin"/>
</Item34>
</Files>
<CompatibilityMode Value="True"/>
<RequiredPkgs Count="2">

View File

@ -15,7 +15,7 @@ uses
mvMapViewerPathEditForm, mvMapViewerPathEditDsgForm, mvDLECache,
mvPluginEditors, mvClassRegistration, mvPluginCommon, mvPlugins,
mvspreadmarker_plugin, uInactivityAlarmTimer, mvMapGridPlugin,
mvMapScalePlugin, LazarusPackageIntf;
mvMapScalePlugin, mvGreatCirclePainterPlugin, LazarusPackageIntf;
implementation

View File

@ -69,6 +69,19 @@ begin
Result := round(IntPower(2, AZoomLevel));
end;
{ Protected version of arcin which does not crash when the argument is outside
domain due to round-off error. }
function SafeArcsin(x: Double): Double;
begin
if x >= +1.0 then
Result := pi * 0.5
else
if x <= -1.0 then
Result := -pi * 0.5
else
Result := arcsin(x);
end;
{ Calculation of distance on a sphere
https://stackoverflow.com/questions/73608975/pascal-delphi-11-formula-for-distance-in-meters-between-two-decimal-gps-point
}
@ -76,24 +89,24 @@ end;
function HaversineAngle(Lat1, Lon1, Lat2, Lon2: Double): Double;
var
latFrom, latTo, lonDiff: Double;
dx, dy, dz, arg: Double;
sinLatFrom, cosLatFrom: Double;
sinLatTo, cosLatTo: Double;
sinLonDiff, cosLonDiff: Double;
dx, dy, dz: Double;
begin
lonDiff := Lon1 - Lon2;
latFrom := Lat1;
latTo := Lat2;
dz := sin(latFrom) - sin(latTo);
dx := cos(lonDiff) * cos(latFrom) - cos(latTo);
dy := sin(lonDiff) * cos(latFrom);
SinCos(latFrom, sinLatFrom, cosLatFrom);
SinCos(latTo, sinLatTo, cosLatTo);
SinCos(lonDiff, sinLonDiff, cosLonDiff);
arg := sqrt(sqr(dx) + sqr(dy) + sqr(dz)) / 2.0;
if arg >= 1.0 then
Result := pi
else
if arg <= -1.0 then
Result := -pi
else
Result := arcsin(arg) * 2.0;
dz := sinlatFrom - sinlatTo;
dx := coslonDiff * coslatFrom - coslatTo;
dy := sinlonDiff * coslatFrom;
Result := SafeArcsin(sqrt(sqr(dx) + sqr(dy) + sqr(dz)) / 2.0) * 2.0;
end;
// Angles in degrees
@ -167,12 +180,20 @@ end;
function CalcBearing(Lat1, Lon1, Lat2, Lon2: Double): Double;
var
latFrom, latTo, lonDiff: Double;
sin_LatFrom, cos_LatFrom: Double;
sin_LatTo, cos_LatTo: Double;
sin_LonDiff, cos_LonDiff: Double;
begin
lonDiff := DegToRad(Lon2 - Lon1);
latFrom := DegToRad(Lat1);
latTo := DegToRad(Lat2);
Result := ArcTan2(Sin(lonDiff) * Cos(latTo),
Cos(latFrom) * Sin(latTo) - Sin(latFrom) * Cos(latTo) * Cos(lonDiff));
SinCos(lonDiff, sin_LonDiff, cos_LonDiff);
SinCos(latFrom, sin_LatFrom, cos_LatFrom);
SinCos(latTo, sin_LatTo, cos_LatTo);
Result := ArcTan2(sin_LonDiff * cos_LatTo,
cos_LatFrom * sin_LatTo - sin_LatFrom * cos_LatTo * cos_LonDiff);
Result := RadToDeg(Result);
if Result < 0.0 then
Result := Result + 360.0;
@ -186,16 +207,25 @@ procedure CalcLatLon(const Lat1, Lon1, ADist, ABearing: Double; out Lat2,
Lon2: Double);
var
latFrom, lonFrom, brng, aD: Double;
sin_LatFrom, cos_LatFrom: Double;
sin_LonFrom, cos_LonFrom: Double;
sin_brng, cos_brng: Double;
sin_aD, cos_aD: Double;
begin
latFrom := DegToRad(Lat1);
lonFrom := DegToRad(Lon1);
brng := DegToRad(ABearing);
aD := ADist / EARTH_EQUATORIAL_RADIUS;
Lat2 := ArcSin(Sin(latFrom) * Cos(aD) + Cos(latFrom) * Sin(aD) * Cos(brng));
Lon2 := lonFrom + ArcTan2(Sin(brng) * Sin(aD) * Cos(latFrom),
Cos(aD) - Sin(latFrom) * Sin(Lat2));
SinCos(latFrom, sin_LatFrom, cos_LatFrom);
SinCos(lonFrom, sin_lonFrom, cos_lonFrom);
SinCos(brng, sin_brng, cos_brng);
SinCos(aD, sin_aD, cos_aD);
Lat2 := SafeArcSin(sin_LatFrom * cos_aD + cos_LatFrom * sin_aD * cos_brng);
Lon2 := lonFrom + ArcTan2(sin_brng * sin_aD * cos_latFrom, cos_aD - sin_latFrom * Sin(Lat2));
Lat2 := RadToDeg(Lat2);
Lon2 := NormalizeLon(RadToDeg(Lon2));
Lon2 := {%H-}NormalizeLon(RadToDeg(Lon2));
end;
{ Calculate midpoint Lat,Lon by given start point Lat1,Lon1 and end point
@ -205,6 +235,7 @@ procedure CalcMidpoint(const Lat1, Lon1, Lat2, Lon2: Double; out Lat,
Lon: Double);
var
latFrom, lonDiff, latTo, lonTo, Bx, By: Double;
sin_latFrom, cos_latFrom: Double;
begin
lonDiff := DegToRad(Lon2 - Lon1);
latFrom := DegToRad(Lat1);
@ -212,10 +243,13 @@ begin
lonTo := DegToRad(Lon2);
Bx := Cos(latTo) * Cos(lonDiff);
By := Cos(latTo) * Sin(lonDiff);
Lat := ArcTan2(Sin(latFrom) + Sin(latTo), Sqrt(Sqr(Cos(latFrom) + Bx) + Sqr(By)));
Lon := lonTo + ArcTan2(By, Cos(latFrom) + By);
SinCos(latFrom, sin_latFrom, cos_latFrom);
Lat := ArcTan2(sin_latFrom + Sin(latTo), Sqrt(Sqr(cos_latFrom + Bx) + Sqr(By)));
Lon := lonTo + ArcTan2(By, cos_latFrom + By);
Lat := RadToDeg(Lat);
Lon := NormalizeLon(RadToDeg(Lon));
Lon := {%H-}NormalizeLon(RadToDeg(Lon));
end;
{ Calculate intermediate point Lat,Lon by given start point Lat1,Lon1, end point
@ -227,6 +261,9 @@ procedure CalcIntermedPoint(const Lat1, Lon1, Lat2, Lon2, AFrac: Double; out
var
latFrom, lonFrom, latTo, lonTo: Double;
A, B, aD, X, Y, Z: Double;
sin_latFrom, cos_latFrom: Double;
sin_lonFrom, cos_lonFrom: Double;
sin_latTo, cos_latTo: Double;
begin
if (Lat1 = Lat2) and (Lon1 = Lon2) or (AFrac < 0.001) then
begin
@ -245,15 +282,20 @@ begin
lonFrom := DegToRad(Lon1);
latTo := DegToRad(Lat2);
lonTo := DegToRad(Lon2);
SinCos(latFrom, sin_LatFrom, cos_LatFrom);
SinCos(lonFrom, sin_LonFrom, cos_lonFrom);
SinCos(latTo, sin_latTo, cos_latTo);
A := Sin((1.0 - AFrac) * aD) / Sin(aD);
B := Sin(AFrac * aD) / Sin(aD);
X := A * Cos(latFrom) * Cos(lonFrom) + B * Cos(latTo) * Cos(lonTo);
Y := A * Cos(latFrom) * Sin(lonFrom) + B * Cos(latTo) * Sin(lonTo);
Z := A * Sin(latFrom) + B * Sin(latTo);
X := A * cos_latFrom * cos_lonFrom + B * cos_latTo * Cos(lonTo);
Y := A * cos_latFrom * sin_lonFrom + B * cos_latTo * Sin(lonTo);
Z := A * sin_latFrom + B * sin_latTo;
Lat := ArcTan2(Z, Sqrt(Sqr(X) + Sqr(Y)));
Lon := ArcTan2(Y, X);
Lat := RadToDeg(Lat);
Lon := NormalizeLon(RadToDeg(Lon));
Lon := {%H-}NormalizeLon(RadToDeg(Lon));
end;
function NormalizeLon(const Lon: Double): Double;