2
0

uCoordinate.pas 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. unit uCoordinate;
  2. {$mode Delphi}
  3. interface
  4. uses
  5. Classes, SysUtils, Math;
  6. type
  7. TLongitude_from_LatitudeOverflow_Proc= procedure of object;
  8. { T_Coordinate }
  9. T_Coordinate
  10. =
  11. class
  12. private
  13. //FLatLon_ True: type Latitude -90..+90; False: Longitude -180..+180
  14. FLatLon_: Boolean;
  15. FStr: String;
  16. FSign, FDeg, FMin, FSec: Integer;
  17. FDegrees, FRadians: Extended;
  18. procedure Set_Degrees ( Value: Extended);
  19. procedure Set_Radians( Value: Extended);
  20. function String_to_Sexagesimal: Byte;
  21. procedure Sexagesimal_to_String;
  22. procedure Sexagesimal_to_Degrees;
  23. procedure Degrees_to_Sexagesimal;
  24. procedure Degrees_to_Radians;
  25. procedure Radians_to_Degrees;
  26. function Check_Radians: Boolean;
  27. function Check_Degrees: Boolean;
  28. function Check_Sexagesimal: Byte;
  29. procedure doModify;
  30. public
  31. sinus, cosinus: Extended;
  32. //procedure called by latitude if 90° is exceeded in absolute value
  33. // in this case we make a half turn in longitude and recalculate the latitude
  34. Longitude_from_LatitudeOverflow: TLongitude_from_LatitudeOverflow_Proc; //for the latitude coordinate
  35. procedure Longitude_Turnaround; // for the longitude coordinate, the link is made in Twasm_leaflet
  36. constructor Create( _LatLon_: Boolean);
  37. destructor Destroy; override;
  38. procedure Copy_From( _Coordinate: T_Coordinate);
  39. function Set_To(aSign, aDeg, aMin, aSec: Integer): Byte;
  40. function Set_Str(Value: String): Byte;
  41. property LatLon_: Boolean read FLatLon_;
  42. property Str : String read FStr;
  43. property Sign : integer read FSign;
  44. property Deg : integer read FDeg;
  45. property Min : Integer read FMin;
  46. property Sec : Integer read FSec;
  47. property Degrees : Extended read FDegrees write Set_Degrees ;
  48. property Radians: Extended read FRadians write Set_Radians;
  49. end;
  50. implementation
  51. //from uuStrings.pas
  52. function StrToK( Key: String; var S: String): String;
  53. var
  54. I: Integer;
  55. begin
  56. I:= Pos( Key, S);
  57. if I = 0
  58. then
  59. begin
  60. Result:= S;
  61. S:= '';
  62. end
  63. else
  64. begin
  65. Result:= Copy( S, 1, I-1);
  66. Delete( S, 1, (I-1)+Length( Key));
  67. end;
  68. end;
  69. //from uuStrings.pas
  70. //Like StrToK but takes the first NbCaracteres characters
  71. function StrReadString( var S: String; NbCaracteres: Integer): String;
  72. begin
  73. Result:= Copy( S, 1, NbCaracteres);
  74. Delete( S, 1, NbCaracteres);
  75. end;
  76. //from uuStrings.pas
  77. function IsDigit( S: String): Boolean;
  78. var
  79. I: Integer;
  80. begin
  81. Result:= S <> '';
  82. if not Result then exit;
  83. for I:= 1 to Length( S)
  84. do
  85. begin
  86. Result:= S[I] in ['0'..'9'];
  87. if not Result then break;
  88. end;
  89. end;
  90. //from uuStrings.pas
  91. function IsInt( S: String): Boolean;
  92. var
  93. I, LS: Integer;
  94. begin
  95. Result:= False;
  96. // String length
  97. LS:= Length( S);
  98. if LS = 0 then exit; // empty string
  99. // Evacuation of spaces before the number
  100. I:= 1;
  101. while I <= LS
  102. do
  103. if S[I] = ' '
  104. then
  105. Inc( I)
  106. else
  107. break;
  108. if I > LS then exit; // only spaces
  109. // Process eventual sign
  110. if S[I] = '+'
  111. then
  112. Inc( I)
  113. else if S[I] = '-'
  114. then
  115. Inc( I);
  116. if I > LS then exit; // no digits
  117. // Checking that we only have digits
  118. Result:= True;
  119. while I <= LS
  120. do
  121. if IsDigit(S[I])
  122. then
  123. Inc( I)
  124. else
  125. begin
  126. Result:= False;
  127. break;
  128. end;
  129. end;
  130. { T_Coordinate }
  131. constructor T_Coordinate.Create( _LatLon_: Boolean);
  132. begin
  133. FLatLon_:= _LatLon_;
  134. Longitude_from_LatitudeOverflow:= nil;
  135. end;
  136. destructor T_Coordinate.Destroy;
  137. begin
  138. inherited Destroy;
  139. end;
  140. const
  141. DegSize: array[False..True] of Byte = (4,3); // LatLon_
  142. MinSize= 3;
  143. SecSize= 3;
  144. // 4: incorrect sign, 3: incorrect deg , 2: incorrect Min, 1: incorrect Sec
  145. // 0: OK
  146. function T_Coordinate.String_to_Sexagesimal: Byte;
  147. var
  148. sSign, sDeg, sMin, sSec: String;
  149. S: String;
  150. cSign: Char;
  151. begin
  152. S:= FStr;
  153. sSign:= StrReadString( S, 1);
  154. cSign:= sSign[1];
  155. sDeg:= StrTok( '°', S);
  156. sMin:= StrTok( '''', S);
  157. sSec:= StrTok( '"', S);
  158. if sSec = ''
  159. then
  160. sSec:= '0';
  161. //if not FLatLon_
  162. //then
  163. // begin
  164. // WriteLn(ClassName+'.String_to_Sexagesimal: cSign:'+cSign+' sDeg:'+sDeg+' sMin:'+sMin+' sSec:'+sSec);
  165. // end;
  166. Result:= 4; if (cSign <> '-') and (cSign <> '+') then exit;
  167. Result:= 3; if not IsInt( sDeg) then exit;
  168. Result:= 2; if not IsInt( sMin) then exit;
  169. Result:= 1; if not IsInt( sSec) then exit;
  170. Result:= 0;
  171. case sSign[1]
  172. of
  173. '-': FSign:= -1;
  174. '+': FSign:= +1;
  175. end;
  176. FDeg:= StrToInt( sDeg);
  177. FMin:= StrToInt( sMin);
  178. FSec:= StrToInt( sSec);
  179. end;
  180. procedure T_Coordinate.Sexagesimal_to_String;
  181. var
  182. sSign, sDeg, sMin, sSec: String;
  183. begin
  184. if FSign < 0
  185. then
  186. sSign:= '-'
  187. else
  188. sSign:= '+';
  189. sDeg:= IntToStr(FDeg)+'°';
  190. while Length(sDeg) < DegSize[LatLon_] do sDeg:= ' '+sDeg;
  191. sMin:= IntToStr(FMin)+ '''';
  192. while Length(sMin) < 3 do sMin:= ' '+sMin;
  193. sSec:= IntToStr(FSec)+'"';
  194. while Length(sSec) < 3 do sSec:= ' '+sSec;
  195. FStr:= sSign+sDeg+sMin+sSec;
  196. end;
  197. procedure T_Coordinate.Sexagesimal_to_Degrees;
  198. begin
  199. FDegrees:= FSign*(FDeg+(FMin+FSec/60.0)/60.0);
  200. end;
  201. procedure T_Coordinate.Degrees_to_Sexagesimal;
  202. var
  203. d: Extended;
  204. begin
  205. if FDegrees < 0
  206. then
  207. begin
  208. FSign:= -1;
  209. d:= -FDegrees;
  210. end
  211. else
  212. begin
  213. FSign:= +1;
  214. d:= FDegrees;
  215. end;
  216. FDeg:= Trunc(d);
  217. d:= (d - FDeg) * 60;
  218. FMin:= Trunc(d);
  219. d:= (d - FMin) * 60;
  220. FSec:= Trunc( d);
  221. end;
  222. procedure T_Coordinate.Degrees_to_Radians;
  223. begin
  224. FRadians:= FDegrees * PI /180;
  225. end;
  226. procedure T_Coordinate.Radians_to_Degrees;
  227. begin
  228. FDegrees:= FRadians * 180 / PI;
  229. end;
  230. procedure T_Coordinate.Set_Degrees(Value: Extended);
  231. begin
  232. if FDegrees = Value then exit;
  233. FDegrees:= Value;
  234. Check_Degrees;
  235. Degrees_to_Radians;
  236. Degrees_to_Sexagesimal;Sexagesimal_to_String;
  237. doModify;
  238. end;
  239. procedure T_Coordinate.Set_Radians(Value: Extended);
  240. begin
  241. if FRadians = Value then exit;
  242. FRadians:= Value;
  243. Check_Radians;
  244. Radians_to_Degrees; Degrees_to_Sexagesimal; Sexagesimal_to_String;
  245. doModify;
  246. end;
  247. function T_Coordinate.Set_To(aSign, aDeg, aMin, aSec: Integer): Byte;
  248. begin
  249. Result:= 0;
  250. if (Sign = aSign) and (Deg = aDeg) and (Min = aMin) and (Sec = aSec) then exit;
  251. FSign:= aSign;
  252. FDeg:= aDeg;
  253. FMin:= aMin;
  254. FSec:= aSec;
  255. Sexagesimal_to_Degrees;
  256. if Check_Degrees
  257. then
  258. begin
  259. Result:= Check_Sexagesimal;
  260. Radians_to_Degrees;
  261. Degrees_to_Sexagesimal;
  262. exit;
  263. end;
  264. Degrees_to_Radians;
  265. Sexagesimal_to_String;
  266. doModify;
  267. end;
  268. function T_Coordinate.Set_Str(Value: String): Byte;
  269. begin
  270. Result:= 0;
  271. if FStr = Value then exit;
  272. FStr:= Value;
  273. Result:= String_to_Sexagesimal;
  274. if Result > 0
  275. then
  276. begin
  277. Sexagesimal_to_String;
  278. exit;
  279. end;
  280. Sexagesimal_to_Degrees;
  281. if Check_Degrees
  282. then
  283. begin
  284. Result:= Check_Sexagesimal;
  285. Radians_to_Degrees;
  286. Degrees_to_Sexagesimal;
  287. Sexagesimal_to_String;
  288. exit;
  289. end;
  290. Degrees_to_Radians;
  291. Sexagesimal_to_String;
  292. doModify;
  293. end;
  294. function T_Coordinate.Check_Sexagesimal: Byte;
  295. begin
  296. Result:= 4; if abs(FSign) <> 1 then exit;
  297. if LatLon_
  298. then
  299. begin
  300. Result:= 3; if (FDeg < 0)or( 89 < FDeg ) then exit;
  301. end
  302. else
  303. begin
  304. Result:= 3; if (FDeg < 0)or(179 < FDeg ) then exit;
  305. end;
  306. Result:= 2; if (FMin < 0)or(59 < FMin) then exit;
  307. Result:= 1; if (FSec < 0)or(59 < FMin) then exit;
  308. Result:= 0;
  309. end;
  310. function T_Coordinate.Check_Degrees: Boolean;
  311. begin
  312. Result:= True;
  313. if LatLon_
  314. then
  315. if 90 < FDegrees
  316. then
  317. FDegrees:= 180 - FDegrees
  318. else
  319. if FDegrees < -90
  320. then
  321. FDegrees:= 180 + FDegrees
  322. else
  323. Result:= False
  324. else
  325. if 180 < FDegrees
  326. then
  327. FDegrees:= 360 - FDegrees
  328. else
  329. if FDegrees < -180
  330. then
  331. FDegrees:= 360 + FDegrees
  332. else
  333. Result:= False;
  334. end;
  335. function T_Coordinate.Check_Radians: Boolean;
  336. begin
  337. Result:= True;
  338. if LatLon_
  339. then
  340. if PI/2 < FRadians
  341. then
  342. begin
  343. if Assigned( Longitude_from_LatitudeOverflow)
  344. then
  345. Longitude_from_LatitudeOverflow;
  346. FRadians:= PI - FRadians
  347. end
  348. else
  349. if FRadians < -PI/2
  350. then
  351. begin
  352. if Assigned( Longitude_from_LatitudeOverflow)
  353. then
  354. Longitude_from_LatitudeOverflow;
  355. FRadians:= -PI - FRadians
  356. end
  357. else
  358. Result:= False
  359. else
  360. if PI < FRadians
  361. then
  362. FRadians:= FRadians - 2*PI
  363. else if FRadians < -PI
  364. then
  365. FRadians:= 2*PI + FRadians
  366. else
  367. Result:= False;
  368. end;
  369. procedure T_Coordinate.Longitude_Turnaround;
  370. begin
  371. if LatLon_ then exit; // this only applies to a longitude coordinate.
  372. FRadians:= FRadians + PI;
  373. Check_Radians;
  374. Radians_To_Degrees;
  375. Degrees_to_Sexagesimal;
  376. Sexagesimal_to_String;
  377. doModify;
  378. end;
  379. procedure T_Coordinate.Copy_From( _Coordinate: T_Coordinate);
  380. begin
  381. if _Coordinate = nil then exit;
  382. FLatLon_:= _Coordinate.FLatLon_;
  383. FStr := _Coordinate.FStr ;
  384. FSign := _Coordinate.FSign ;
  385. FDeg := _Coordinate.FDeg ;
  386. FMin := _Coordinate.FMin ;
  387. FSec := _Coordinate.FSec ;
  388. FDegrees := _Coordinate.FDegrees ;
  389. FRadians:= _Coordinate.FRadians;
  390. sinus := _Coordinate.sinus ;
  391. cosinus := _Coordinate.cosinus ;
  392. end;
  393. procedure T_Coordinate.doModify;
  394. begin
  395. SinCos( FRadians, sinus, cosinus);
  396. end;
  397. end.