/GMap.NET.Core/GMap.NET.MapProviders/GoogleMapProvider.cs

# · C# · 1408 lines · 822 code · 160 blank · 426 comment · 157 complexity · cf9bf3bfed3939a4966fde6c71af05d7 MD5 · raw file

Large files are truncated click here to view the full file

  1. namespace GMap.NET.MapProviders
  2. {
  3. using System;
  4. using GMap.NET.Projections;
  5. using System.Security.Cryptography;
  6. using System.Diagnostics;
  7. using System.Net;
  8. using System.IO;
  9. using System.Text.RegularExpressions;
  10. using System.Threading;
  11. using GMap.NET.Internals;
  12. using System.Collections.Generic;
  13. using System.Globalization;
  14. using System.Xml;
  15. public abstract class GoogleMapProviderBase : GMapProvider, RoutingProvider, GeocodingProvider, DirectionsProvider
  16. {
  17. public GoogleMapProviderBase()
  18. {
  19. MaxZoom = null;
  20. RefererUrl = string.Format("http://maps.{0}/", Server);
  21. Copyright = string.Format("Š{0} Google - Map data Š{0} Tele Atlas, Imagery Š{0} TerraMetrics", DateTime.Today.Year);
  22. }
  23. public readonly string Server = ThisIsLegalString("zOl/KnHzebJUqs6JWROaCQ==");
  24. public readonly string ServerChina = ThisIsLegalString("zOl/KnHzebLqgdc2FRlQHg==");
  25. public readonly string ServerKorea = ThisIsLegalString("ecw6OdJzJ/zgnFTB90qgtw==");
  26. public readonly string ServerKoreaKr = ThisIsLegalString("zOl/KnHzebIhmuu+tK5lbw==");
  27. public string SecureWord = "Galileo";
  28. /// <summary>
  29. /// API generated using http://greatmaps.codeplex.com/
  30. /// from http://tinyurl.com/3q6zhcw <- http://code.server.com/intl/en-us/apis/maps/signup.html
  31. /// </summary>
  32. public string APIKey = @"ABQIAAAAWaQgWiEBF3lW97ifKnAczhRAzBk5Igf8Z5n2W3hNnMT0j2TikxTLtVIGU7hCLLHMAuAMt-BO5UrEWA";
  33. #region GMapProvider Members
  34. public override Guid Id
  35. {
  36. get
  37. {
  38. throw new NotImplementedException();
  39. }
  40. }
  41. public override string Name
  42. {
  43. get
  44. {
  45. throw new NotImplementedException();
  46. }
  47. }
  48. public override PureProjection Projection
  49. {
  50. get
  51. {
  52. return MercatorProjection.Instance;
  53. }
  54. }
  55. GMapProvider[] overlays;
  56. public override GMapProvider[] Overlays
  57. {
  58. get
  59. {
  60. if(overlays == null)
  61. {
  62. overlays = new GMapProvider[] { this };
  63. }
  64. return overlays;
  65. }
  66. }
  67. public override PureImage GetTileImage(GPoint pos, int zoom)
  68. {
  69. throw new NotImplementedException();
  70. }
  71. #endregion
  72. public bool TryCorrectVersion = true;
  73. static bool init = false;
  74. public override void OnInitialized()
  75. {
  76. if(!init && TryCorrectVersion)
  77. {
  78. string url = string.Format("http://maps.{0}", Server);
  79. try
  80. {
  81. string html = Cache.Instance.GetContent(url, CacheType.UrlCache, TimeSpan.FromHours(8));
  82. if(string.IsNullOrEmpty(html))
  83. {
  84. html = GetContentUsingHttp(url);
  85. if(!string.IsNullOrEmpty(html))
  86. {
  87. Cache.Instance.SaveContent(url, CacheType.UrlCache, html);
  88. }
  89. }
  90. if(!string.IsNullOrEmpty(html))
  91. {
  92. #region -- match versions --
  93. Regex reg = new Regex(string.Format("\"*http://mt0.{0}/vt/lyrs=m@(\\d*)", Server), RegexOptions.IgnoreCase);
  94. Match mat = reg.Match(html);
  95. if(mat.Success)
  96. {
  97. GroupCollection gc = mat.Groups;
  98. int count = gc.Count;
  99. if(count > 0)
  100. {
  101. string ver = string.Format("m@{0}", gc[1].Value);
  102. string old = GMapProviders.GoogleMap.Version;
  103. GMapProviders.GoogleMap.Version = ver;
  104. GMapProviders.GoogleChinaMap.Version = ver;
  105. Debug.WriteLine("GMapProviders.GoogleMap.Version: " + ver + ", " + (ver == old ? "OK" : "old: " + old + ", consider updating source"));
  106. if(Debugger.IsAttached && ver != old)
  107. {
  108. Thread.Sleep(5555);
  109. }
  110. }
  111. }
  112. reg = new Regex(string.Format("\"*http://mt0.{0}/vt/lyrs=h@(\\d*)", Server), RegexOptions.IgnoreCase);
  113. mat = reg.Match(html);
  114. if(mat.Success)
  115. {
  116. GroupCollection gc = mat.Groups;
  117. int count = gc.Count;
  118. if(count > 0)
  119. {
  120. string ver = string.Format("h@{0}", gc[1].Value);
  121. string old = GMapProviders.GoogleHybridMap.Version;
  122. GMapProviders.GoogleHybridMap.Version = ver;
  123. GMapProviders.GoogleChinaHybridMap.Version = ver;
  124. Debug.WriteLine("GMapProviders.GoogleHybridMap.Version: " + ver + ", " + (ver == old ? "OK" : "old: " + old + ", consider updating source"));
  125. if(Debugger.IsAttached && ver != old)
  126. {
  127. Thread.Sleep(5555);
  128. }
  129. }
  130. }
  131. reg = new Regex(string.Format("\"*http://khm0.{0}/kh/v=(\\d*)", Server), RegexOptions.IgnoreCase);
  132. mat = reg.Match(html);
  133. if(mat.Success)
  134. {
  135. GroupCollection gc = mat.Groups;
  136. int count = gc.Count;
  137. if(count > 0)
  138. {
  139. string ver = gc[1].Value;
  140. string old = GMapProviders.GoogleSatelliteMap.Version;
  141. GMapProviders.GoogleSatelliteMap.Version = ver;
  142. GMapProviders.GoogleKoreaSatelliteMap.Version = ver;
  143. GMapProviders.GoogleChinaSatelliteMap.Version = "s@" + ver;
  144. Debug.WriteLine("GMapProviders.GoogleSatelliteMap.Version: " + ver + ", " + (ver == old ? "OK" : "old: " + old + ", consider updating source"));
  145. if(Debugger.IsAttached && ver != old)
  146. {
  147. Thread.Sleep(5555);
  148. }
  149. }
  150. }
  151. reg = new Regex(string.Format("\"*http://mt0.{0}/vt/lyrs=t@(\\d*),r@(\\d*)", Server), RegexOptions.IgnoreCase);
  152. mat = reg.Match(html);
  153. if(mat.Success)
  154. {
  155. GroupCollection gc = mat.Groups;
  156. int count = gc.Count;
  157. if(count > 1)
  158. {
  159. string ver = string.Format("t@{0},r@{1}", gc[1].Value, gc[2].Value);
  160. string old = GMapProviders.GoogleTerrainMap.Version;
  161. GMapProviders.GoogleTerrainMap.Version = ver;
  162. GMapProviders.GoogleChinaTerrainMap.Version = ver;
  163. Debug.WriteLine("GMapProviders.GoogleTerrainMap.Version: " + ver + ", " + (ver == old ? "OK" : "old: " + old + ", consider updating source"));
  164. if(Debugger.IsAttached && ver != old)
  165. {
  166. Thread.Sleep(5555);
  167. }
  168. }
  169. }
  170. #endregion
  171. }
  172. init = true; // try it only once
  173. }
  174. catch(Exception ex)
  175. {
  176. Debug.WriteLine("TryCorrectGoogleVersions failed: " + ex.ToString());
  177. }
  178. }
  179. }
  180. internal void GetSecureWords(GPoint pos, out string sec1, out string sec2)
  181. {
  182. sec1 = string.Empty; // after &x=...
  183. sec2 = string.Empty; // after &zoom=...
  184. int seclen = (int)((pos.X * 3) + pos.Y) % 8;
  185. sec2 = SecureWord.Substring(0, seclen);
  186. if(pos.Y >= 10000 && pos.Y < 100000)
  187. {
  188. sec1 = Sec1;
  189. }
  190. }
  191. static readonly string Sec1 = "&s=";
  192. #region -- encryption --
  193. static string EncryptString(string Message, string Passphrase)
  194. {
  195. byte[] Results;
  196. System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();
  197. // Step 1. We hash the passphrase using MD5
  198. // We use the MD5 hash generator as the result is a 128 bit byte array
  199. // which is a valid length for the TripleDES encoder we use below
  200. using(MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider())
  201. {
  202. byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase));
  203. // Step 2. Create a new TripleDESCryptoServiceProvider object
  204. using(TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider())
  205. {
  206. // Step 3. Setup the encoder
  207. TDESAlgorithm.Key = TDESKey;
  208. TDESAlgorithm.Mode = CipherMode.ECB;
  209. TDESAlgorithm.Padding = PaddingMode.PKCS7;
  210. // Step 4. Convert the input string to a byte[]
  211. byte[] DataToEncrypt = UTF8.GetBytes(Message);
  212. // Step 5. Attempt to encrypt the string
  213. try
  214. {
  215. using(ICryptoTransform Encryptor = TDESAlgorithm.CreateEncryptor())
  216. {
  217. Results = Encryptor.TransformFinalBlock(DataToEncrypt, 0, DataToEncrypt.Length);
  218. }
  219. }
  220. finally
  221. {
  222. // Clear the TripleDes and Hashprovider services of any sensitive information
  223. TDESAlgorithm.Clear();
  224. HashProvider.Clear();
  225. }
  226. }
  227. }
  228. // Step 6. Return the encrypted string as a base64 encoded string
  229. return Convert.ToBase64String(Results);
  230. }
  231. static string DecryptString(string Message, string Passphrase)
  232. {
  233. byte[] Results;
  234. System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();
  235. // Step 1. We hash the passphrase using MD5
  236. // We use the MD5 hash generator as the result is a 128 bit byte array
  237. // which is a valid length for the TripleDES encoder we use below
  238. using(MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider())
  239. {
  240. byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase));
  241. // Step 2. Create a new TripleDESCryptoServiceProvider object
  242. using(TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider())
  243. {
  244. // Step 3. Setup the decoder
  245. TDESAlgorithm.Key = TDESKey;
  246. TDESAlgorithm.Mode = CipherMode.ECB;
  247. TDESAlgorithm.Padding = PaddingMode.PKCS7;
  248. // Step 4. Convert the input string to a byte[]
  249. byte[] DataToDecrypt = Convert.FromBase64String(Message);
  250. // Step 5. Attempt to decrypt the string
  251. try
  252. {
  253. using(ICryptoTransform Decryptor = TDESAlgorithm.CreateDecryptor())
  254. {
  255. Results = Decryptor.TransformFinalBlock(DataToDecrypt, 0, DataToDecrypt.Length);
  256. }
  257. }
  258. finally
  259. {
  260. // Clear the TripleDes and Hashprovider services of any sensitive information
  261. TDESAlgorithm.Clear();
  262. HashProvider.Clear();
  263. }
  264. }
  265. }
  266. // Step 6. Return the decrypted string in UTF8 format
  267. return UTF8.GetString(Results, 0, Results.Length);
  268. }
  269. public static string EncryptString(string Message)
  270. {
  271. return EncryptString(Message, manifesto);
  272. }
  273. public static string ThisIsLegalString(string Message)
  274. {
  275. return DecryptString(Message, manifesto);
  276. }
  277. static readonly string manifesto = "GMap.NET is great and Powerful, Free, cross platform, open source .NET control.";
  278. #endregion
  279. #region RoutingProvider Members
  280. public MapRoute GetRoute(PointLatLng start, PointLatLng end, bool avoidHighways, bool walkingMode, int Zoom)
  281. {
  282. string tooltip;
  283. int numLevels;
  284. int zoomFactor;
  285. MapRoute ret = null;
  286. List<PointLatLng> points = GetRoutePoints(MakeRouteUrl(start, end, LanguageStr, avoidHighways, walkingMode), Zoom, out tooltip, out numLevels, out zoomFactor);
  287. if(points != null)
  288. {
  289. ret = new MapRoute(points, tooltip);
  290. }
  291. return ret;
  292. }
  293. public MapRoute GetRoute(string start, string end, bool avoidHighways, bool walkingMode, int Zoom)
  294. {
  295. string tooltip;
  296. int numLevels;
  297. int zoomFactor;
  298. MapRoute ret = null;
  299. List<PointLatLng> points = GetRoutePoints(MakeRouteUrl(start, end, LanguageStr, avoidHighways, walkingMode), Zoom, out tooltip, out numLevels, out zoomFactor);
  300. if(points != null)
  301. {
  302. ret = new MapRoute(points, tooltip);
  303. }
  304. return ret;
  305. }
  306. #region -- internals --
  307. string MakeRouteUrl(PointLatLng start, PointLatLng end, string language, bool avoidHighways, bool walkingMode)
  308. {
  309. string opt = walkingMode ? WalkingStr : (avoidHighways ? RouteWithoutHighwaysStr : RouteStr);
  310. return string.Format(CultureInfo.InvariantCulture, RouteUrlFormatPointLatLng, language, opt, start.Lat, start.Lng, end.Lat, end.Lng, Server);
  311. }
  312. string MakeRouteUrl(string start, string end, string language, bool avoidHighways, bool walkingMode)
  313. {
  314. string opt = walkingMode ? WalkingStr : (avoidHighways ? RouteWithoutHighwaysStr : RouteStr);
  315. return string.Format(RouteUrlFormatStr, language, opt, start.Replace(' ', '+'), end.Replace(' ', '+'), Server);
  316. }
  317. List<PointLatLng> GetRoutePoints(string url, int zoom, out string tooltipHtml, out int numLevel, out int zoomFactor)
  318. {
  319. List<PointLatLng> points = null;
  320. tooltipHtml = string.Empty;
  321. numLevel = -1;
  322. zoomFactor = -1;
  323. try
  324. {
  325. string urlEnd = url.Substring(url.IndexOf("&hl="));
  326. string route = GMaps.Instance.UseRouteCache ? Cache.Instance.GetContent(urlEnd, CacheType.RouteCache) : string.Empty;
  327. if(string.IsNullOrEmpty(route))
  328. {
  329. route = GetContentUsingHttp(url);
  330. if(!string.IsNullOrEmpty(route))
  331. {
  332. if(GMaps.Instance.UseRouteCache)
  333. {
  334. Cache.Instance.SaveContent(urlEnd, CacheType.RouteCache, route);
  335. }
  336. }
  337. }
  338. // parse values
  339. if(!string.IsNullOrEmpty(route))
  340. {
  341. //{
  342. //tooltipHtml:" (300\x26#160;km / 2 valandos 59 min.)",
  343. //polylines:
  344. //[{
  345. // id:"route0",
  346. // points:"cy~rIcvp`ClJ~v@jHpu@N|BB~A?tA_@`J@nAJrB|AhEf@h@~@^pANh@Mr@a@`@_@x@cBPk@ZiBHeDQ{C]wAc@mAqCeEoA_C{@_Cy@iDoEaW}AsJcJ}t@iWowB{C_Vyw@gvGyTyjBu@gHwDoZ{W_zBsX}~BiA_MmAyOcAwOs@yNy@eTk@mVUmTE}PJ_W`@cVd@cQ`@}KjA_V`AeOn@oItAkOdAaKfBaOhDiVbD}RpBuKtEkTtP}q@fr@ypCfCmK|CmNvEqVvCuQ`BgLnAmJ`CgTpA_N~@sLlBwYh@yLp@cSj@e]zFkzKHaVViSf@wZjFwqBt@{Wr@qS`AaUjAgStBkYrEwe@xIuw@`Gmj@rFok@~BkYtCy_@|KccBvBgZjC}[tD__@pDaYjB_MpBuLhGi[fC}KfFcSnEkObFgOrFkOzEoLt[ys@tJeUlIsSbKqXtFiPfKi]rG_W|CiNhDkPfDuQlDoShEuXrEy[nOgiAxF{`@|DoVzFk[fDwPlXupA~CoPfDuQxGcd@l@yEdH{r@xDam@`AiWz@mYtAq~@p@uqAfAqx@|@kZxA}^lBq\\|Be\\lAaO~Dm`@|Gsj@tS_~AhCyUrCeZrByWv@uLlUiyDpA}NdHkn@pGmb@LkAtAoIjDqR`I{`@`BcH|I_b@zJcd@lKig@\\_CbBaIlJ}g@lIoj@pAuJtFoh@~Eqs@hDmv@h@qOfF{jBn@gSxCio@dAuQn@gIVoBjAiOlCqWbCiT`PekAzKiu@~EgYfIya@fA{ExGwWnDkMdHiU|G}R`HgQhRsa@hW}g@jVsg@|a@cbAbJkUxKoYxLa_@`IiZzHu[`DoOXsBhBuJbCwNdBaL`EkYvAwM`CeVtEwj@nDqj@BkAnB{YpGgeAn@eJ`CmYvEid@tBkQpGkd@rE}UxB}JdJo_@nDcNfSan@nS}j@lCeIvDsMbC{J|CyNbAwFfCgPz@uGvBiSdD}`@rFon@nKaqAxDmc@xBuT|Fqc@nC_PrEcUtC_MpFcT`GqQxJmXfXwq@jQgh@hBeGhG_U|BaK|G}[nRikAzIam@tDsYfE}^v@_MbAwKn@oIr@yLrBub@jAoa@b@sRdDmjBx@aZdA}XnAqVpAgTlAqPn@oGvFye@dCeRzGwb@xT_}A`BcPrAoOvCad@jAmXv@eV`BieA~@a[fBg_@`CiZ~A_OhHqk@hHcn@tEwe@rDub@nBoW~@sN|BeZnAgMvDm\\hFs^hSigArFaY`Gc\\`C}OhD}YfByQdAaNbAkOtOu~Cn@wKz@uLfCeY|CkW~B}OhCmO|AcI~A_IvDoPpEyPdImWrDuKnL_YjI{Ptl@qfAle@u|@xI}PbImQvFwMbGgOxFkOpdAosCdD_KxGsU|E}RxFcXhCwNjDwTvBiPfBqOrAyMfBcTxAaVhAwVrCy_Al@iPt@_OtA}Q`AuJ`AgIzAkK`EoUtBsJhCaKxCaKdDaKhQeg@jGiRfGaSrFyR`HsWvL}f@xp@grC`Sq|@pEsVdAoGjF{XlkAgwHxHgj@|Jex@fg@qlEjQs{AdHwh@zDkVhEkVzI_e@v}AgzHpK_l@tE}YtEy[rC}TpFme@jg@cpEbF{d@~BoXfBqUbAyOx@yN|Ao]bAo[tIazC`@iLb@aJ~AkWbBgRdBgPjA{IdCePlAmHfBmJdCiL~CuM|DoNxhDezKdDkLvBoInFqVbCuMxBqNnAeJ~CwXdBoSb^crElFsl@`Dy[zDu^xBiRzc@aaE|Fsd@vCkShDmTpG}^lD}QzDoR|zAcdHvIob@dKoj@jDmSlKiq@xVacBhEqXnBqL|Ga^zJke@`y@ktD~Mop@tP}_AdOg`AtCiQxCyOlDkPfDoN`GiTfGkRjEwLvEsL|HkQtEkJdE{HrwAkaCrT{a@rpDiuHtE_KvLuV|{AwaDzAqCb@mAf{Ac`D~FqL~y@_fBlNmZbGaNtF}Mpn@s~AlYss@dFgK|DoGhBoCrDuE~AcBtGaGnByAnDwBnCwAfDwAnFaBjGkA~[{E`iEkn@pQaDvIwBnIiCl\\qLn}J{pDhMcGrFcDhGeEvoDehC|AsArCwChBaC`C_EzC_HbBcFd@uB`@qAn@gDdB}Kz@}Hn@iPjByx@jDcvAj@}RDsEn@yTv@a]VcPtEamFBcHT_LNkEdAiShDsi@`GudAbFgx@`@iKdP}yFhBgs@p@yRjCo_AJwCXeEb@uEz@_H|@yEnBqHrCiIpAmE`o@qhBxC_IjIuVdIcXh{AgmG`i@_{BfCuLrhAssGfFeXxbBklInCsN|_AoiGpGs_@pl@w}Czy@_kEvG{]h}@ieFbQehAdHye@lPagA|Eu\\tAmI|CwWjn@mwGj@eH|]azFl@kPjAqd@jJe|DlD}vAxAeh@@eBvVk}JzIkqDfE_aBfA{YbBk[zp@e}LhAaObCeUlAuIzAeJrb@q`CjCcOnAaIpBwOtBkTjDsg@~AiPvBwOlAcH|AkIlCkLlYudApDoN`BgHhBaJvAeIvAqJbAuHrBqQbAsLx@oL`MwrCXkFr@uJh@{FhBsOvXwoB|EqVdBmHxC}KtCcJtDgKjDoIxE}JdHcMdCuDdIoKlmB}|BjJuMfFgIlE{HlEyIdEeJ~FaOvCgInCuI`EmN`J}]rEsP`EuMzCoIxGwPpi@cnAhGgPzCiJvFmRrEwQbDyOtCoPbDwTxDq\\rAsK`BgLhB{KxBoLfCgLjDqKdBqEfEkJtSy^`EcJnDuJjAwDrCeK\\}AjCaNr@qEjAaJtNaqAdCqQ`BsItS}bAbQs{@|Kor@xBmKz}@}uDze@{zAjk@}fBjTsq@r@uCd@aDFyCIwCWcCY}Aq_@w|A{AwF_DyHgHwOgu@m_BSb@nFhL",
  347. // levels:"B?@?????@?@???A???@?@????@??@????????@????@???A????@????@??@???@??@???A???@??@???A??@???@????A??@???@??@????@??@???@????@???@??A@?@???@????A????@??@?@???@???????@??@?@????@????@?A??@???@????@??@?A??????@???????@??A???@??@???@??@????@??@?@?????@?@?A?@????@???@??@??@????@?@??@?@??@??????@???@?@????@???B???@??@??????@??@???A?????@????@???A??@??????@??@??A?@???@???@??A????@???@???@????A????@@??A???@???@??@??A????@??????@??@???@???B????@?@????????@????@????A?????@????@??A???@???@???B???@?????@???@????@????@???A???????@??A@??@?@??@@?????A?@@????????@??@?A????@?????@???@???@???@???@?@?A???@??@?@??@???@?????@???A??@???????@????@???@????@????@@???A????@?@??@?B",
  348. // numLevels:4,
  349. // zoomFactor:16
  350. //}]
  351. //}
  352. #region -- title --
  353. int tooltipEnd = 0;
  354. {
  355. int x = route.IndexOf("tooltipHtml:") + 13;
  356. if(x >= 13)
  357. {
  358. tooltipEnd = route.IndexOf("\"", x + 1);
  359. if(tooltipEnd > 0)
  360. {
  361. int l = tooltipEnd - x;
  362. if(l > 0)
  363. {
  364. tooltipHtml = route.Substring(x, l).Replace(@"\x26#160;", " ");
  365. }
  366. }
  367. }
  368. }
  369. #endregion
  370. #region -- points --
  371. int pointsEnd = 0;
  372. {
  373. int x = route.IndexOf("points:", tooltipEnd >= 0 ? tooltipEnd : 0) + 8;
  374. if(x >= 8)
  375. {
  376. pointsEnd = route.IndexOf("\"", x + 1);
  377. if(pointsEnd > 0)
  378. {
  379. int l = pointsEnd - x;
  380. if(l > 0)
  381. {
  382. /*
  383. while(l % 5 != 0)
  384. {
  385. l--;
  386. }
  387. */
  388. points = new List<PointLatLng>();
  389. DecodePointsInto(points, route.Substring(x, l));
  390. }
  391. }
  392. }
  393. }
  394. #endregion
  395. #region -- levels --
  396. string levels = string.Empty;
  397. int levelsEnd = 0;
  398. {
  399. int x = route.IndexOf("levels:", pointsEnd >= 0 ? pointsEnd : 0) + 8;
  400. if(x >= 8)
  401. {
  402. levelsEnd = route.IndexOf("\"", x + 1);
  403. if(levelsEnd > 0)
  404. {
  405. int l = levelsEnd - x;
  406. if(l > 0)
  407. {
  408. levels = route.Substring(x, l);
  409. }
  410. }
  411. }
  412. }
  413. #endregion
  414. #region -- numLevel --
  415. int numLevelsEnd = 0;
  416. {
  417. int x = route.IndexOf("numLevels:", levelsEnd >= 0 ? levelsEnd : 0) + 10;
  418. if(x >= 10)
  419. {
  420. numLevelsEnd = route.IndexOf(",", x);
  421. if(numLevelsEnd > 0)
  422. {
  423. int l = numLevelsEnd - x;
  424. if(l > 0)
  425. {
  426. numLevel = int.Parse(route.Substring(x, l));
  427. }
  428. }
  429. }
  430. }
  431. #endregion
  432. #region -- zoomFactor --
  433. {
  434. int x = route.IndexOf("zoomFactor:", numLevelsEnd >= 0 ? numLevelsEnd : 0) + 11;
  435. if(x >= 11)
  436. {
  437. int end = route.IndexOf("}", x);
  438. if(end > 0)
  439. {
  440. int l = end - x;
  441. if(l > 0)
  442. {
  443. zoomFactor = int.Parse(route.Substring(x, l));
  444. }
  445. }
  446. }
  447. }
  448. #endregion
  449. #region -- trim point overload --
  450. if(points != null && numLevel > 0 && !string.IsNullOrEmpty(levels))
  451. {
  452. if(points.Count - levels.Length > 0)
  453. {
  454. points.RemoveRange(levels.Length, points.Count - levels.Length);
  455. }
  456. //http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline/description.html
  457. //
  458. string allZlevels = "TSRPONMLKJIHGFEDCBA@?";
  459. if(numLevel > allZlevels.Length)
  460. {
  461. numLevel = allZlevels.Length;
  462. }
  463. // used letters in levels string
  464. string pLevels = allZlevels.Substring(allZlevels.Length - numLevel);
  465. // remove useless points at zoom
  466. {
  467. List<PointLatLng> removedPoints = new List<PointLatLng>();
  468. for(int i = 0; i < levels.Length; i++)
  469. {
  470. int zi = pLevels.IndexOf(levels[i]);
  471. if(zi > 0)
  472. {
  473. if(zi * numLevel > zoom)
  474. {
  475. removedPoints.Add(points[i]);
  476. }
  477. }
  478. }
  479. foreach(var v in removedPoints)
  480. {
  481. points.Remove(v);
  482. }
  483. removedPoints.Clear();
  484. removedPoints = null;
  485. }
  486. }
  487. #endregion
  488. }
  489. }
  490. catch(Exception ex)
  491. {
  492. points = null;
  493. Debug.WriteLine("GetRoutePoints: " + ex);
  494. }
  495. return points;
  496. }
  497. static readonly string RouteUrlFormatPointLatLng = "http://maps.{6}/maps?f=q&output=dragdir&doflg=p&hl={0}{1}&q=&saddr=@{2},{3}&daddr=@{4},{5}";
  498. static readonly string RouteUrlFormatStr = "http://maps.{4}/maps?f=q&output=dragdir&doflg=p&hl={0}{1}&q=&saddr=@{2}&daddr=@{3}";
  499. static readonly string WalkingStr = "&mra=ls&dirflg=w";
  500. static readonly string RouteWithoutHighwaysStr = "&mra=ls&dirflg=dh";
  501. static readonly string RouteStr = "&mra=ls&dirflg=d";
  502. #endregion
  503. #endregion
  504. #region GeocodingProvider Members
  505. public GeoCoderStatusCode GetPoints(string keywords, out List<PointLatLng> pointList)
  506. {
  507. return GetLatLngFromGeocoderUrl(MakeGeocoderUrl(keywords, LanguageStr), out pointList);
  508. }
  509. public PointLatLng? GetPoint(string keywords, out GeoCoderStatusCode status)
  510. {
  511. List<PointLatLng> pointList;
  512. status = GetPoints(keywords, out pointList);
  513. return pointList != null && pointList.Count > 0 ? pointList[0] : (PointLatLng?)null;
  514. }
  515. public GeoCoderStatusCode GetPlacemarks(PointLatLng location, out List<Placemark> placemarkList)
  516. {
  517. return GetPlacemarkFromReverseGeocoderUrl(MakeReverseGeocoderUrl(location, LanguageStr), out placemarkList);
  518. }
  519. public Placemark GetPlacemark(PointLatLng location, out GeoCoderStatusCode status)
  520. {
  521. List<Placemark> pointList;
  522. status = GetPlacemarks(location, out pointList);
  523. return pointList != null && pointList.Count > 0 ? pointList[0] : null;
  524. }
  525. #region -- internals --
  526. // TODO: switch to Geocoding API
  527. // The Google Geocoding API: http://code.google.com/apis/maps/documentation/geocoding/
  528. string MakeGeocoderUrl(string keywords, string language)
  529. {
  530. return string.Format(GeocoderUrlFormat, keywords.Replace(' ', '+'), language, APIKey, Server);
  531. }
  532. string MakeReverseGeocoderUrl(PointLatLng pt, string language)
  533. {
  534. return string.Format(CultureInfo.InvariantCulture, ReverseGeocoderUrlFormat, language, pt.Lat, pt.Lng, APIKey, Server);
  535. }
  536. GeoCoderStatusCode GetLatLngFromGeocoderUrl(string url, out List<PointLatLng> pointList)
  537. {
  538. var status = GeoCoderStatusCode.Unknow;
  539. pointList = null;
  540. try
  541. {
  542. string urlEnd = url.Substring(url.IndexOf("geo?q="));
  543. string geo = GMaps.Instance.UseGeocoderCache ? Cache.Instance.GetContent(urlEnd, CacheType.GeocoderCache) : string.Empty;
  544. bool cache = false;
  545. if(string.IsNullOrEmpty(geo))
  546. {
  547. geo = GetContentUsingHttp(url);
  548. if(!string.IsNullOrEmpty(geo))
  549. {
  550. cache = true;
  551. }
  552. }
  553. if(!string.IsNullOrEmpty(geo))
  554. {
  555. if(geo.StartsWith("200"))
  556. {
  557. // true : 200,4,56.1451640,22.0681787
  558. // false: 602,0,0,0
  559. string[] values = geo.Split(',');
  560. if(values.Length == 4)
  561. {
  562. status = (GeoCoderStatusCode)int.Parse(values[0]);
  563. if(status == GeoCoderStatusCode.G_GEO_SUCCESS)
  564. {
  565. if(cache && GMaps.Instance.UseGeocoderCache)
  566. {
  567. Cache.Instance.SaveContent(urlEnd, CacheType.GeocoderCache, geo);
  568. }
  569. double lat = double.Parse(values[2], CultureInfo.InvariantCulture);
  570. double lng = double.Parse(values[3], CultureInfo.InvariantCulture);
  571. pointList = new List<PointLatLng>();
  572. pointList.Add(new PointLatLng(lat, lng));
  573. }
  574. }
  575. }
  576. else if(geo.StartsWith("<?xml"))
  577. {
  578. #region -- kml response --
  579. //<?xml version="1.0" encoding="UTF-8" ?>
  580. //<kml xmlns="http://earth.google.com/kml/2.0">
  581. //<Response>
  582. // <name>Lithuania, Vilnius</name>
  583. // <Status>
  584. // <code>200</code>
  585. // <request>geocode</request>
  586. // </Status>
  587. // <Placemark id="p1">
  588. // <address>Vilnius, Lithuania</address>
  589. // <AddressDetails Accuracy="4" xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0">
  590. // <Country>
  591. // <CountryNameCode>LT</CountryNameCode>
  592. // <CountryName>Lithuania</CountryName>
  593. // <SubAdministrativeArea>
  594. // <SubAdministrativeAreaName>Vilnius Region</SubAdministrativeAreaName>
  595. // <Locality>
  596. // <LocalityName>Vilnius</LocalityName>
  597. // </Locality>
  598. // </SubAdministrativeArea>
  599. // </Country>
  600. // </AddressDetails>
  601. // <ExtendedData>
  602. // <LatLonBox north="54.8616279" south="54.4663633" east="25.4839269" west="24.9688846" />
  603. // </ExtendedData>
  604. // <Point><coordinates>25.2800243,54.6893865,0</coordinates></Point>
  605. // </Placemark>
  606. //</Response>
  607. //</kml>
  608. #endregion
  609. XmlDocument doc = new XmlDocument();
  610. doc.LoadXml(geo);
  611. XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable);
  612. nsMgr.AddNamespace("sm", string.Format("http://earth.{0}/kml/2.0", Server));
  613. nsMgr.AddNamespace("sn", "urn:oasis:names:tc:ciq:xsdschema:xAL:2.0");
  614. XmlNode nn = doc.SelectSingleNode("//sm:Status/sm:code", nsMgr);
  615. if(nn != null)
  616. {
  617. status = (GeoCoderStatusCode)int.Parse(nn.InnerText);
  618. if(status == GeoCoderStatusCode.G_GEO_SUCCESS)
  619. {
  620. if(cache && GMaps.Instance.UseGeocoderCache)
  621. {
  622. Cache.Instance.SaveContent(urlEnd, CacheType.GeocoderCache, geo);
  623. }
  624. pointList = new List<PointLatLng>();
  625. XmlNodeList l = doc.SelectNodes("/sm:kml/sm:Response/sm:Placemark", nsMgr);
  626. if(l != null)
  627. {
  628. foreach(XmlNode n in l)
  629. {
  630. nn = n.SelectSingleNode("sm:Point/sm:coordinates", nsMgr);
  631. if(nn != null)
  632. {
  633. string[] values = nn.InnerText.Split(',');
  634. if(values.Length >= 2)
  635. {
  636. double lat = double.Parse(values[1], CultureInfo.InvariantCulture);
  637. double lng = double.Parse(values[0], CultureInfo.InvariantCulture);
  638. pointList.Add(new PointLatLng(lat, lng));
  639. }
  640. }
  641. }
  642. }
  643. }
  644. }
  645. }
  646. }
  647. }
  648. catch(Exception ex)
  649. {
  650. status = GeoCoderStatusCode.ExceptionInCode;
  651. Debug.WriteLine("GetLatLngFromGeocoderUrl: " + ex);
  652. }
  653. return status;
  654. }
  655. GeoCoderStatusCode GetPlacemarkFromReverseGeocoderUrl(string url, out List<Placemark> placemarkList)
  656. {
  657. GeoCoderStatusCode status = GeoCoderStatusCode.Unknow;
  658. placemarkList = null;
  659. try
  660. {
  661. string urlEnd = url.Substring(url.IndexOf("geo?hl="));
  662. string reverse = GMaps.Instance.UsePlacemarkCache ? Cache.Instance.GetContent(urlEnd, CacheType.PlacemarkCache) : string.Empty;
  663. bool cache = false;
  664. if(string.IsNullOrEmpty(reverse))
  665. {
  666. reverse = GetContentUsingHttp(url);
  667. if(!string.IsNullOrEmpty(reverse))
  668. {
  669. cache = true;
  670. }
  671. }
  672. if(!string.IsNullOrEmpty(reverse))
  673. {
  674. if(reverse.StartsWith("200"))
  675. {
  676. if(cache && GMaps.Instance.UsePlacemarkCache)
  677. {
  678. Cache.Instance.SaveContent(urlEnd, CacheType.PlacemarkCache, reverse);
  679. }
  680. string acc = reverse.Substring(0, reverse.IndexOf('\"'));
  681. var ret = new Placemark(reverse.Substring(reverse.IndexOf('\"')));
  682. ret.Accuracy = int.Parse(acc.Split(',').GetValue(1) as string);
  683. placemarkList = new List<Placemark>();
  684. placemarkList.Add(ret);
  685. status = GeoCoderStatusCode.G_GEO_SUCCESS;
  686. }
  687. else if(reverse.StartsWith("<?xml"))
  688. {
  689. #region -- kml version --
  690. #region -- kml response --
  691. //<?xml version="1.0" encoding="UTF-8" ?>
  692. //<kml xmlns="http://earth.server.com/kml/2.0">
  693. // <Response>
  694. // <name>55.023322,24.668408</name>
  695. // <Status>
  696. // <code>200</code>
  697. // <request>geocode</request>
  698. // </Status>
  699. // <Placemark id="p1">
  700. // <address>4313, Širvintos 19023, Lithuania</address>
  701. // <AddressDetails Accuracy="6" xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"><Country><CountryNameCode>LT</CountryNameCode><CountryName>Lithuania</CountryName><SubAdministrativeArea><SubAdministrativeAreaName>Vilnius Region</SubAdministrativeAreaName><Locality><LocalityName>Širvintos</LocalityName><Thoroughfare><ThoroughfareName>4313</ThoroughfareName></Thoroughfare><PostalCode><PostalCodeNumber>19023</PostalCodeNumber></PostalCode></Locality></SubAdministrativeArea></Country></AddressDetails>
  702. // <ExtendedData>
  703. // <LatLonBox north="55.0270661" south="55.0207709" east="24.6711965" west="24.6573382" />
  704. // </ExtendedData>
  705. // <Point><coordinates>24.6642677,55.0239187,0</coordinates></Point>
  706. // </Placemark>
  707. // <Placemark id="p2">
  708. // <address>Širvintos 19023, Lithuania</address>
  709. // <AddressDetails Accuracy="5" xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"><Country><CountryNameCode>LT</CountryNameCode><CountryName>Lithuania</CountryName><SubAdministrativeArea><SubAdministrativeAreaName>Vilnius Region</SubAdministrativeAreaName><Locality><LocalityName>Širvintos</LocalityName><PostalCode><PostalCodeNumber>19023</PostalCodeNumber></PostalCode></Locality></SubAdministrativeArea></Country></AddressDetails>
  710. // <ExtendedData>
  711. // <LatLonBox north="55.1109513" south="54.9867479" east="24.7563286" west="24.5854650" />
  712. // </ExtendedData>
  713. // <Point><coordinates>24.6778290,55.0561428,0</coordinates></Point>
  714. // </Placemark>
  715. // <Placemark id="p3">
  716. // <address>Širvintos, Lithuania</address>
  717. // <AddressDetails Accuracy="4" xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"><Country><CountryNameCode>LT</CountryNameCode><CountryName>Lithuania</CountryName><SubAdministrativeArea><SubAdministrativeAreaName>Vilnius Region</SubAdministrativeAreaName><Locality><LocalityName>Širvintos</LocalityName></Locality></SubAdministrativeArea></Country></AddressDetails>
  718. // <ExtendedData>
  719. // <LatLonBox north="55.1597127" south="54.8595715" east="25.2358124" west="24.5536348" />
  720. // </ExtendedData>
  721. // <Point><coordinates>24.9447696,55.0482439,0</coordinates></Point>
  722. // </Placemark>
  723. // <Placemark id="p4">
  724. // <address>Vilnius Region, Lithuania</address>
  725. // <AddressDetails Accuracy="3" xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"><Country><CountryNameCode>LT</CountryNameCode><CountryName>Lithuania</CountryName><SubAdministrativeArea><SubAdministrativeAreaName>Vilnius Region</SubAdministrativeAreaName></SubAdministrativeArea></Country></AddressDetails>
  726. // <ExtendedData>
  727. // <LatLonBox north="55.5177330" south="54.1276791" east="26.7590747" west="24.3866334" />
  728. // </ExtendedData>
  729. // <Point><coordinates>25.2182138,54.8086502,0</coordinates></Point>
  730. // </Placemark>
  731. // <Placemark id="p5">
  732. // <address>Lithuania</address>
  733. // <AddressDetails Accuracy="1" xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"><Country><CountryNameCode>LT</CountryNameCode><CountryName>Lithuania</CountryName></Country></AddressDetails>
  734. // <ExtendedData>
  735. // <LatLonBox north="56.4503174" south="53.8986720" east="26.8356500" west="20.9310000" />
  736. // </ExtendedData>
  737. // <Point><coordinates>23.8812750,55.1694380,0</coordinates></Point>
  738. // </Placemark>
  739. //</Response>
  740. //</kml>
  741. #endregion
  742. XmlDocument doc = new XmlDocument();
  743. doc.LoadXml(reverse);
  744. XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable);
  745. nsMgr.AddNamespace("sm", string.Format("http://earth.{0}/kml/2.0", Server));
  746. nsMgr.AddNamespace("sn", "urn:oasis:names:tc:ciq:xsdschema:xAL:2.0");
  747. var codeNode = doc.SelectSingleNode("//sm:Status/sm:code", nsMgr);
  748. if(codeNode != null)
  749. {
  750. status = (GeoCoderStatusCode)int.Parse(codeNode.InnerText);
  751. if(status == GeoCoderStatusCode.G_GEO_SUCCESS)
  752. {
  753. if(cache && GMaps.Instance.UsePlacemarkCache)
  754. {
  755. Cache.Instance.SaveContent(urlEnd, CacheType.PlacemarkCache, reverse);
  756. }
  757. placemarkList = new List<Placemark>();
  758. #region -- placemarks --
  759. XmlNodeList l = doc.SelectNodes("/sm:kml/sm:Response/sm:Placemark", nsMgr);
  760. if(l != null)
  761. {
  762. foreach(XmlNode n in l)
  763. {
  764. XmlNode nnd, nnl, nn;
  765. {
  766. nn = n.SelectSingleNode("sm:address", nsMgr);
  767. if(nn != null)
  768. {
  769. var ret = new Placemark(nn.InnerText);
  770. nnd = n.SelectSingleNode("sn:AddressDetails", nsMgr);
  771. if(nnd != null)
  772. {
  773. nn = nnd.SelectSingleNode("@Accuracy", nsMgr);
  774. if(nn != null)
  775. {
  776. ret.Accuracy = int.Parse(nn.InnerText);
  777. }
  778. nn = nnd.SelectSingleNode("sn:Country/sn:CountryNameCode", nsMgr);
  779. if(nn != null)
  780. {
  781. ret.CountryNameCode = nn.InnerText;
  782. }
  783. nn = nnd.SelectSingleNode("sn:Country/sn:CountryName", nsMgr);
  784. if(nn != null)
  785. {
  786. ret.CountryName = nn.InnerText;
  787. }
  788. nn = nnd.SelectSingleNode("descendant::sn:AdministrativeArea/sn:AdministrativeAreaName", nsMgr);
  789. if(nn != null)
  790. {
  791. ret.AdministrativeAreaName = nn.InnerText;
  792. }
  793. nn = nnd.SelectSingleNode("descendant::sn:SubAdministrativeArea/sn:SubAdministrativeAreaName", nsMgr);
  794. if(nn != null)
  795. {
  796. ret.SubAdministrativeAreaName = nn.InnerText;
  797. }
  798. // Locality or DependentLocality tag ?
  799. nnl = nnd.SelectSingleNode("descendant::sn:Locality", nsMgr) ?? nnd.SelectSingleNode("descendant::sn:DependentLocality", nsMgr);
  800. if(nnl != null)
  801. {
  802. nn = nnl.SelectSingleNode(string.Format("sn:{0}Name", nnl.Name), nsMgr);
  803. if(nn != null)
  804. {
  805. ret.LocalityName = nn.InnerText;
  806. }
  807. nn = nnl.SelectSingleNode("sn:Thoroughfare/sn:ThoroughfareName", nsMgr);
  808. if(nn != null)
  809. {
  810. ret.ThoroughfareName = nn.InnerText;
  811. }
  812. nn = nnl.SelectSingleNode("sn:PostalCode/sn:PostalCodeNumber", nsMgr);
  813. if(nn != null)
  814. {
  815. ret.PostalCodeNumber = nn.InnerText;
  816. }
  817. }
  818. }
  819. placemarkList.Add(ret);
  820. }
  821. }
  822. }
  823. }
  824. #endregion
  825. }
  826. }
  827. #endregion
  828. }
  829. }
  830. }
  831. catch(Exception ex)
  832. {
  833. status = GeoCoderStatusCode.ExceptionInCode;
  834. placemarkList = null;
  835. Debug.WriteLine("GetPlacemarkReverseGeocoderUrl: " + ex.ToString());
  836. }
  837. return status;
  838. }
  839. static readonly string ReverseGeocoderUrlFormat = "http://maps.{4}/maps/geo?hl={0}&ll={1},{2}&output=xml&key={3}";
  840. static readonly string GeocoderUrlFormat = "http://maps.{3}/maps/geo?q={0}&hl={1}&output=kml&key={2}";
  841. #endregion
  842. #endregion
  843. #region DirectionsProvider Members
  844. public DirectionsStatusCode GetDirections(out GDirections direction, PointLatLng start, PointLatLng end, bool avoidHighways, bool avoidTolls, bool walkingMode, bool sensor, bool metric)
  845. {
  846. return GetDirectionsUrl(MakeDirectionsUrl(start, end, LanguageStr, avoidHighways, avoidTolls, walkingMode, sensor, metric), out direction);
  847. }
  848. public DirectionsStatusCode GetDirections(out GDirections direction, string start, string end, bool avoidHighways, bool avoidTolls, bool walkingMode, bool sensor, bool metric)
  849. {
  850. return GetDirectionsUrl(MakeDirectionsUrl(start, end, LanguageStr, avoidHighways, avoidTolls, walkingMode, sensor, metric), out direction);
  851. }
  852. /// <summary>
  853. /// NotImplemented
  854. /// </summary>
  855. /// <param name="status"></param>
  856. /// <param name="start"></param>
  857. /// <param name="end"></param>
  858. /// <param name="avoidHighways"></param>
  859. /// <param name="avoidTolls"></param>
  860. /// <param name="walkingMode"></param>
  861. /// <param name="sensor"></param>
  862. /// <param name="metric"></param>
  863. /// <returns></returns>
  864. public IEnumerable<GDirections> GetDirections(out DirectionsStatusCode status, string start, string end, bool avoidHighways, bool avoidTolls, bool walkingMode, bool sensor, bool metric)
  865. {
  866. // TODO: add alternative directions
  867. throw new NotImplementedException();
  868. }
  869. /// <summary>
  870. /// NotImplemented
  871. /// </summary>
  872. /// <param name="status"></param>
  873. /// <param name="start"></param>
  874. /// <param name="end"></param>
  875. /// <param name="avoidHighways"></param>
  876. /// <param name="avoidTolls"></param>
  877. /// <param name="walkingMode"></param>
  878. /// <param name="sensor"></param>
  879. /// <param name="metric"></param>
  880. /// <returns></returns>
  881. public IEnumerable<GDirections> GetDirections(out DirectionsStatusCode status, PointLatLng start, PointLatLng end, bool avoidHighways, bool avoidTolls, bool walkingMode, bool sensor, bool metric)
  882. {
  883. // TODO: add alternative directions
  884. throw new NotImplementedException();
  885. }
  886. #region -- internals --
  887. // The Google Directions API: http://code.google.com/apis/maps/documentation/directions/
  888. string MakeDirectionsUrl(PointLatLng start, PointLatLng end, string language, bool avoidHighways, bool avoidTolls, bool walkingMode, bool sensor, bool metric)
  889. {
  890. string av = (avoidHighways ? "&avoid=highways" : string.Empty) + (avoidTolls ? "&avoid=tolls" : string.Empty); // 6
  891. string mt = "&units=" + (metric ? "metric" : "imperial"); // 7
  892. string wk = "&mode=" + (walkingMode ? "walking" : "driving"); // 8
  893. return string.Format(CultureInfo.InvariantCulture, DirectionUrlFormatPoint, start.Lat, start.Lng, end.Lat, end.Lng, sensor.ToString().ToLower(), language, av, mt, wk);
  894. }
  895. string MakeDirectionsUrl(string start, string end, string language, bool avoidHighways, bool walkingMode, bool avoidTolls, bool sensor, bool metric)
  896. {
  897. string av = (avoidHighways ? "&avoid=highways" : string.Empty) + (avoidTolls ? "&avoid=tolls" : string.Empty); // 4
  898. string mt = "&units=" + (metric ? "metric" : "imperial"); // 5
  899. string wk = "&mode=" + (walkingMode ? "walking" : "driving"); // 6
  900. return string.Format(DirectionUrlFormatStr, start.Replace(' ', '+'), end.Replace(' ', '+'), sensor.ToString().ToLower(), language, av, mt, wk);
  901. }
  902. DirectionsStatusCode GetDirectionsUrl(string url, out GDirections direction)
  903. {
  904. DirectionsStatusCode ret = DirectionsStatusCode.UNKNOWN_ERROR;
  905. direction = null;
  906. try
  907. {
  908. string urlEnd = url.Substring(url.IndexOf("xml?"));
  909. string kml = GMaps.Instance.UseDirectionsCache ? Cache.Instance.GetContent(urlEnd, CacheType.DirectionsCache) : string.Empty;
  910. bool cache = false;
  911. if(string.IsNullOrEmpty(kml))
  912. {
  913. kml = GetContentUsingHttp(url);
  914. if(!string.IsNullOrEmpty(kml))
  915. {
  916. cache = true;
  917. }
  918. }
  919. if(!string.IsNullOrEmpty(kml))
  920. {
  921. #region -- kml response --
  922. //<?xml version="1.0" encoding="UTF-8"?>
  923. //<DirectionsResponse>
  924. // <status>OK</status>
  925. // <route>
  926. // <summary>A1/E85</summary>
  927. // <leg>
  928. // <step>
  929. // <travel_mode>DRIVING</travel_mode>
  930. // <start_location>
  931. // <lat>54.6893800</lat>
  932. // <lng>25.2800500</lng>
  933. // </start_location>
  934. // <end_location>
  935. // <lat>54.6907800</lat>
  936. // <lng>25.2798000</lng>
  937. // </end_location>
  938. // <polyline>
  939. // <points>soxlIiohyCYLkCJ}@Vs@?</points>
  940. // </polyline>
  941. // <duration>…