LazMapViewer: Remove duplicate distance calculation in TGPSPoint. Had to move some general-purpose geo-math routines to a new units mvGeoMath. Old functions kept in mvEngine, but marked as deprecated.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@9337 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2024-04-15 23:11:21 +00:00
parent e545177bc3
commit 93167ada8b
20 changed files with 820 additions and 980 deletions

View File

@ -7,8 +7,8 @@ interface
uses
Classes, SysUtils, LazFileUtils,
LCLIntf, Forms, Controls, Graphics, Dialogs, StdCtrls,
ExtCtrls, Grids, Buttons, mvMapViewer, mvTypes, mvEngine, Types, mvGpsObj,
mvDrawingEngine;
ExtCtrls, Grids, Buttons, Types,
mvMapViewer, mvTypes, mvGeoMath, mvEngine, mvGpsObj, mvDrawingEngine;
type
{ TMainForm }

View File

@ -6,9 +6,10 @@ object MainForm: TMainForm
Caption = 'Flights - LazMapViewer'
ClientHeight = 662
ClientWidth = 942
ShowHint = True
LCLVersion = '3.99.0.0'
OnCreate = FormCreate
OnDestroy = FormDestroy
ShowHint = True
object MapView: TMapView
Left = 0
Height = 662
@ -27823,192 +27824,33 @@ object MainForm: TMainForm
MapCenter.Longitude = -87.9081388888889
MapCenter.Latitude = 41.9769444444444
POIImage.Data = {
36170000424D36170000000000003600000028000000200000002E0000000100
2000000000000017000064000000640000000000000000000000FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FD560000FC4CFFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000F1110000FEE70000FEE10000
EA0CFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FE9A0000FFFF0000FFFF0000
FE8FFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF000000FB3E0000FEFE0000FFFF0000FFFF0000
FEFC0000FB35FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF000000DB070000FED70000FFFF0000FFFF0000FFFF0000
FFFF0000FECF0000C004FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF000000FE800000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FD75FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF000000F9290000FEF90000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FEF50000F821FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00000080020000FEC30000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FEB8FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF000000FD660000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FD5CFFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000
F5180000FEEF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FEEA0000F213FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000
FEAA0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FE9FFFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FC4C0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEFE0000FC42FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000EA0C0000FEE10000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEDB0000E309FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FE900000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FE86FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FB350000FEFC0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEFA0000
FA2DFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF000000C0040000FED00000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FEC700008002FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF000000FD770000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FD6CFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF000000F8230000FEF80000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FEF50000F61CFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF000000FECF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FEC8FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF000000FE820000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FD79FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF000000F9270000FEFD0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FEFD0000F822FFFFFF00FFFFFF00FFFFFF00FFFF
FF000000FEA70000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FEF70000FEB50000FE810000FD670000FD670000
FE810000FEB50000FEF70000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FEA7FFFFFF00FFFFFF00FFFFFF000000
FB340000FEFE0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FEFD0000FE920000F213FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF000000F2130000FE920000FEFD0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FEFE0000FB34FFFFFF00FFFFFF000000
FEAC0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FEF60000FC4DFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF000000FC4D0000FEF60000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEACFFFFFF000000F3150000
FEFA0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEFE0000
FC54FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FC540000FEFE0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEFA0000F3150000FD610000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEA2FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FEA20000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FD610000FEA00000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEFD0000F823FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000F8230000FEFD0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEA00000FECD0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEC5FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FEC50000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FECD0000FEEC0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FE8CFFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FE8C0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEEC0000FEFA0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FD72FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FD720000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEFA0000FEF70000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FD75FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FD750000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEF70000FEE80000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FE95FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FE950000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEE80000FEC50000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FED5FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FED50000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEC50000FE940000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FB37FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FB370000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FE940000FC520000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEC00000
8002FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00000080020000FEC00000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FC520000E60A0000
FEF20000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FD7EFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FD7E0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEF20000E60AFFFFFF000000
FE950000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FEFE0000FD7F00008002FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00000080020000FD7F0000FEFE0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FE95FFFFFF00FFFFFF000000
F8200000FEF80000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FEC30000FB3DFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF000000FB3D0000FEC30000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FEF80000F820FFFFFF00FFFFFF00FFFF
FF000000FE830000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FEDB0000FE9C0000FD7E0000FD7E0000
FE9C0000FEDB0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FE83FFFFFF00FFFFFF00FFFFFF00FFFF
FF000000DB070000FECC0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FECC0000DB07FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF000000F71F0000FEE40000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FEE40000F71FFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF000000FA2B0000FEE50000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FEE50000FA2BFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF000000F8210000FECD0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FECE0000F821FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000DB070000FE850000FEF90000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEF90000FE850000
DB07FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000F8230000
FE990000FEF30000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FEF30000FE990000F823FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF000000EA0C0000FD570000FE980000FEC70000FEEB0000FEF90000FEF90000
FEEB0000FEC70000FE980000FD570000EA0CFFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00
4503000089504E470D0A1A0A0000000D49484452000000200000002E08060000
0049701B840000030C49444154789CB5983B68145114867F4410C54644B0107C
152262A7A4D2C2DA3422DAD8D868631510B1D1688855409104A2088634819056
B00C488845626C82C55662A29204C40DC96ED66CD6F38F77976B76E6DCC7CC14
077667CEF9FFEF3EE6CC03AD560B3EB1021C6C023777805189598955892D13AB
E6D8287398EBABEB4CA803A745F895C47A92EE11CC650D6BA3012AC03E1119E4
087D8D5340383B83D40A0210F29352381F6B9C02F2899A5E000DE09C142C1765
6E412C535B05A801C72571A968730B62891EA90066CDE7CA32B720E6EC3DD101
901303012215893772C93D62F0378F05D40FFC07201BE4841CAC7B147EF8035C
CADAD13CC71C0F9D3A3D3B007260C451B023237DDC0FEC715DD7CC612E6B1C9A
2309C077E080FCA96AC99C66DFCED68EA44607A8D21BDBC03547E2B4CFC8D366
82B59A36BD39FDC35A92AC6B4FA8B9B5277A1C831B26C08C92B0186B6E5D5D8B
8AFE0C137EBA364A4E80CC0D4E6F26D494CD773F2F40A2910D5023404301E82B
00A04F016810604D49182A60098614FD3568B7DD6483E607D036F93C13C69484
A6CF534D5698A7A9A6A23FC635BAEDB856C7738C7E5CD3A637D68123DA46644F
978E753DD49C35DAFD809EF46E934E39666153047B03CC7B59E3D09C6AFDFB97
B4CC0B1E77AFA6C4CB2A7038CB98E798A3AD7B7B56E9D90130B330A115D9CD43
E29DC41359C3BB0CFE36C7329BDA2E8D89B66F07600B38E3222F22CC9575AA0B
C0CCC2D3B20164C61EDA9E5DEB28100B258EFEE36EBF2E00D33C9CCF8711E61B
9BC0312780B9813C2861EAEFA579A5024C037B85F87381A39FCD7AAC4B0530BD
E1A2146E1760DE9057B2F3593E99006643BE2800E099E6A102F04383087CCD61
5EF906EC8F06307DFD6AA4396F62575CFA4E00B3149311006F7DB4BD003680A3
22F82BC07C45BB69050398DE7027E09ABFE5ABEB0D605EB57CDE7CDFFB6A0601
30E48E7956FB68C5769BF52DA81000B321FB95A90F7E910906309F72BEA48C7E
812DBC740086B4E9CBF6231C5B365B778C561480598AD716C0F3589D6880DFC0
2131FEC1561DF26DB83000866CBA1B49ABCEA1F117D2B9A057721AC774000000
0049454E44AE426082
}
POIImages = ilPOIs
POIImagesWidth = 32
@ -28031,8 +27873,8 @@ object MainForm: TMainForm
TabOrder = 1
object PgData: TTabSheet
Caption = 'Tracking'
ClientHeight = 627
ClientWidth = 312
ClientHeight = 634
ClientWidth = 310
object ZoomTrackBar: TTrackBar
AnchorSideLeft.Control = PgData
AnchorSideTop.Control = LblZoom
@ -28041,12 +27883,12 @@ object MainForm: TMainForm
AnchorSideRight.Side = asrBottom
Left = 2
Height = 40
Top = 23
Width = 308
Top = 21
Width = 306
Min = 1
OnChange = ZoomTrackBarChange
Position = 4
TickMarks = tmBoth
OnChange = ZoomTrackBarChange
Anchors = [akTop, akLeft, akRight]
BorderSpacing.Left = 2
BorderSpacing.Right = 2
@ -28056,9 +27898,9 @@ object MainForm: TMainForm
AnchorSideLeft.Control = PgData
AnchorSideTop.Control = PgData
Left = 6
Height = 17
Height = 15
Top = 6
Width = 43
Width = 35
BorderSpacing.Left = 6
BorderSpacing.Top = 6
Caption = 'Zoom:'
@ -28071,9 +27913,9 @@ object MainForm: TMainForm
AnchorSideRight.Control = PgData
AnchorSideRight.Side = asrBottom
Left = 6
Height = 73
Top = 71
Width = 300
Height = 70
Top = 69
Width = 298
Anchors = [akTop, akLeft, akRight]
AutoSize = True
BorderSpacing.Left = 6
@ -28081,17 +27923,17 @@ object MainForm: TMainForm
BorderSpacing.Right = 6
BorderSpacing.InnerBorder = 4
Caption = 'Mouse position'
ClientHeight = 54
ClientWidth = 296
ClientHeight = 50
ClientWidth = 294
TabOrder = 1
object lblMouseLon: TLabel
AnchorSideLeft.Control = gbMouse
AnchorSideTop.Control = lblMouseLat
AnchorSideTop.Side = asrBottom
Left = 8
Height = 17
Top = 29
Width = 68
Height = 15
Top = 27
Width = 54
BorderSpacing.Left = 8
BorderSpacing.Top = 4
Caption = 'Longitude'
@ -28101,9 +27943,9 @@ object MainForm: TMainForm
AnchorSideLeft.Control = gbMouse
AnchorSideTop.Control = gbMouse
Left = 8
Height = 17
Height = 15
Top = 4
Width = 57
Width = 43
BorderSpacing.Left = 8
BorderSpacing.Top = 4
BorderSpacing.Bottom = 8
@ -28116,10 +27958,10 @@ object MainForm: TMainForm
AnchorSideTop.Side = asrBottom
AnchorSideRight.Control = gbMouse
AnchorSideRight.Side = asrBottom
Left = 235
Height = 17
Top = 29
Width = 45
Left = 244
Height = 15
Top = 27
Width = 34
Alignment = taRightJustify
Anchors = [akTop, akRight]
BorderSpacing.Top = 4
@ -28132,10 +27974,10 @@ object MainForm: TMainForm
AnchorSideTop.Control = gbMouse
AnchorSideRight.Control = gbMouse
AnchorSideRight.Side = asrBottom
Left = 235
Height = 17
Left = 244
Height = 15
Top = 4
Width = 45
Width = 34
Alignment = taRightJustify
Anchors = [akTop, akRight]
BorderSpacing.Top = 4
@ -28152,25 +27994,25 @@ object MainForm: TMainForm
AnchorSideRight.Control = gbMouse
AnchorSideRight.Side = asrBottom
Left = 6
Height = 73
Top = 152
Width = 300
Height = 70
Top = 147
Width = 298
Anchors = [akTop, akLeft, akRight]
AutoSize = True
BorderSpacing.Top = 8
BorderSpacing.InnerBorder = 4
Caption = 'Center'
ClientHeight = 54
ClientWidth = 296
ClientHeight = 50
ClientWidth = 294
TabOrder = 2
object lblCenterLon: TLabel
AnchorSideLeft.Control = gbCenter
AnchorSideTop.Control = lblCenterLat
AnchorSideTop.Side = asrBottom
Left = 8
Height = 17
Top = 29
Width = 68
Height = 15
Top = 27
Width = 54
BorderSpacing.Left = 8
BorderSpacing.Top = 4
Caption = 'Longitude'
@ -28180,9 +28022,9 @@ object MainForm: TMainForm
AnchorSideLeft.Control = gbCenter
AnchorSideTop.Control = gbCenter
Left = 8
Height = 17
Height = 15
Top = 4
Width = 57
Width = 43
BorderSpacing.Left = 8
BorderSpacing.Top = 4
BorderSpacing.Bottom = 8
@ -28195,10 +28037,10 @@ object MainForm: TMainForm
AnchorSideTop.Side = asrBottom
AnchorSideRight.Control = gbCenter
AnchorSideRight.Side = asrBottom
Left = 235
Height = 17
Top = 29
Width = 45
Left = 244
Height = 15
Top = 27
Width = 34
Alignment = taRightJustify
Anchors = [akTop, akRight]
BorderSpacing.Top = 4
@ -28211,10 +28053,10 @@ object MainForm: TMainForm
AnchorSideTop.Control = gbCenter
AnchorSideRight.Control = gbCenter
AnchorSideRight.Side = asrBottom
Left = 235
Height = 17
Left = 244
Height = 15
Top = 4
Width = 45
Width = 34
Alignment = taRightJustify
Anchors = [akTop, akRight]
BorderSpacing.Top = 4
@ -28230,9 +28072,9 @@ object MainForm: TMainForm
AnchorSideRight.Side = asrBottom
AnchorSideBottom.Control = plInfo
Left = 6
Height = 231
Top = 233
Width = 298
Height = 246
Top = 225
Width = 296
Anchors = [akTop, akLeft, akRight, akBottom]
AutoFillColumns = True
BorderSpacing.Top = 8
@ -28248,12 +28090,12 @@ object MainForm: TMainForm
item
Title.Alignment = taCenter
Title.Caption = 'Latitude'
Width = 105
Width = 111
end
item
Title.Alignment = taCenter
Title.Caption = 'Longitude'
Width = 106
Width = 111
end>
FixedCols = 0
Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goRowSelect, goSmoothScroll]
@ -28262,8 +28104,8 @@ object MainForm: TMainForm
OnDblClick = sgTracksDblClick
ColWidths = (
70
105
106
111
111
)
end
object pnlFlightControl: TPanel
@ -28274,8 +28116,8 @@ object MainForm: TMainForm
AnchorSideBottom.Side = asrBottom
Left = 0
Height = 50
Top = 577
Width = 312
Top = 584
Width = 310
Anchors = [akLeft, akRight, akBottom]
BevelOuter = bvNone
ChildSizing.LeftRightSpacing = 5
@ -28288,13 +28130,13 @@ object MainForm: TMainForm
ChildSizing.Layout = cclLeftToRightThenTopToBottom
ChildSizing.ControlsPerLine = 99
ClientHeight = 50
ClientWidth = 312
ClientWidth = 310
TabOrder = 4
object btnPlay: TSpeedButton
Left = 5
Height = 40
Top = 5
Width = 65
Width = 66
Glyph.Data = {
36040000424D3604000000000000360000002800000010000000100000000100
2000000000000004000064000000640000000000000000000000000000000000
@ -28334,10 +28176,10 @@ object MainForm: TMainForm
OnClick = btnPlayClick
end
object btnPause: TSpeedButton
Left = 75
Left = 76
Height = 40
Top = 5
Width = 65
Width = 66
Glyph.Data = {
36040000424D3604000000000000360000002800000010000000100000000100
2000000000000004000064000000640000000000000000000000000000000000
@ -28377,10 +28219,10 @@ object MainForm: TMainForm
OnClick = btnPauseClick
end
object btnStop: TSpeedButton
Left = 145
Left = 147
Height = 40
Top = 5
Width = 65
Width = 67
Glyph.Data = {
36040000424D3604000000000000360000002800000010000000100000000100
2000000000000004000064000000640000000000000000000000000000000000
@ -28420,10 +28262,10 @@ object MainForm: TMainForm
OnClick = btnStopClick
end
object btnWarp: TSpeedButton
Left = 215
Left = 219
Height = 40
Top = 5
Width = 92
Width = 86
Caption = 'Warp+'
OnClick = btnWarpClick
end
@ -28433,8 +28275,8 @@ object MainForm: TMainForm
AnchorSideBottom.Control = pnlFlightControl
Left = 6
Height = 33
Top = 536
Width = 298
Top = 543
Width = 296
Anchors = [akLeft, akRight, akBottom]
BorderSpacing.Bottom = 8
TabOrder = 5
@ -28446,11 +28288,11 @@ object MainForm: TMainForm
AnchorSideBottom.Control = pnlWarpInfo
Left = 6
Height = 72
Top = 464
Width = 298
Top = 471
Width = 296
Anchors = [akLeft, akRight, akBottom]
ClientHeight = 72
ClientWidth = 298
ClientWidth = 296
Color = clCream
ParentBackground = False
ParentColor = False
@ -28459,7 +28301,7 @@ object MainForm: TMainForm
Left = 6
Height = 60
Top = 6
Width = 286
Width = 284
Align = alClient
AutoSize = False
BorderSpacing.Around = 5
@ -28473,16 +28315,16 @@ object MainForm: TMainForm
AnchorSideBottom.Control = plInfo
AnchorSideBottom.Side = asrBottom
Left = 4
Height = 21
Top = 47
Width = 114
Height = 19
Top = 49
Width = 90
Anchors = [akLeft, akBottom]
BorderSpacing.Around = 3
Caption = 'Follow tightly'
Checked = True
OnChange = cbTightFollowChange
State = cbChecked
TabOrder = 0
OnChange = cbTightFollowChange
end
end
end

View File

@ -71,7 +71,7 @@ implementation
{$R *.lfm}
uses
LCLType, Math, FPImage, DateUtils, mvEngine;
LCLType, Math, FPImage, DateUtils, mvGeoMath, mvEngine;
type

View File

@ -5,7 +5,7 @@ unit globals;
interface
uses
Classes, SysUtils, mvEngine;
Classes, SysUtils, mvGeoMath, mvEngine;
type
TPOIMode = (pmDefaultDrawing, pmDefaultImage, pmImageList, pmCustomDrawing);

View File

@ -57,7 +57,7 @@ implementation
{$R *.lfm}
uses
mvTypes, mvEngine,
mvTypes, mvGeoMath, mvEngine,
globals;
destructor TGPSListViewer.Destroy;
@ -143,7 +143,7 @@ begin
// show distance between selected items
ShowMessage(Format('Distance between %s and %s is: %.2n %s.', [
CoordArr[0].Name, CoordArr[1].Name,
CalcGeoDistance(CoordArr[0].Lat, CoordArr[0].Lon, CoordArr[1].Lat, CoordArr[1].Lon, DistanceUnit),
CalcGeoDistance(CoordArr[0].Lat, CoordArr[0].Lon, CoordArr[1].Lat, CoordArr[1].Lon, DistanceUnit, esEllipsoid),
DistanceUnit_Names[DistanceUnit]
]));
end;

View File

@ -15,7 +15,7 @@ uses
Classes, SysUtils, Types, Forms, Controls, Graphics, Dialogs, ExtCtrls,
StdCtrls, ComCtrls, Buttons, IntfGraphics, PrintersDlgs,
Grids, ExtDlgs,
mvGeoNames, mvMapViewer, mvTypes, mvGpsObj, mvDrawingEngine,
mvGeoMath, mvGeoNames, mvMapViewer, mvTypes, mvGpsObj, mvDrawingEngine,
{$IFDEF WITH_ADDONS}ConfigFrame_with_Addons{$ELSE}ConfigFrame{$ENDIF};
type

View File

@ -24,192 +24,33 @@ object MainForm: TMainForm
Font.Pitch = fpVariable
MapProvider = 'Google Maps'
POIImage.Data = {
36170000424D36170000000000003600000028000000200000002E0000000100
2000000000000017000064000000640000000000000000000000FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FD560000FC4CFFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000F1110000FEE70000FEE10000
EA0CFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FE9A0000FFFF0000FFFF0000
FE8FFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF000000FB3E0000FEFE0000FFFF0000FFFF0000
FEFC0000FB35FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF000000DB070000FED70000FFFF0000FFFF0000FFFF0000
FFFF0000FECF0000C004FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF000000FE800000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FD75FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF000000F9290000FEF90000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FEF50000F821FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00000080020000FEC30000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FEB8FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF000000FD660000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FD5CFFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000
F5180000FEEF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FEEA0000F213FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000
FEAA0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FE9FFFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FC4C0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEFE0000FC42FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000EA0C0000FEE10000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEDB0000E309FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FE900000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FE86FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FB350000FEFC0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEFA0000
FA2DFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF000000C0040000FED00000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FEC700008002FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF000000FD770000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FD6CFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF000000F8230000FEF80000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FEF50000F61CFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF000000FECF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FEC8FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF000000FE820000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FD79FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF000000F9270000FEFD0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FEFD0000F822FFFFFF00FFFFFF00FFFFFF00FFFF
FF000000FEA70000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FEF70000FEB50000FE810000FD670000FD670000
FE810000FEB50000FEF70000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FEA7FFFFFF00FFFFFF00FFFFFF000000
FB340000FEFE0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FEFD0000FE920000F213FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF000000F2130000FE920000FEFD0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FEFE0000FB34FFFFFF00FFFFFF000000
FEAC0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FEF60000FC4DFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF000000FC4D0000FEF60000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEACFFFFFF000000F3150000
FEFA0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEFE0000
FC54FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FC540000FEFE0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEFA0000F3150000FD610000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEA2FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FEA20000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FD610000FEA00000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEFD0000F823FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000F8230000FEFD0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEA00000FECD0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEC5FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FEC50000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FECD0000FEEC0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FE8CFFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FE8C0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEEC0000FEFA0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FD72FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FD720000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEFA0000FEF70000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FD75FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FD750000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEF70000FEE80000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FE95FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FE950000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEE80000FEC50000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FED5FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FED50000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEC50000FE940000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FB37FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FB370000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FE940000FC520000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEC00000
8002FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00000080020000FEC00000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FC520000E60A0000
FEF20000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FD7EFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FD7E0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEF20000E60AFFFFFF000000
FE950000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FEFE0000FD7F00008002FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00000080020000FD7F0000FEFE0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FE95FFFFFF00FFFFFF000000
F8200000FEF80000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FEC30000FB3DFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF000000FB3D0000FEC30000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FEF80000F820FFFFFF00FFFFFF00FFFF
FF000000FE830000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FEDB0000FE9C0000FD7E0000FD7E0000
FE9C0000FEDB0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FE83FFFFFF00FFFFFF00FFFFFF00FFFF
FF000000DB070000FECC0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FECC0000DB07FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF000000F71F0000FEE40000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FEE40000F71FFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF000000FA2B0000FEE50000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FEE50000FA2BFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF000000F8210000FECD0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FECE0000F821FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000DB070000FE850000FEF90000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FEF90000FE850000
DB07FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000F8230000
FE990000FEF30000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000
FFFF0000FFFF0000FFFF0000FFFF0000FEF30000FE990000F823FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF000000EA0C0000FD570000FE980000FEC70000FEEB0000FEF90000FEF90000
FEEB0000FEC70000FE980000FD570000EA0CFFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00
4503000089504E470D0A1A0A0000000D49484452000000200000002E08060000
0049701B840000030C49444154789CB5983B68145114867F4410C54644B0107C
152262A7A4D2C2DA3422DAD8D868631510B1D1688855409104A2088634819056
B00C488845626C82C55662A29204C40DC96ED66CD6F38F77976B76E6DCC7CC14
077667CEF9FFEF3EE6CC03AD560B3EB1021C6C023777805189598955892D13AB
E6D8287398EBABEB4CA803A745F895C47A92EE11CC650D6BA3012AC03E1119E4
087D8D5340383B83D40A0210F29352381F6B9C02F2899A5E000DE09C142C1765
6E412C535B05A801C72571A968730B62891EA90066CDE7CA32B720E6EC3DD101
901303012215893772C93D62F0378F05D40FFC07201BE4841CAC7B147EF8035C
CADAD13CC71C0F9D3A3D3B007260C451B023237DDC0FEC715DD7CC612E6B1C9A
2309C077E080FCA96AC99C66DFCED68EA44607A8D21BDBC03547E2B4CFC8D366
82B59A36BD39FDC35A92AC6B4FA8B9B5277A1C831B26C08C92B0186B6E5D5D8B
8AFE0C137EBA364A4E80CC0D4E6F26D494CD773F2F40A2910D5023404301E82B
00A04F016810604D49182A60098614FD3568B7DD6483E607D036F93C13C69484
A6CF534D5698A7A9A6A23FC635BAEDB856C7738C7E5CD3A637D68123DA46644F
978E753DD49C35DAFD809EF46E934E39666153047B03CC7B59E3D09C6AFDFB97
B4CC0B1E77AFA6C4CB2A7038CB98E798A3AD7B7B56E9D90130B330A115D9CD43
E29DC41359C3BB0CFE36C7329BDA2E8D89B66F07600B38E3222F22CC9575AA0B
C0CCC2D3B20164C61EDA9E5DEB28100B258EFEE36EBF2E00D33C9CCF8711E61B
9BC0312780B9813C2861EAEFA579A5024C037B85F87381A39FCD7AAC4B0530BD
E1A2146E1760DE9057B2F3593E99006643BE2800E099E6A102F04383087CCD61
5EF906EC8F06307DFD6AA4396F62575CFA4E00B3149311006F7DB4BD003680A3
22F82BC07C45BB69050398DE7027E09ABFE5ABEB0D605EB57CDE7CDFFB6A0601
30E48E7956FB68C5769BF52DA81000B321FB95A90F7E910906309F72BEA48C7E
812DBC740086B4E9CBF6231C5B365B778C561480598AD716C0F3589D6880DFC0
2131FEC1561DF26DB83000866CBA1B49ABCEA1F117D2B9A057721AC774000000
0049454E44AE426082
}
POITextBgColor = clCream
UseThreads = True

View File

@ -6,7 +6,7 @@ interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls,
Grids, StdCtrls, ColorBox, mvMapViewer, mvGpsObj;
Grids, StdCtrls, ColorBox, mvMapViewer, mvGeoMath, mvGpsObj;
type

View File

@ -20,7 +20,7 @@
<Description Value="Component for viewing maps (Google, OpenStreetMap, etc). This is a fork of MapViewer by ti_dic (https://sourceforge.net/p/roadbook/code/ci/master/tree/mapviewer/) which itself is based on the MapViewer by Maciej Kaczkowski (https://github.com/maciejkaczkowski/mapviewer)."/>
<License Value="Modified LGPL with linking exception, like FreePascal RTL/FCL and Lazarus LCL"/>
<Version Minor="2" Release="7"/>
<Files Count="21">
<Files Count="22">
<Item1>
<Filename Value="source/mvcache.pas"/>
<UnitName Value="mvCache"/>
@ -108,6 +108,10 @@
<HasRegisterProc Value="True"/>
<UnitName Value="mvDE_LCL"/>
</Item21>
<Item22>
<Filename Value="source/mvgeomath.pas"/>
<UnitName Value="mvgeomath"/>
</Item22>
</Files>
<CompatibilityMode Value="True"/>
<RequiredPkgs Count="2">

View File

@ -11,7 +11,7 @@ uses
mvCache, mvDownloadEngine, mvDragObj, mvEngine, mvGeoNames, mvGpsObj,
mvJobQueue, mvJobs, mvMapProvider, mvTypes, mvMapViewer, mvExtraData,
mvDLEFpc, mvMapViewerReg, mvGPX, mvDrawingEngine, mvDE_IntfGraphics,
mvDLEWin, mvMapViewerPropEdits, mvLayersPropEditForm, mvDE_LCL,
mvDLEWin, mvMapViewerPropEdits, mvLayersPropEditForm, mvDE_LCL, mvgeomath,
LazarusPackageIntf;
implementation

View File

@ -21,13 +21,7 @@ interface
uses
Classes, SysUtils, IntfGraphics, Controls, Math, GraphType, FPImage,
mvTypes, mvJobQueue, mvMapProvider, mvDownloadEngine, mvCache, mvDragObj;
const
EARTH_EQUATORIAL_RADIUS = 6378137;
EARTH_POLAR_RADIUS = 6356752.3142;
EARTH_CIRCUMFERENCE = 2 * pi * EARTH_EQUATORIAL_RADIUS;
EARTH_ECCENTRICITY = sqrt(1 - sqr(EARTH_POLAR_RADIUS / EARTH_EQUATORIAL_RADIUS));
mvTypes, mvGeoMath, mvJobQueue, mvMapProvider, mvDownloadEngine, mvCache, mvDragObj;
type
TDrawTileEvent = procedure (const TileId: TTileId; X,Y: integer;
@ -42,8 +36,6 @@ type
TTileIdArray = Array of TTileId;
TDistanceUnits = (duMeters, duKilometers, duMiles);
{ TMapWindow }
TMapWindow = Record
@ -215,33 +207,19 @@ type
property OnZoomChange: TNotifyEvent read FOnZoomChange write FOnZoomChange;
end;
function RealPoint(Lat, Lon: Double): TRealPoint;
function NormalizeLon(const Lon: Double): Double; inline;
// The following functions have been moved to mvGeoMath and will be removed here sooner or later...
function DMSToDeg(Deg, Min: Word; Sec: Double): Double; deprecated 'Use function in unit mvGeoMath';
function GPSToDMS(Angle: Double): string; deprecated 'Use function in unit mvGeoMath';
function GPSToDMS(Angle: Double; AFormatSettings: TFormatSettings): string; deprecated 'Use function in unit mvGeoMath';
function HaversineDist(Lat1, Lon1, Lat2, Lon2, Radius: Double): Double;
function CalcGeoDistance(Lat1, Lon1, Lat2, Lon2: double;
AUnits: TDistanceUnits = duKilometers): double;
function LatToStr(ALatitude: Double; DMS: Boolean): String; deprecated 'Use function in unit mvGeoMath';
function LatToStr(ALatitude: Double; DMS: Boolean; AFormatSettings: TFormatSettings): String; deprecated 'Use function in unit mvGeoMath';
function LonToStr(ALongitude: Double; DMS: Boolean): String; deprecated 'Use function in unit mvGeoMath';
function LonToStr(ALongitude: Double; DMS: Boolean; AFormatSettings: TFormatSettings): String; deprecated 'Use function in unit mvGeoMath';
function TryStrToGps(const AValue: String; out ADeg: Double): Boolean; deprecated 'Use function in unit mvGeoMath';
function TryStrDMSToDeg(const AValue: String; out ADeg: Double): Boolean; deprecated 'Use function in unit mvGeoMath';
function CalcBearing(Lat1, Lon1, Lat2, Lon2: Double): Double;
procedure CalcLatLon(const Lat1, Lon1, ADist, ABearing: Double; out Lat2, Lon2: Double);
procedure CalcMidpoint(const Lat1, Lon1, Lat2, Lon2: Double; out Lat, Lon: Double);
procedure CalcIntermedPoint(const Lat1, Lon1, Lat2, Lon2, AFrac: Double; out Lat, Lon: Double);
function DMSToDeg(Deg, Min: Word; Sec: Double): Double;
function GPSToDMS(Angle: Double): string;
function GPSToDMS(Angle: Double; AFormatSettings: TFormatSettings): string;
function LatToStr(ALatitude: Double; DMS: Boolean): String;
function LatToStr(ALatitude: Double; DMS: Boolean; AFormatSettings: TFormatSettings): String;
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);
function ZoomFactor(AZoomLevel: Integer): Int64;
function ZoomFactor(AZoomLevel: Integer): Int64; deprecated 'Use function in unit mvGeoMath';
var
HERE_AppID: String = '';
@ -249,8 +227,6 @@ var
OpenWeatherMap_ApiKey: String = '';
ThunderForest_ApiKey: String = '';
DMS_Decimals: Integer = 1;
implementation
@ -258,25 +234,6 @@ uses
Forms, TypInfo, laz2_xmlread, laz2_xmlwrite, laz2_dom,
mvJobs, mvGpsObj;
const
_K = 1024;
_M = _K*_K;
_G = _K*_M;
ZOOM_FACTOR: array[0..32] of Int64 = (
1, 2, 4, 8, 16, 32, 64, 128, 256, 512, // 0..9
_K, 2*_K, 4*_K, 8*_K, 16*_K, 32*_K, 64*_K, 128*_K, 256*_K, 512*_K, // 10..19
_M, 2*_M, 4*_M, 8*_M, 16*_M, 32*_M, 64*_M, 128*_M, 256*_M, 512*_M, // 20..29
_G, 2*_G, 4*_G // 30..32
);
function ZoomFactor(AZoomLevel: Integer): Int64;
begin
if (AZoomLevel >= Low(ZOOM_FACTOR)) and (AZoomLevel < High(ZOOM_FACTOR)) then
Result := ZOOM_FACTOR[AZoomLevel]
else
Result := round(IntPower(2, AZoomLevel));
end;
type
{ TEnvTile }
@ -1474,492 +1431,56 @@ begin
MapWin.MapProvider := MP; // Restore the old Map Provider
end;
//------------------------------------------------------------------------------
{------------------------------------------------------------------------------}
function RealPoint(Lat, Lon: Double): TRealPoint;
function DMSToDeg(Deg, Min: Word; Sec: Double): Double;
begin
Result.Lon := Lon;
Result.Lat := Lat;
end;
procedure SplitGps(AValue: Double; out ADegs, AMins: Double);
begin
AValue := abs(AValue);
AMins := frac(AValue) * 60;
if abs(AMins - 60) < 1E-3 then
begin
AMins := 0;
ADegs := trunc(AValue) + 1;
end else
ADegs := trunc(AValue);
if AValue < 0 then
ADegs := -ADegs;
end;
procedure SplitGps(AValue: Double; out ADegs, AMins, ASecs: Double);
begin
SplitGps(AValue, ADegs, AMins);
ASecs := frac(AMins) * 60;
AMins := trunc(AMins);
if abs(ASecs - 60) < 1E-3 then
begin
ASecs := 0;
AMins := AMins + 1;
if abs(AMins - 60) < 1e-3 then
begin
AMins := 0;
ADegs := ADegs + 1;
end;
end;
if AValue < 0 then
ADegs := -ADegs;
Result := mvGeoMath.DMSToDeg(Deg, Min, Sec);
end;
function GPSToDMS(Angle: Double): string;
begin
Result := GPSToDMS(Angle, DefaultFormatSettings);
Result := mvGeoMath.GPSToDMS(Angle);
end;
function GPSToDMS(Angle: Double; AFormatSettings: TFormatSettings): string;
var
deg, min, sec: Double;
begin
SplitGPS(Angle, deg, min, sec);
Result := Format('%.0f° %.0f'' %.*f"', [deg, min, DMS_Decimals, sec], AFormatSettings);
Result := mvGeoMath.GPSToDMS(Angle, AFormatSettings);
end;
function LatToStr(ALatitude: Double; DMS: Boolean): String;
begin
Result := LatToStr(ALatitude, DMS, DefaultFormatSettings);
Result := mvGeoMath.LatToStr(ALatitude, DMS);
end;
function LatToStr(ALatitude: Double; DMS: Boolean; AFormatSettings: TFormatSettings): String;
begin
if DMS then
Result := GPSToDMS(abs(ALatitude), AFormatSettings)
else
Result := Format('%.6f°',[abs(ALatitude)], AFormatSettings);
if ALatitude > 0 then
Result := Result + ' N'
else
if ALatitude < 0 then
Result := Result + ' S';
Result := mvGeoMath.LatToStr(ALatitude, DMS, AFormatSettings);
end;
function LonToStr(ALongitude: Double; DMS: Boolean): String;
begin
Result := LonToStr(ALongitude, DMS, DefaultFormatSettings);
Result := mvGeoMath.LonToStr(ALongitude, DMS);
end;
function LonToStr(ALongitude: Double; DMS: Boolean; AFormatSettings: TFormatSettings): String;
begin
if DMS then
Result := GPSToDMS(abs(ALongitude), AFormatSettings)
else
Result := Format('%.6f°', [abs(ALongitude)], AFormatSettings);
if ALongitude > 0 then
Result := Result + ' E'
else if ALongitude < 0 then
Result := Result + ' W';
Result := mvGeoMath.LonToStr(ALongitude, DMS, AFormatSettings);
end;
{ Combines up to three parts of a GPS coordinate string (degrees, minutes, seconds)
to a floating-point degree value. The parts are separated by non-numeric
characters:
three parts ---> d m s ---> d and m must be integer, s can be float
two parts ---> d m ---> d must be integer, s can be float
one part ---> d ---> d can be float
Each part can exhibit a unit identifier, such as °, ', or ". BUT: they are
ignored. This means that an input string 50°30" results in the output value 50.5
although the second part is marked as seconds, not minutes!
Hemisphere suffixes ('N', 'S', 'E', 'W') are supported at the end of the input string.
}
function TryStrToGps(const AValue: String; out ADeg: Double): Boolean;
const
NUMERIC_CHARS = ['0'..'9', '.', ',', '-', '+'];
var
mins, secs: Double;
i, j, len: Integer;
n: Integer;
s: String = '';
res: Integer;
sgn: Double;
begin
Result := false;
ADeg := NaN;
mins := 0;
secs := 0;
if AValue = '' then
exit;
len := Length(AValue);
i := len;
while (i >= 1) and (AValue[i] = ' ') do dec(i);
sgn := 1.0;
if (AValue[i] in ['S', 's', 'W', 'w']) then sgn := -1;
// skip leading non-numeric characters
i := 1;
while (i <= len) and not (AValue[i] in NUMERIC_CHARS) do
inc(i);
// extract first value: degrees
SetLength(s, len);
j := 1;
n := 0;
while (i <= len) and (AValue[i] in NUMERIC_CHARS) do begin
if AValue[i] = ',' then s[j] := '.' else s[j] := AValue[i];
inc(i);
inc(j);
inc(n);
end;
if n > 0 then begin
SetLength(s, n);
val(s, ADeg, res);
if res <> 0 then
exit;
end;
// skip non-numeric characters between degrees and minutes
while (i <= len) and not (AValue[i] in NUMERIC_CHARS) do
inc(i);
// extract second value: minutes
SetLength(s, len);
j := 1;
n := 0;
while (i <= len) and (AValue[i] in NUMERIC_CHARS) do begin
if AValue[i] = ',' then s[j] := '.' else s[j] := AValue[i];
inc(i);
inc(j);
inc(n);
end;
if n > 0 then begin
SetLength(s, n);
val(s, mins, res);
if (res <> 0) or (mins < 0) then
exit;
end;
// skip non-numeric characters between minutes and seconds
while (i <= len) and not (AValue[i] in NUMERIC_CHARS) do
inc(i);
// extract third value: seconds
SetLength(s, len);
j := 1;
n := 0;
while (i <= len) and (AValue[i] in NUMERIC_CHARS) do begin
if AValue[i] = ',' then s[j] := '.' else s[j] := AValue[i];
inc(i);
inc(j);
inc(n);
end;
if n > 0 then begin
SetLength(s, n);
val(s, secs, res);
if (res <> 0) or (secs < 0) then
exit;
end;
// If the string contains seconds then minutes and deegrees must be integers
if (secs <> 0) and ((frac(ADeg) > 0) or (frac(mins) > 0)) then
exit;
// If the string does not contain seconds then degrees must be integer.
if (secs = 0) and (mins <> 0) and (frac(ADeg) > 0) then
exit;
// If the string contains minutes, but no seconds, then the degrees must be integer.
Result := (mins >= 0) and (mins < 60) and (secs >= 0) and (secs < 60);
// A similar check should be made for the degrees range, but since this is
// different for latitude and longitude the check is skipped here.
if Result then
ADeg := sgn * (abs(ADeg) + mins / 60 + secs / 3600);
Result := mvGeoMath.TryStrToGps(AValue, ADeg);
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;
Result := mvGeoMath.TryStrDMSToDeg(AValue, ADeg);
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
latFrom, latTo, lonDiff: Double;
dx, dy, dz: Double;
function ZoomFactor(AZoomLevel: Integer): Int64;
begin
lonDiff := DegToRad(Lon1 - Lon2);
latFrom := DegToRad(Lat1);
latTo := DegToRad(Lat2);
dz := sin(latFrom) - sin(latTo);
dx := cos(lonDiff) * cos(latFrom) - cos(latTo);
dy := sin(lonDiff) * cos(latFrom);
Result := arcsin(sqrt(sqr(dx) + sqr(dy) + sqr(dz)) / 2) * Radius * 2;
end;
{ Returns the direct distance (air-line) between two geo coordinates
If latitude NOT between -90°..+90° and longitude NOT between -180°..+180°
the function returns NaN.
Usage: CalcGeoDistance(51.53323, -2.90130, 51.29442, -2.27275, duKilometers);
}
function CalcGeoDistance(Lat1, Lon1, Lat2, Lon2: double;
AUnits: TDistanceUnits = duKilometers): double;
begin
// Validate
if (Lat1 < -90.0) or (Lat1 > 90.0) then exit(NaN);
if (Lat2 < -90.0) or (Lat2 > 90.0) then exit(NaN);
Result := HaversineDist(Lat1, Lon1, Lat2, Lon2, EARTH_EQUATORIAL_RADIUS);
case AUnits of
duMeters: ;
duKilometers: Result := Result * 1E-3;
duMiles: Result := Result * 0.62137E-3;
end;
end;
{ Calculate initial bearing (in °) from the start point Lat1,Lon1 to
the end point Lat2,Lon2. No parameter checks, result normalized to 0°..360°.
}
function CalcBearing(Lat1, Lon1, Lat2, Lon2: Double): Double;
var
latFrom, latTo, 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));
Result := RadToDeg(Result);
if Result < 0.0 then
Result := Result + 360.0;
end;
{ Calculate end point Lat2,Lon2 by given start point Lat1,Lon1, distance
ADist (in meters) and bearing ABearing (in °). No parameter checks,
result Lon2 normalized to -180°..180°.
}
procedure CalcLatLon(const Lat1, Lon1, ADist, ABearing: Double; out Lat2,
Lon2: Double);
var
latFrom, lonFrom, brng, 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));
Lat2 := RadToDeg(Lat2);
Lon2 := NormalizeLon(RadToDeg(Lon2));
end;
{ Calculate midpoint Lat,Lon by given start point Lat1,Lon1 and end point
Lat2,Lon2. No parameter checks, result Lon normalized to -180°..180°.
}
procedure CalcMidpoint(const Lat1, Lon1, Lat2, Lon2: Double; out Lat,
Lon: Double);
var
latFrom, lonDiff, latTo, lonTo, Bx, By: Double;
begin
lonDiff := DegToRad(Lon2 - Lon1);
latFrom := DegToRad(Lat1);
latTo := DegToRad(Lat2);
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);
Lat := RadToDeg(Lat);
Lon := NormalizeLon(RadToDeg(Lon));
end;
{ Calculate intermediate point Lat,Lon by given start point Lat1,Lon1, end point
Lat2,Lon2 and fraction AFrac (0.0-1.0). No parameter checks for
Lat1,Lon1,Lat2 and Lon2. Result Lon normalized to -180°..180°.
}
procedure CalcIntermedPoint(const Lat1, Lon1, Lat2, Lon2, AFrac: Double; out
Lat, Lon: Double);
var
latFrom, lonFrom, latTo, lonTo: Double;
A, B, aD, X, Y, Z: Double;
begin
if (Lat1 = Lat2) and (Lon1 = Lon2) or (AFrac < 0.001) then
begin
Lat := Lat1;
Lon := Lon1;
Exit;
end;
if AFrac > 0.999 then
begin
Lat := Lat2;
Lon := Lon2;
Exit;
end;
aD := CalcGeoDistance(Lat1, Lon1, Lat2, Lon2) / EARTH_EQUATORIAL_RADIUS;
latFrom := DegToRad(Lat1);
lonFrom := DegToRad(Lon1);
latTo := DegToRad(Lat2);
lonTo := DegToRad(Lon2);
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);
Lat := ArcTan2(Z, Sqrt(Sqr(X) + Sqr(Y)));
Lon := ArcTan2(Y, X);
Lat := RadToDeg(Lat);
Lon := NormalizeLon(RadToDeg(Lon));
end;
function NormalizeLon(const Lon: Double): Double;
begin
if InRange(Lon, -180.0, 180.0)
then Result := Lon
else Result := FMod(Lon + 540.0, 360.0) - 180.0;
end;
{ Converts an angle given as degrees, minutes and seconds to a single
floating point degrees value. }
function DMSToDeg(Deg, Min: Word; Sec: Double): Double;
begin
Result := Deg + Min/60.0 + Sec/3600.0;
Result := mvGeoMath.ZoomFactor(AZoomLevel);
end;
end.

View File

@ -0,0 +1,601 @@
unit mvGeoMath;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Math;
const
EARTH_EQUATORIAL_RADIUS = 6378137;
EARTH_POLAR_RADIUS = 6356752.3142;
EARTH_CIRCUMFERENCE = 2 * pi * EARTH_EQUATORIAL_RADIUS;
EARTH_ECCENTRICITY = sqrt(1 - sqr(EARTH_POLAR_RADIUS / EARTH_EQUATORIAL_RADIUS));
EARTH_FLATTENING = 1.0 - EARTH_POLAR_RADIUS / EARTH_EQUATORIAL_RADIUS;
DMS_Decimals: Integer = 1;
type
TDistanceUnits = (duMeters, duKilometers, duMiles);
TEarthShape = (esSphere, esEllipsoid);
function NormalizeLon(const Lon: Double): Double; inline;
function HaversineDist(Lat1, Lon1, Lat2, Lon2, Radius: Double): Double;
function LambertsDist(Lat1, Lon1, Lat2, Lon2, Radius, Flattening: Double): Double;
function CalcGeoDistance(Lat1, Lon1, Lat2, Lon2: double;
AUnits: TDistanceUnits = duKilometers; AShape: TEarthShape = esSphere): double;
function CalcBearing(Lat1, Lon1, Lat2, Lon2: Double): Double;
procedure CalcLatLon(const Lat1, Lon1, ADist, ABearing: Double; out Lat2, Lon2: Double);
procedure CalcMidpoint(const Lat1, Lon1, Lat2, Lon2: Double; out Lat, Lon: Double);
procedure CalcIntermedPoint(const Lat1, Lon1, Lat2, Lon2, AFrac: Double; out Lat, Lon: Double);
function DMSToDeg(Deg, Min: Word; Sec: Double): Double;
function GPSToDMS(Angle: Double): string;
function GPSToDMS(Angle: Double; AFormatSettings: TFormatSettings): string;
function LatToStr(ALatitude: Double; DMS: Boolean): String;
function LatToStr(ALatitude: Double; DMS: Boolean; AFormatSettings: TFormatSettings): String;
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);
function ZoomFactor(AZoomLevel: Integer): Int64;
implementation
const
_K = 1024;
_M = _K*_K;
_G = _K*_M;
ZOOM_FACTOR: array[0..32] of Int64 = (
1, 2, 4, 8, 16, 32, 64, 128, 256, 512, // 0..9
_K, 2*_K, 4*_K, 8*_K, 16*_K, 32*_K, 64*_K, 128*_K, 256*_K, 512*_K, // 10..19
_M, 2*_M, 4*_M, 8*_M, 16*_M, 32*_M, 64*_M, 128*_M, 256*_M, 512*_M, // 20..29
_G, 2*_G, 4*_G // 30..32
);
function ZoomFactor(AZoomLevel: Integer): Int64;
begin
if (AZoomLevel >= Low(ZOOM_FACTOR)) and (AZoomLevel < High(ZOOM_FACTOR)) then
Result := ZOOM_FACTOR[AZoomLevel]
else
Result := round(IntPower(2, AZoomLevel));
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
}
// angles in radians
function HaversineAngle(Lat1, Lon1, Lat2, Lon2: Double): Double;
var
latFrom, latTo, lonDiff: 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);
Result := arcsin(sqrt(sqr(dx) + sqr(dy) + sqr(dz)) / 2.0) * 2.0;
end;
// Angles in degrees
function HaversineDist(Lat1, Lon1, Lat2, Lon2, Radius: Double): Double;
begin
Result := HaversineAngle(DegToRad(Lat1), DegToRad(Lon1), DegToRad(Lat2), DegToRad(Lon2)) * Radius;
end;
{ Calculation of distance on an ellipsoid
https://en.wikipedia.org/wiki/Geographical_distance
}
// Angles in degrees
function LambertsDist(Lat1, Lon1, Lat2, Lon2, Radius, Flattening: Double): Double;
var
reducedLat1, reducedLat2, sigma: Double;
P, Q: Double;
X, Y: Double;
sinP, cosP, sinQ, cosQ: Double;
sinSigma, sinSigma2, cosSigma2: Double;
begin
Lat1 := DegToRad(Lat1);
Lon1 := DegToRad(Lon1);
Lat2 := DegToRad(Lat2);
Lon2 := DegToRad(Lon2);
reducedLat1 := arctan((1.0 - flattening) * tan(Lat1));
reducedLat2 := arctan((1.0 - flattening) * tan(Lat2));
sigma := HaversineAngle(reducedLat1, Lon1, reducedLat2, Lon2);
P := (reducedLat1 + reducedLat2) * 0.5;
Q := (reducedLat2 - reducedLat1) * 0.5;
SinCos(P, sinP, cosP);
SinCos(Q, sinQ, cosQ);
SinCos(sigma*0.5, sinSigma2, cosSigma2);
sinSigma := sin(sigma);
X := (sigma - sinSigma) * sqr(sinP * cosQ / cosSigma2);
Y := (sigma + sinSigma) * sqr(cosP * sinQ / sinSigma2);
Result := (sigma - 0.5*Flattening*(X + Y)) * Radius;
end;
{ Returns the direct distance (air-line) between two geo coordinates
If latitude is NOT between -90°..+90° and longitude is NOT between -180°..+180°
the function returns NaN.
Usage example:
CalcGeoDistance(51.53323, -2.90130, 51.29442, -2.27275, duKilometers, esEllipsoid);
}
function CalcGeoDistance(Lat1, Lon1, Lat2, Lon2: double;
AUnits: TDistanceUnits = duKilometers; AShape: TEarthShape = esSphere): double;
begin
// Validate
if (Lat1 < -90.0) or (Lat1 > 90.0) then exit(NaN);
if (Lat2 < -90.0) or (Lat2 > 90.0) then exit(NaN);
case AShape of
esSphere:
Result := HaversineDist(Lat1, Lon1, Lat2, Lon2, EARTH_EQUATORIAL_RADIUS);
esEllipsoid:
Result := LambertsDist(Lat1, Lon1, Lat2, Lon2, EARTH_EQUATORIAL_RADIUS, EARTH_FLATTENING);
end;
case AUnits of
duMeters: ;
duKilometers: Result := Result * 0.001;
duMiles: Result := Result * 0.62137E-3;
end;
end;
{ Calculate initial bearing (in °) from the start point Lat1,Lon1 to
the end point Lat2,Lon2. No parameter checks, result normalized to 0°..360°.
}
function CalcBearing(Lat1, Lon1, Lat2, Lon2: Double): Double;
var
latFrom, latTo, 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));
Result := RadToDeg(Result);
if Result < 0.0 then
Result := Result + 360.0;
end;
{ Calculate end point Lat2,Lon2 by given start point Lat1,Lon1, distance
ADist (in meters) and bearing ABearing (in °). No parameter checks,
result Lon2 normalized to -180°..180°.
}
procedure CalcLatLon(const Lat1, Lon1, ADist, ABearing: Double; out Lat2,
Lon2: Double);
var
latFrom, lonFrom, brng, 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));
Lat2 := RadToDeg(Lat2);
Lon2 := NormalizeLon(RadToDeg(Lon2));
end;
{ Calculate midpoint Lat,Lon by given start point Lat1,Lon1 and end point
Lat2,Lon2. No parameter checks, result Lon normalized to -180°..180°.
}
procedure CalcMidpoint(const Lat1, Lon1, Lat2, Lon2: Double; out Lat,
Lon: Double);
var
latFrom, lonDiff, latTo, lonTo, Bx, By: Double;
begin
lonDiff := DegToRad(Lon2 - Lon1);
latFrom := DegToRad(Lat1);
latTo := DegToRad(Lat2);
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);
Lat := RadToDeg(Lat);
Lon := NormalizeLon(RadToDeg(Lon));
end;
{ Calculate intermediate point Lat,Lon by given start point Lat1,Lon1, end point
Lat2,Lon2 and fraction AFrac (0.0-1.0). No parameter checks for
Lat1,Lon1,Lat2 and Lon2. Result Lon normalized to -180°..180°.
}
procedure CalcIntermedPoint(const Lat1, Lon1, Lat2, Lon2, AFrac: Double; out
Lat, Lon: Double);
var
latFrom, lonFrom, latTo, lonTo: Double;
A, B, aD, X, Y, Z: Double;
begin
if (Lat1 = Lat2) and (Lon1 = Lon2) or (AFrac < 0.001) then
begin
Lat := Lat1;
Lon := Lon1;
Exit;
end;
if AFrac > 0.999 then
begin
Lat := Lat2;
Lon := Lon2;
Exit;
end;
aD := CalcGeoDistance(Lat1, Lon1, Lat2, Lon2) / EARTH_EQUATORIAL_RADIUS;
latFrom := DegToRad(Lat1);
lonFrom := DegToRad(Lon1);
latTo := DegToRad(Lat2);
lonTo := DegToRad(Lon2);
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);
Lat := ArcTan2(Z, Sqrt(Sqr(X) + Sqr(Y)));
Lon := ArcTan2(Y, X);
Lat := RadToDeg(Lat);
Lon := NormalizeLon(RadToDeg(Lon));
end;
function NormalizeLon(const Lon: Double): Double;
begin
if InRange(Lon, -180.0, 180.0)
then Result := Lon
else Result := FMod(Lon + 540.0, 360.0) - 180.0;
end;
{ Converts an angle given as degrees, minutes and seconds to a single
floating point degrees value. }
function DMSToDeg(Deg, Min: Word; Sec: Double): Double;
begin
Result := Deg + Min/60.0 + Sec/3600.0;
end;
procedure SplitGps(AValue: Double; out ADegs, AMins: Double);
begin
AValue := abs(AValue);
AMins := frac(AValue) * 60;
if abs(AMins - 60) < 1E-3 then
begin
AMins := 0;
ADegs := trunc(AValue) + 1;
end else
ADegs := trunc(AValue);
if AValue < 0 then
ADegs := -ADegs;
end;
procedure SplitGps(AValue: Double; out ADegs, AMins, ASecs: Double);
begin
SplitGps(AValue, ADegs, AMins);
ASecs := frac(AMins) * 60;
AMins := trunc(AMins);
if abs(ASecs - 60) < 1E-3 then
begin
ASecs := 0;
AMins := AMins + 1;
if abs(AMins - 60) < 1e-3 then
begin
AMins := 0;
ADegs := ADegs + 1;
end;
end;
if AValue < 0 then
ADegs := -ADegs;
end;
function GPSToDMS(Angle: Double): string;
begin
Result := GPSToDMS(Angle, DefaultFormatSettings);
end;
function GPSToDMS(Angle: Double; AFormatSettings: TFormatSettings): string;
var
deg, min, sec: Double;
begin
SplitGPS(Angle, deg, min, sec);
Result := Format('%.0f° %.0f'' %.*f"', [deg, min, DMS_Decimals, sec], AFormatSettings);
end;
function LatToStr(ALatitude: Double; DMS: Boolean): String;
begin
Result := LatToStr(ALatitude, DMS, DefaultFormatSettings);
end;
function LatToStr(ALatitude: Double; DMS: Boolean; AFormatSettings: TFormatSettings): String;
begin
if DMS then
Result := GPSToDMS(abs(ALatitude), AFormatSettings)
else
Result := Format('%.6f°',[abs(ALatitude)], AFormatSettings);
if ALatitude > 0 then
Result := Result + ' N'
else
if ALatitude < 0 then
Result := Result + ' S';
end;
function LonToStr(ALongitude: Double; DMS: Boolean): String;
begin
Result := LonToStr(ALongitude, DMS, DefaultFormatSettings);
end;
function LonToStr(ALongitude: Double; DMS: Boolean; AFormatSettings: TFormatSettings): String;
begin
if DMS then
Result := GPSToDMS(abs(ALongitude), AFormatSettings)
else
Result := Format('%.6f°', [abs(ALongitude)], AFormatSettings);
if ALongitude > 0 then
Result := Result + ' E'
else if ALongitude < 0 then
Result := Result + ' W';
end;
{ Combines up to three parts of a GPS coordinate string (degrees, minutes, seconds)
to a floating-point degree value. The parts are separated by non-numeric
characters:
three parts ---> d m s ---> d and m must be integer, s can be float
two parts ---> d m ---> d must be integer, s can be float
one part ---> d ---> d can be float
Each part can exhibit a unit identifier, such as °, ', or ". BUT: they are
ignored. This means that an input string 50°30" results in the output value 50.5
although the second part is marked as seconds, not minutes!
Hemisphere suffixes ('N', 'S', 'E', 'W') are supported at the end of the input string.
}
function TryStrToGps(const AValue: String; out ADeg: Double): Boolean;
const
NUMERIC_CHARS = ['0'..'9', '.', ',', '-', '+'];
var
mins, secs: Double;
i, j, len: Integer;
n: Integer;
s: String = '';
res: Integer;
sgn: Double;
begin
Result := false;
ADeg := NaN;
mins := 0;
secs := 0;
if AValue = '' then
exit;
len := Length(AValue);
i := len;
while (i >= 1) and (AValue[i] = ' ') do dec(i);
sgn := 1.0;
if (AValue[i] in ['S', 's', 'W', 'w']) then sgn := -1;
// skip leading non-numeric characters
i := 1;
while (i <= len) and not (AValue[i] in NUMERIC_CHARS) do
inc(i);
// extract first value: degrees
SetLength(s, len);
j := 1;
n := 0;
while (i <= len) and (AValue[i] in NUMERIC_CHARS) do begin
if AValue[i] = ',' then s[j] := '.' else s[j] := AValue[i];
inc(i);
inc(j);
inc(n);
end;
if n > 0 then begin
SetLength(s, n);
val(s, ADeg, res);
if res <> 0 then
exit;
end;
// skip non-numeric characters between degrees and minutes
while (i <= len) and not (AValue[i] in NUMERIC_CHARS) do
inc(i);
// extract second value: minutes
SetLength(s, len);
j := 1;
n := 0;
while (i <= len) and (AValue[i] in NUMERIC_CHARS) do begin
if AValue[i] = ',' then s[j] := '.' else s[j] := AValue[i];
inc(i);
inc(j);
inc(n);
end;
if n > 0 then begin
SetLength(s, n);
val(s, mins, res);
if (res <> 0) or (mins < 0) then
exit;
end;
// skip non-numeric characters between minutes and seconds
while (i <= len) and not (AValue[i] in NUMERIC_CHARS) do
inc(i);
// extract third value: seconds
SetLength(s, len);
j := 1;
n := 0;
while (i <= len) and (AValue[i] in NUMERIC_CHARS) do begin
if AValue[i] = ',' then s[j] := '.' else s[j] := AValue[i];
inc(i);
inc(j);
inc(n);
end;
if n > 0 then begin
SetLength(s, n);
val(s, secs, res);
if (res <> 0) or (secs < 0) then
exit;
end;
// If the string contains seconds then minutes and deegrees must be integers
if (secs <> 0) and ((frac(ADeg) > 0) or (frac(mins) > 0)) then
exit;
// If the string does not contain seconds then degrees must be integer.
if (secs = 0) and (mins <> 0) and (frac(ADeg) > 0) then
exit;
// If the string contains minutes, but no seconds, then the degrees must be integer.
Result := (mins >= 0) and (mins < 60) and (secs >= 0) and (secs < 60);
// A similar check should be made for the degrees range, but since this is
// different for latitude and longitude the check is skipped here.
if Result then
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;
end.

View File

@ -16,7 +16,8 @@ unit mvGpsObj;
interface
uses
Classes, SysUtils, Graphics, fgl, mvtypes, contnrs, syncobjs;
Classes, SysUtils, Graphics, fgl, contnrs, syncobjs,
mvTypes, mvGeoMath;
const
NO_ELE = -10000000;
@ -1013,6 +1014,21 @@ begin
Result := FDateTime <> NO_DATE;
end;
function TGPSPoint.DistanceInKmFrom(OtherPt: TGPSPoint;
UseElevation: Boolean = true): Double;
var
lat1, lon1, lat2, lon2: Double;
dElev: Double;
begin
Result := CalcGeoDistance(Lat, Lon, OtherPt.Lat, OtherPt.Lon, duKilometers, esEllipsoid);
if UseElevation and HasElevation and OtherPt.HasElevation and (FElevation <> OtherPt.Elevation) then
begin
dElev := (FElevation - OtherPt.Elevation) * 0.001; // Elevation is given in meters
Result := sqrt(sqr(dElev) + sqr(Result));
end;
end;
(*
function TGPSPoint.DistanceInKmFrom(OtherPt: TGPSPoint;
UseElevation: boolean = true): double;
var
@ -1031,7 +1047,13 @@ begin
t3 := cos(lon1 - lon2);
t4 := t2 * t3;
t5 := t1 + t4;
rad_dist := arctan(-t5/sqrt(-t5 * t5 +1)) + 2 * arctan(1);
if t5 >= 1.0 then
rad_dist := 0.0
else
if t5 <= 1.0 then
rad_dist := pi
else
rad_dist := arctan(-t5/sqrt(-t5 * t5 + 1)) + 2 * arctan(1);
result := (rad_dist * 3437.74677 * 1.1508) * 1.6093470878864446;
if UseElevation and (FElevation <> OtherPt.FElevation) then
if (HasElevation) and (OtherPt.HasElevation) then
@ -1041,6 +1063,7 @@ begin
Result := sqrt(DiffEle*DiffEle + result*result);
end;
end;
*)
procedure TGPSPoint.MoveTo(ALon, ALat: Double; AElevation: double = NO_ELE;
ADateTime: TDateTime = NO_DATE);

View File

@ -29,8 +29,8 @@ interface
uses
Classes, SysUtils, Controls, GraphType, Graphics, FPImage, IntfGraphics,
Forms, ImgList, LCLVersion, fgl,
MvTypes, MvGPSObj, MvEngine, MvMapProvider, MvDownloadEngine, MvDrawingEngine,
mvCache, mvExtraData;
MvTypes, MvGeoMath, MvGPSObj, MvCache, MvExtraData, MvEngine, MvMapProvider,
MvDownloadEngine, MvDrawingEngine;
Type

View File

@ -78,8 +78,9 @@ procedure Register;
implementation
uses
Dialogs, IDEWindowIntf, mvMapViewer, mvGpsObj, mvLayersPropEditForm, mvEngine,
StrUtils;
Dialogs, IDEWindowIntf,
StrUtils,
mvGeoMath, mvMapViewer, mvGpsObj, mvLayersPropEditForm, mvEngine;
const
NONE = '(none)';

View File

@ -63,6 +63,8 @@ Type
function Union(const Area: TRealArea): TRealArea;
end;
function RealPoint(Lat, Lon: Double): TRealPoint;
implementation
@ -276,6 +278,12 @@ begin
Self.Lat := RadToDeg(AValue);
end;
function RealPoint(Lat, Lon: Double): TRealPoint;
begin
Result.Lon := Lon;
Result.Lat := Lat;
end;
{ TRealArea

View File

@ -8,7 +8,6 @@
<Title Value="mapviewer_tests"/>
<ResourceType Value="res"/>
<UseXPManifest Value="True"/>
<Icon Value="0"/>
</General>
<BuildModes>
<Item Name="Default" Default="True"/>
@ -41,7 +40,7 @@
<IsPartOfProject Value="True"/>
</Unit>
<Unit>
<Filename Value="mvtests_engine.pas"/>
<Filename Value="mvtests_geomath.pas"/>
<IsPartOfProject Value="True"/>
</Unit>
</Units>

View File

@ -4,7 +4,7 @@ program mapviewer_tests;
uses
Interfaces, Forms, GuiTestRunner,
mvtests_engine, mvtests_types;
mvtests_geomath, mvtests_types;
{$R *.res}

View File

@ -1,4 +1,4 @@
unit mvtests_engine;
unit mvtests_geomath;
{$mode objfpc}{$H+}
@ -8,7 +8,7 @@ uses
Classes, SysUtils, fpcunit, testutils, testregistry;
type
TMvTests_Engine= class(TTestCase)
TMvTests_GeoMath = class(TTestCase)
published
procedure Test_Distance;
procedure Test_LatToStr_DMS;
@ -22,7 +22,7 @@ type
implementation
uses
Math, mvEngine;
Math, mvGeoMath;
type
TDistanceRec = record
@ -94,7 +94,7 @@ const
var
PointFormatsettings: TFormatSettings;
procedure TMvTests_Engine.Test_Distance;
procedure TMvTests_GeoMath.Test_Distance;
const
TOLERANCE = 2;
RADIUS = 6378; // Earth radius in km, as used by the references
@ -110,7 +110,7 @@ begin
);
end;
procedure TMvTests_Engine.Test_LatToStr_Deg;
procedure TMvTests_GeoMath.Test_LatToStr_Deg;
const
NO_DMS = false;
var
@ -125,7 +125,7 @@ begin
);
end;
procedure TMvTests_Engine.Test_LatToStr_DMS;
procedure TMvTests_GeoMath.Test_LatToStr_DMS;
const
NEED_DMS = true;
var
@ -140,7 +140,7 @@ begin
);
end;
procedure TMvTests_Engine.Test_LonToStr_Deg;
procedure TMvTests_GeoMath.Test_LonToStr_Deg;
const
NO_DMS = false;
var
@ -155,7 +155,7 @@ begin
);
end;
procedure TMvTests_Engine.Test_LonToStr_DMS;
procedure TMvTests_GeoMath.Test_LonToStr_DMS;
const
NEED_DMS = true;
var
@ -170,7 +170,7 @@ begin
);
end;
procedure TMvTests_Engine.Test_SplitGPS;
procedure TMvTests_GeoMath.Test_SplitGPS;
const
TOLERANCE = 1e-5;
var
@ -218,7 +218,7 @@ begin
end;
end;
procedure TMvTests_Engine.Test_ZoomFactor;
procedure TMvTests_GeoMath.Test_ZoomFactor;
var
z: Integer;
f: Extended;
@ -236,6 +236,6 @@ initialization
PointFormatSettings.DecimalSeparator := '.';
DMS_Decimals := 4;
RegisterTest(TMvTests_Engine);
RegisterTest(TMvTests_GeoMath);
end.

View File

@ -9,7 +9,7 @@ uses
type
TMvTestsArea= class(TTestCase)
TMvTests_Area= class(TTestCase)
published
procedure Test_PointInArea;
procedure Test_Intersection;
@ -28,7 +28,7 @@ begin
]);
end;
procedure TMvTestsArea.Test_PointInArea;
procedure TMvTests_Area.Test_PointInArea;
var
counter: Integer;
a: TRealArea;
@ -123,7 +123,7 @@ begin
);
end;
procedure TMvTestsArea.Test_Union;
procedure TMvTests_Area.Test_Union;
var
counter: Integer;
a, b, expected, actual: TRealArea;
@ -298,7 +298,7 @@ begin
);
end;
procedure TMvTestsArea.Test_Intersection;
procedure TMvTests_Area.Test_Intersection;
var
counter: Integer;
a, b, expected, actual: TRealArea;
@ -430,7 +430,7 @@ end;
initialization
RegisterTest(TMvTestsArea);
RegisterTest(TMvTests_Area);
end.