Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2498 | - | 1 | |
2 | namespace GMap.NET.MapProviders |
||
3 | { |
||
4 | using System; |
||
5 | using System.Collections.Generic; |
||
6 | using System.Diagnostics; |
||
7 | using System.Globalization; |
||
8 | using System.Net; |
||
9 | using System.Text; |
||
10 | using System.Text.RegularExpressions; |
||
11 | using System.Threading; |
||
12 | using System.Xml; |
||
13 | using GMap.NET.Internals; |
||
14 | using GMap.NET.Projections; |
||
15 | |||
16 | public abstract class BingMapProviderBase : GMapProvider, RoutingProvider, GeocodingProvider |
||
17 | { |
||
18 | public BingMapProviderBase() |
||
19 | { |
||
20 | MaxZoom = null; |
||
21 | RefererUrl = "http://www.bing.com/maps/"; |
||
22 | Copyright = string.Format("©{0} Microsoft Corporation, ©{0} NAVTEQ, ©{0} Image courtesy of NASA", DateTime.Today.Year); |
||
23 | } |
||
24 | |||
25 | public string Version = "4810"; |
||
26 | |||
27 | /// <summary> |
||
28 | /// Bing Maps Customer Identification. |
||
29 | /// | |
||
30 | /// FOR LEGAL AND COMMERCIAL USAGE SET YOUR OWN REGISTERED KEY |
||
31 | /// | |
||
32 | /// http://msdn.microsoft.com/en-us/library/ff428642.aspx |
||
33 | /// </summary> |
||
34 | public string ClientKey = string.Empty; |
||
35 | |||
36 | internal string SessionId = string.Empty; |
||
37 | |||
38 | /// <summary> |
||
39 | /// set true to append SessionId on requesting tiles |
||
40 | /// </summary> |
||
41 | public bool ForceSessionIdOnTileAccess = false; |
||
42 | |||
43 | /// <summary> |
||
44 | /// set true to avoid using dynamic tile url format |
||
45 | /// </summary> |
||
46 | public bool DisableDynamicTileUrlFormat = false; |
||
47 | |||
48 | /// <summary> |
||
49 | /// Converts tile XY coordinates into a QuadKey at a specified level of detail. |
||
50 | /// </summary> |
||
51 | /// <param name="tileX">Tile X coordinate.</param> |
||
52 | /// <param name="tileY">Tile Y coordinate.</param> |
||
53 | /// <param name="levelOfDetail">Level of detail, from 1 (lowest detail) |
||
54 | /// to 23 (highest detail).</param> |
||
55 | /// <returns>A string containing the QuadKey.</returns> |
||
56 | internal string TileXYToQuadKey(long tileX, long tileY, int levelOfDetail) |
||
57 | { |
||
58 | StringBuilder quadKey = new StringBuilder(); |
||
59 | for(int i = levelOfDetail; i > 0; i--) |
||
60 | { |
||
61 | char digit = '0'; |
||
62 | int mask = 1 << (i - 1); |
||
63 | if((tileX & mask) != 0) |
||
64 | { |
||
65 | digit++; |
||
66 | } |
||
67 | if((tileY & mask) != 0) |
||
68 | { |
||
69 | digit++; |
||
70 | digit++; |
||
71 | } |
||
72 | quadKey.Append(digit); |
||
73 | } |
||
74 | return quadKey.ToString(); |
||
75 | } |
||
76 | |||
77 | /// <summary> |
||
78 | /// Converts a QuadKey into tile XY coordinates. |
||
79 | /// </summary> |
||
80 | /// <param name="quadKey">QuadKey of the tile.</param> |
||
81 | /// <param name="tileX">Output parameter receiving the tile X coordinate.</param> |
||
82 | /// <param name="tileY">Output parameter receiving the tile Y coordinate.</param> |
||
83 | /// <param name="levelOfDetail">Output parameter receiving the level of detail.</param> |
||
84 | internal void QuadKeyToTileXY(string quadKey, out int tileX, out int tileY, out int levelOfDetail) |
||
85 | { |
||
86 | tileX = tileY = 0; |
||
87 | levelOfDetail = quadKey.Length; |
||
88 | for(int i = levelOfDetail; i > 0; i--) |
||
89 | { |
||
90 | int mask = 1 << (i - 1); |
||
91 | switch(quadKey[levelOfDetail - i]) |
||
92 | { |
||
93 | case '0': |
||
94 | break; |
||
95 | |||
96 | case '1': |
||
97 | tileX |= mask; |
||
98 | break; |
||
99 | |||
100 | case '2': |
||
101 | tileY |= mask; |
||
102 | break; |
||
103 | |||
104 | case '3': |
||
105 | tileX |= mask; |
||
106 | tileY |= mask; |
||
107 | break; |
||
108 | |||
109 | default: |
||
110 | throw new ArgumentException("Invalid QuadKey digit sequence."); |
||
111 | } |
||
112 | } |
||
113 | } |
||
114 | |||
115 | #region GMapProvider Members |
||
116 | public override Guid Id |
||
117 | { |
||
118 | get |
||
119 | { |
||
120 | throw new NotImplementedException(); |
||
121 | } |
||
122 | } |
||
123 | |||
124 | public override string Name |
||
125 | { |
||
126 | get |
||
127 | { |
||
128 | throw new NotImplementedException(); |
||
129 | } |
||
130 | } |
||
131 | |||
132 | public override PureProjection Projection |
||
133 | { |
||
134 | get |
||
135 | { |
||
136 | return MercatorProjection.Instance; |
||
137 | } |
||
138 | } |
||
139 | |||
140 | GMapProvider[] overlays; |
||
141 | public override GMapProvider[] Overlays |
||
142 | { |
||
143 | get |
||
144 | { |
||
145 | if(overlays == null) |
||
146 | { |
||
147 | overlays = new GMapProvider[] { this }; |
||
148 | } |
||
149 | return overlays; |
||
150 | } |
||
151 | } |
||
152 | |||
153 | public override PureImage GetTileImage(GPoint pos, int zoom) |
||
154 | { |
||
155 | throw new NotImplementedException(); |
||
156 | } |
||
157 | #endregion |
||
158 | |||
159 | public bool TryCorrectVersion = true; |
||
160 | |||
161 | /// <summary> |
||
162 | /// set false to use your own key. |
||
163 | /// FOR LEGAL AND COMMERCIAL USAGE SET YOUR OWN REGISTERED KEY |
||
164 | /// http://msdn.microsoft.com/en-us/library/ff428642.aspx |
||
165 | /// </summary> |
||
166 | public bool TryGetDefaultKey = true; |
||
167 | static bool init = false; |
||
168 | |||
169 | public override void OnInitialized() |
||
170 | { |
||
171 | if(!init) |
||
172 | { |
||
173 | try |
||
174 | { |
||
175 | var key = ClientKey; |
||
176 | |||
177 | // to avoid registration stuff, default key |
||
178 | if(TryGetDefaultKey && string.IsNullOrEmpty(ClientKey)) |
||
179 | { |
||
180 | //old: Vx8dmDflxzT02jJUG8bEjMU07Xr9QWRpPTeRuAZTC1uZFQdDCvK/jUbHKdyHEWj4LvccTPoKofDHtzHsWu/0xuo5u2Y9rj88 |
||
181 | key = Stuff.GString("Jq7FrGTyaYqcrvv9ugBKv4OVSKnmzpigqZtdvtcDdgZexmOZ2RugOexFSmVzTAhOWiHrdhFoNCoySnNF3MyyIOo5u2Y9rj88"); |
||
182 | } |
||
183 | |||
184 | #region -- try get sesion key -- |
||
185 | if(!string.IsNullOrEmpty(key)) |
||
186 | { |
||
187 | string keyResponse = GMaps.Instance.UseUrlCache ? Cache.Instance.GetContent("BingLoggingServiceV1" + key, CacheType.UrlCache, TimeSpan.FromHours(8)) : string.Empty; |
||
188 | |||
189 | if(string.IsNullOrEmpty(keyResponse)) |
||
190 | { |
||
191 | // Bing Maps WPF Control |
||
192 | // http://dev.virtualearth.net/webservices/v1/LoggingService/LoggingService.svc/Log?entry=0&auth={0}&fmt=1&type=3&group=MapControl&name=WPF&version=1.0.0.0&session=00000000-0000-0000-0000-000000000000&mkt=en-US |
||
193 | |||
194 | keyResponse = GetContentUsingHttp(string.Format("http://dev.virtualearth.net/webservices/v1/LoggingService/LoggingService.svc/Log?entry=0&fmt=1&type=3&group=MapControl&name=AJAX&mkt=en-us&auth={0}&jsonp=microsoftMapsNetworkCallback", key)); |
||
195 | |||
196 | if(!string.IsNullOrEmpty(keyResponse) && keyResponse.Contains("ValidCredentials")) |
||
197 | { |
||
198 | if(GMaps.Instance.UseUrlCache) |
||
199 | { |
||
200 | Cache.Instance.SaveContent("BingLoggingServiceV1" + key, CacheType.UrlCache, keyResponse); |
||
201 | } |
||
202 | } |
||
203 | } |
||
204 | |||
205 | if(!string.IsNullOrEmpty(keyResponse) && keyResponse.Contains("sessionId") && keyResponse.Contains("ValidCredentials")) |
||
206 | { |
||
207 | // microsoftMapsNetworkCallback({"sessionId" : "xxx", "authenticationResultCode" : "ValidCredentials"}) |
||
208 | |||
209 | SessionId = keyResponse.Split(',')[0].Split(':')[1].Replace("\"", string.Empty).Replace(" ", string.Empty); |
||
210 | Debug.WriteLine("GMapProviders.BingMap.SessionId: " + SessionId); |
||
211 | } |
||
212 | else |
||
213 | { |
||
214 | Debug.WriteLine("BingLoggingServiceV1: " + keyResponse); |
||
215 | } |
||
216 | } |
||
217 | #endregion |
||
218 | |||
219 | // supporting old road |
||
220 | if(TryCorrectVersion && DisableDynamicTileUrlFormat) |
||
221 | { |
||
222 | #region -- get the version -- |
||
223 | string url = @"http://www.bing.com/maps"; |
||
224 | string html = GMaps.Instance.UseUrlCache ? Cache.Instance.GetContent(url, CacheType.UrlCache, TimeSpan.FromDays(7)) : string.Empty; |
||
225 | |||
226 | if(string.IsNullOrEmpty(html)) |
||
227 | { |
||
228 | html = GetContentUsingHttp(url); |
||
229 | if(!string.IsNullOrEmpty(html)) |
||
230 | { |
||
231 | if(GMaps.Instance.UseUrlCache) |
||
232 | { |
||
233 | Cache.Instance.SaveContent(url, CacheType.UrlCache, html); |
||
234 | } |
||
235 | } |
||
236 | } |
||
237 | |||
238 | if(!string.IsNullOrEmpty(html)) |
||
239 | { |
||
240 | #region -- match versions -- |
||
241 | |||
242 | Regex reg = new Regex("tilegeneration:(\\d*)", RegexOptions.IgnoreCase); |
||
243 | Match mat = reg.Match(html); |
||
244 | if(mat.Success) |
||
245 | { |
||
246 | GroupCollection gc = mat.Groups; |
||
247 | int count = gc.Count; |
||
248 | if(count == 2) |
||
249 | { |
||
250 | string ver = gc[1].Value; |
||
251 | string old = GMapProviders.BingMap.Version; |
||
252 | if(ver != old) |
||
253 | { |
||
254 | GMapProviders.BingMap.Version = ver; |
||
255 | GMapProviders.BingSatelliteMap.Version = ver; |
||
256 | GMapProviders.BingHybridMap.Version = ver; |
||
257 | #if DEBUG |
||
258 | Debug.WriteLine("GMapProviders.BingMap.Version: " + ver + ", old: " + old + ", consider updating source"); |
||
259 | if(Debugger.IsAttached) |
||
260 | { |
||
261 | Thread.Sleep(5555); |
||
262 | } |
||
263 | #endif |
||
264 | } |
||
265 | else |
||
266 | { |
||
267 | Debug.WriteLine("GMapProviders.BingMap.Version: " + ver + ", OK"); |
||
268 | } |
||
269 | } |
||
270 | } |
||
271 | #endregion |
||
272 | } |
||
273 | #endregion |
||
274 | } |
||
275 | |||
276 | init = true; // try it only once |
||
277 | } |
||
278 | catch(Exception ex) |
||
279 | { |
||
280 | Debug.WriteLine("TryCorrectBingVersions failed: " + ex); |
||
281 | } |
||
282 | } |
||
283 | } |
||
284 | |||
285 | protected override bool CheckTileImageHttpResponse(WebResponse response) |
||
286 | { |
||
287 | var pass = base.CheckTileImageHttpResponse(response); |
||
288 | if(pass) |
||
289 | { |
||
290 | var tileInfo = response.Headers.Get("X-VE-Tile-Info"); |
||
291 | if(tileInfo != null) |
||
292 | { |
||
293 | return !tileInfo.Equals("no-tile"); |
||
294 | } |
||
295 | } |
||
296 | return pass; |
||
297 | } |
||
298 | |||
299 | internal string GetTileUrl(string imageryType) |
||
300 | { |
||
301 | //Retrieve map tile URL from the Imagery Metadata service: http://msdn.microsoft.com/en-us/library/ff701716.aspx |
||
302 | //This ensures that the current tile URL is always used. |
||
303 | //This will prevent the app from breaking when the map tiles change. |
||
304 | |||
305 | string ret = string.Empty; |
||
306 | if(!string.IsNullOrEmpty(SessionId)) |
||
307 | { |
||
308 | try |
||
309 | { |
||
310 | string url = "http://dev.virtualearth.net/REST/V1/Imagery/Metadata/" + imageryType + "?output=xml&key=" + SessionId; |
||
311 | |||
312 | string r = GMaps.Instance.UseUrlCache ? Cache.Instance.GetContent("GetTileUrl" + imageryType, CacheType.UrlCache, TimeSpan.FromDays(7)) : string.Empty; |
||
313 | bool cache = false; |
||
314 | |||
315 | if(string.IsNullOrEmpty(r)) |
||
316 | { |
||
317 | r = GetContentUsingHttp(url); |
||
318 | cache = true; |
||
319 | } |
||
320 | |||
321 | if(!string.IsNullOrEmpty(r)) |
||
322 | { |
||
323 | XmlDocument doc = new XmlDocument(); |
||
324 | doc.LoadXml(r); |
||
325 | |||
326 | XmlNode xn = doc["Response"]; |
||
327 | string statuscode = xn["StatusCode"].InnerText; |
||
328 | if(string.Compare(statuscode, "200", true) == 0) |
||
329 | { |
||
330 | xn = xn["ResourceSets"]["ResourceSet"]["Resources"]; |
||
331 | XmlNodeList xnl = xn.ChildNodes; |
||
332 | foreach(XmlNode xno in xnl) |
||
333 | { |
||
334 | XmlNode imageUrl = xno["ImageUrl"]; |
||
335 | |||
336 | if(imageUrl != null && !string.IsNullOrEmpty(imageUrl.InnerText)) |
||
337 | { |
||
338 | if(cache && GMaps.Instance.UseUrlCache) |
||
339 | { |
||
340 | Cache.Instance.SaveContent("GetTileUrl" + imageryType, CacheType.UrlCache, r); |
||
341 | } |
||
342 | |||
343 | var baseTileUrl = imageUrl.InnerText; |
||
344 | |||
345 | if(baseTileUrl.Contains("{key}") || baseTileUrl.Contains("{token}")) |
||
346 | { |
||
347 | baseTileUrl.Replace("{key}", SessionId).Replace("{token}", SessionId); |
||
348 | } |
||
349 | else if(ForceSessionIdOnTileAccess) |
||
350 | { |
||
351 | // haven't seen anyone doing that, yet? ;/ |
||
352 | baseTileUrl += "&key=" + SessionId; |
||
353 | } |
||
354 | |||
355 | Debug.WriteLine("GetTileUrl, UrlFormat[" + imageryType + "]: " + baseTileUrl); |
||
356 | |||
357 | ret = baseTileUrl; |
||
358 | break; |
||
359 | } |
||
360 | } |
||
361 | } |
||
362 | } |
||
363 | } |
||
364 | catch(Exception ex) |
||
365 | { |
||
366 | Debug.WriteLine("GetTileUrl: Error getting Bing Maps tile URL - " + ex); |
||
367 | } |
||
368 | } |
||
369 | return ret; |
||
370 | } |
||
371 | |||
372 | #region RoutingProvider |
||
373 | |||
374 | public MapRoute GetRoute(PointLatLng start, PointLatLng end, bool avoidHighways, bool walkingMode, int Zoom) |
||
375 | { |
||
376 | string tooltip; |
||
377 | int numLevels; |
||
378 | int zoomFactor; |
||
379 | MapRoute ret = null; |
||
380 | List<PointLatLng> points = GetRoutePoints(MakeRouteUrl(start, end, LanguageStr, avoidHighways, walkingMode), Zoom, out tooltip, out numLevels, out zoomFactor); |
||
381 | if(points != null) |
||
382 | { |
||
383 | ret = new MapRoute(points, tooltip); |
||
384 | } |
||
385 | return ret; |
||
386 | } |
||
387 | |||
388 | public MapRoute GetRoute(string start, string end, bool avoidHighways, bool walkingMode, int Zoom) |
||
389 | { |
||
390 | string tooltip; |
||
391 | int numLevels; |
||
392 | int zoomFactor; |
||
393 | MapRoute ret = null; |
||
394 | List<PointLatLng> points = GetRoutePoints(MakeRouteUrl(start, end, LanguageStr, avoidHighways, walkingMode), Zoom, out tooltip, out numLevels, out zoomFactor); |
||
395 | if(points != null) |
||
396 | { |
||
397 | ret = new MapRoute(points, tooltip); |
||
398 | } |
||
399 | return ret; |
||
400 | } |
||
401 | |||
402 | string MakeRouteUrl(string start, string end, string language, bool avoidHighways, bool walkingMode) |
||
403 | { |
||
404 | string addition = avoidHighways ? "&avoid=highways" : string.Empty; |
||
405 | string mode = walkingMode ? "Walking" : "Driving"; |
||
406 | |||
407 | return string.Format(CultureInfo.InvariantCulture, RouteUrlFormatPointQueries, mode, start, end, addition, SessionId); |
||
408 | } |
||
409 | |||
410 | string MakeRouteUrl(PointLatLng start, PointLatLng end, string language, bool avoidHighways, bool walkingMode) |
||
411 | { |
||
412 | string addition = avoidHighways ? "&avoid=highways" : string.Empty; |
||
413 | string mode = walkingMode ? "Walking" : "Driving"; |
||
414 | |||
415 | return string.Format(CultureInfo.InvariantCulture, RouteUrlFormatPointLatLng, mode, start.Lat, start.Lng, end.Lat, end.Lng, addition, SessionId); |
||
416 | } |
||
417 | |||
418 | List<PointLatLng> GetRoutePoints(string url, int zoom, out string tooltipHtml, out int numLevel, out int zoomFactor) |
||
419 | { |
||
420 | List<PointLatLng> points = null; |
||
421 | tooltipHtml = string.Empty; |
||
422 | numLevel = -1; |
||
423 | zoomFactor = -1; |
||
424 | try |
||
425 | { |
||
426 | string route = GMaps.Instance.UseRouteCache ? Cache.Instance.GetContent(url, CacheType.RouteCache) : string.Empty; |
||
427 | |||
428 | if(string.IsNullOrEmpty(route)) |
||
429 | { |
||
430 | route = GetContentUsingHttp(url); |
||
431 | |||
432 | if(!string.IsNullOrEmpty(route)) |
||
433 | { |
||
434 | if(GMaps.Instance.UseRouteCache) |
||
435 | { |
||
436 | Cache.Instance.SaveContent(url, CacheType.RouteCache, route); |
||
437 | } |
||
438 | } |
||
439 | } |
||
440 | |||
441 | // parse values |
||
442 | if(!string.IsNullOrEmpty(route)) |
||
443 | { |
||
444 | #region -- title -- |
||
445 | int tooltipEnd = 0; |
||
446 | { |
||
447 | int x = route.IndexOf("<RoutePath><Line>") + 17; |
||
448 | if(x >= 17) |
||
449 | { |
||
450 | tooltipEnd = route.IndexOf("</Line></RoutePath>", x + 1); |
||
451 | if(tooltipEnd > 0) |
||
452 | { |
||
453 | int l = tooltipEnd - x; |
||
454 | if(l > 0) |
||
455 | { |
||
456 | //tooltipHtml = route.Substring(x, l).Replace(@"\x26#160;", " "); |
||
457 | tooltipHtml = route.Substring(x, l); |
||
458 | } |
||
459 | } |
||
460 | } |
||
461 | } |
||
462 | #endregion |
||
463 | |||
464 | #region -- points -- |
||
465 | XmlDocument doc = new XmlDocument(); |
||
466 | doc.LoadXml(route); |
||
467 | XmlNode xn = doc["Response"]; |
||
468 | string statuscode = xn["StatusCode"].InnerText; |
||
469 | switch(statuscode) |
||
470 | { |
||
471 | case "200": |
||
472 | { |
||
473 | xn = xn["ResourceSets"]["ResourceSet"]["Resources"]["Route"]["RoutePath"]["Line"]; |
||
474 | XmlNodeList xnl = xn.ChildNodes; |
||
475 | if(xnl.Count > 0) |
||
476 | { |
||
477 | points = new List<PointLatLng>(); |
||
478 | foreach(XmlNode xno in xnl) |
||
479 | { |
||
480 | XmlNode latitude = xno["Latitude"]; |
||
481 | XmlNode longitude = xno["Longitude"]; |
||
482 | points.Add(new PointLatLng(double.Parse(latitude.InnerText, CultureInfo.InvariantCulture), |
||
483 | double.Parse(longitude.InnerText, CultureInfo.InvariantCulture))); |
||
484 | } |
||
485 | } |
||
486 | break; |
||
487 | } |
||
488 | // no status implementation on routes yet although when introduced these are the codes. Exception will be catched. |
||
489 | case "400": |
||
490 | throw new Exception("Bad Request, The request contained an error."); |
||
491 | case "401": |
||
492 | throw new Exception("Unauthorized, Access was denied. You may have entered your credentials incorrectly, or you might not have access to the requested resource or operation."); |
||
493 | case "403": |
||
494 | throw new Exception("Forbidden, The request is for something forbidden. Authorization will not help."); |
||
495 | case "404": |
||
496 | throw new Exception("Not Found, The requested resource was not found."); |
||
497 | case "500": |
||
498 | throw new Exception("Internal Server Error, Your request could not be completed because there was a problem with the service."); |
||
499 | case "501": |
||
500 | throw new Exception("Service Unavailable, There's a problem with the service right now. Please try again later."); |
||
501 | default: |
||
502 | points = null; |
||
503 | break; // unknown, for possible future error codes |
||
504 | } |
||
505 | #endregion |
||
506 | } |
||
507 | } |
||
508 | catch(Exception ex) |
||
509 | { |
||
510 | points = null; |
||
511 | Debug.WriteLine("GetRoutePoints: " + ex); |
||
512 | } |
||
513 | return points; |
||
514 | } |
||
515 | |||
516 | // example : http://dev.virtualearth.net/REST/V1/Routes/Driving?o=xml&wp.0=44.979035,-93.26493&wp.1=44.943828508257866,-93.09332862496376&optmz=distance&rpo=Points&key=[PROVIDEYOUROWNKEY!!] |
||
517 | static readonly string RouteUrlFormatPointLatLng = "http://dev.virtualearth.net/REST/V1/Routes/{0}?o=xml&wp.0={1},{2}&wp.1={3},{4}{5}&optmz=distance&rpo=Points&key={6}"; |
||
518 | static readonly string RouteUrlFormatPointQueries = "http://dev.virtualearth.net/REST/V1/Routes/{0}?o=xml&wp.0={1}&wp.1={2}{3}&optmz=distance&rpo=Points&key={4}"; |
||
519 | |||
520 | #endregion RoutingProvider |
||
521 | |||
522 | #region GeocodingProvider |
||
523 | |||
524 | public GeoCoderStatusCode GetPoints(string keywords, out List<PointLatLng> pointList) |
||
525 | { |
||
526 | //Escape keywords to better handle special characters. |
||
527 | return GetLatLngFromGeocoderUrl(MakeGeocoderUrl("q=" + Uri.EscapeDataString(keywords)), out pointList); |
||
528 | } |
||
529 | |||
530 | public PointLatLng? GetPoint(string keywords, out GeoCoderStatusCode status) |
||
531 | { |
||
532 | List<PointLatLng> pointList; |
||
533 | status = GetPoints(keywords, out pointList); |
||
534 | return pointList != null && pointList.Count > 0 ? pointList[0] : (PointLatLng?)null; |
||
535 | } |
||
536 | |||
537 | public GeoCoderStatusCode GetPoints(Placemark placemark, out List<PointLatLng> pointList) |
||
538 | { |
||
539 | return GetLatLngFromGeocoderUrl(MakeGeocoderDetailedUrl(placemark), out pointList); |
||
540 | } |
||
541 | |||
542 | public PointLatLng? GetPoint(Placemark placemark, out GeoCoderStatusCode status) |
||
543 | { |
||
544 | List<PointLatLng> pointList; |
||
545 | status = GetLatLngFromGeocoderUrl(MakeGeocoderDetailedUrl(placemark), out pointList); |
||
546 | return pointList != null && pointList.Count > 0 ? pointList[0] : (PointLatLng?)null; |
||
547 | } |
||
548 | |||
549 | string MakeGeocoderDetailedUrl(Placemark placemark) |
||
550 | { |
||
551 | string parameters = string.Empty; |
||
552 | |||
553 | if(!AddFieldIfNotEmpty(ref parameters, "countryRegion", placemark.CountryNameCode)) |
||
554 | AddFieldIfNotEmpty(ref parameters, "countryRegion", placemark.CountryName); |
||
555 | |||
556 | AddFieldIfNotEmpty(ref parameters, "adminDistrict", placemark.DistrictName); |
||
557 | AddFieldIfNotEmpty(ref parameters, "locality", placemark.LocalityName); |
||
558 | AddFieldIfNotEmpty(ref parameters, "postalCode", placemark.PostalCodeNumber); |
||
559 | |||
560 | if(!string.IsNullOrEmpty(placemark.HouseNo)) |
||
561 | AddFieldIfNotEmpty(ref parameters, "addressLine", placemark.ThoroughfareName + " " + placemark.HouseNo); |
||
562 | else |
||
563 | AddFieldIfNotEmpty(ref parameters, "addressLine", placemark.ThoroughfareName); |
||
564 | |||
565 | return MakeGeocoderUrl(parameters); |
||
566 | } |
||
567 | |||
568 | bool AddFieldIfNotEmpty(ref string Input, string FieldName, string Value) |
||
569 | { |
||
570 | if(!string.IsNullOrEmpty(Value)) |
||
571 | { |
||
572 | if(string.IsNullOrEmpty(Input)) |
||
573 | Input = string.Empty; |
||
574 | else |
||
575 | Input = Input + "&"; |
||
576 | |||
577 | Input = Input + FieldName + "=" + Value; |
||
578 | |||
579 | return true; |
||
580 | } |
||
581 | return false; |
||
582 | } |
||
583 | |||
584 | public GeoCoderStatusCode GetPlacemarks(PointLatLng location, out List<Placemark> placemarkList) |
||
585 | { |
||
586 | // http://msdn.microsoft.com/en-us/library/ff701713.aspx |
||
587 | throw new NotImplementedException(); |
||
588 | } |
||
589 | |||
590 | public Placemark? GetPlacemark(PointLatLng location, out GeoCoderStatusCode status) |
||
591 | { |
||
592 | // http://msdn.microsoft.com/en-us/library/ff701713.aspx |
||
593 | throw new NotImplementedException(); |
||
594 | } |
||
595 | |||
596 | string MakeGeocoderUrl(string keywords) |
||
597 | { |
||
598 | return string.Format(CultureInfo.InvariantCulture, GeocoderUrlFormat, keywords, SessionId); |
||
599 | } |
||
600 | |||
601 | GeoCoderStatusCode GetLatLngFromGeocoderUrl(string url, out List<PointLatLng> pointList) |
||
602 | { |
||
603 | var status = GeoCoderStatusCode.Unknow; |
||
604 | pointList = null; |
||
605 | |||
606 | try |
||
607 | { |
||
608 | string geo = GMaps.Instance.UseGeocoderCache ? Cache.Instance.GetContent(url, CacheType.GeocoderCache) : string.Empty; |
||
609 | |||
610 | bool cache = false; |
||
611 | |||
612 | if(string.IsNullOrEmpty(geo)) |
||
613 | { |
||
614 | geo = GetContentUsingHttp(url); |
||
615 | |||
616 | if(!string.IsNullOrEmpty(geo)) |
||
617 | { |
||
618 | cache = true; |
||
619 | } |
||
620 | } |
||
621 | |||
622 | status = GeoCoderStatusCode.Unknow; |
||
623 | if(!string.IsNullOrEmpty(geo)) |
||
624 | { |
||
625 | if(geo.StartsWith("<?xml") && geo.Contains("<Response")) |
||
626 | { |
||
627 | XmlDocument doc = new XmlDocument(); |
||
628 | doc.LoadXml(geo); |
||
629 | XmlNode xn = doc["Response"]; |
||
630 | string statuscode = xn["StatusCode"].InnerText; |
||
631 | switch(statuscode) |
||
632 | { |
||
633 | case "200": |
||
634 | { |
||
635 | pointList = new List<PointLatLng>(); |
||
636 | xn = xn["ResourceSets"]["ResourceSet"]["Resources"]; |
||
637 | XmlNodeList xnl = xn.ChildNodes; |
||
638 | foreach(XmlNode xno in xnl) |
||
639 | { |
||
640 | XmlNode latitude = xno["Point"]["Latitude"]; |
||
641 | XmlNode longitude = xno["Point"]["Longitude"]; |
||
642 | pointList.Add(new PointLatLng(Double.Parse(latitude.InnerText, CultureInfo.InvariantCulture), |
||
643 | Double.Parse(longitude.InnerText, CultureInfo.InvariantCulture))); |
||
644 | } |
||
645 | |||
646 | if(pointList.Count > 0) |
||
647 | { |
||
648 | status = GeoCoderStatusCode.G_GEO_SUCCESS; |
||
649 | if(cache && GMaps.Instance.UseGeocoderCache) |
||
650 | { |
||
651 | Cache.Instance.SaveContent(url, CacheType.GeocoderCache, geo); |
||
652 | } |
||
653 | break; |
||
654 | } |
||
655 | |||
656 | status = GeoCoderStatusCode.G_GEO_UNKNOWN_ADDRESS; |
||
657 | break; |
||
658 | } |
||
659 | |||
660 | case "400": |
||
661 | status = GeoCoderStatusCode.G_GEO_BAD_REQUEST; |
||
662 | break; // bad request, The request contained an error. |
||
663 | case "401": |
||
664 | status = GeoCoderStatusCode.G_GEO_BAD_KEY; |
||
665 | break; // Unauthorized, Access was denied. You may have entered your credentials incorrectly, or you might not have access to the requested resource or operation. |
||
666 | case "403": |
||
667 | status = GeoCoderStatusCode.G_GEO_BAD_REQUEST; |
||
668 | break; // Forbidden, The request is for something forbidden. Authorization will not help. |
||
669 | case "404": |
||
670 | status = GeoCoderStatusCode.G_GEO_UNKNOWN_ADDRESS; |
||
671 | break; // Not Found, The requested resource was not found. |
||
672 | case "500": |
||
673 | status = GeoCoderStatusCode.G_GEO_SERVER_ERROR; |
||
674 | break; // Internal Server Error, Your request could not be completed because there was a problem with the service. |
||
675 | case "501": |
||
676 | status = GeoCoderStatusCode.Unknow; |
||
677 | break; // Service Unavailable, There's a problem with the service right now. Please try again later. |
||
678 | default: |
||
679 | status = GeoCoderStatusCode.Unknow; |
||
680 | break; // unknown, for possible future error codes |
||
681 | } |
||
682 | } |
||
683 | } |
||
684 | } |
||
685 | catch(Exception ex) |
||
686 | { |
||
687 | status = GeoCoderStatusCode.ExceptionInCode; |
||
688 | Debug.WriteLine("GetLatLngFromGeocoderUrl: " + ex); |
||
689 | } |
||
690 | |||
691 | return status; |
||
692 | } |
||
693 | |||
694 | // http://dev.virtualearth.net/REST/v1/Locations/1%20Microsoft%20Way%20Redmond%20WA%2098052?o=xml&key=BingMapsKey |
||
695 | static readonly string GeocoderUrlFormat = "http://dev.virtualearth.net/REST/v1/Locations?{0}&o=xml&key={1}"; |
||
696 | |||
697 | #endregion GeocodingProvider |
||
698 | } |
||
699 | |||
700 | /// <summary> |
||
701 | /// BingMapProvider provider |
||
702 | /// </summary> |
||
703 | public class BingMapProvider : BingMapProviderBase |
||
704 | { |
||
705 | public static readonly BingMapProvider Instance; |
||
706 | |||
707 | BingMapProvider() |
||
708 | { |
||
709 | } |
||
710 | |||
711 | static BingMapProvider() |
||
712 | { |
||
713 | Instance = new BingMapProvider(); |
||
714 | } |
||
715 | |||
716 | #region GMapProvider Members |
||
717 | |||
718 | readonly Guid id = new Guid("D0CEB371-F10A-4E12-A2C1-DF617D6674A8"); |
||
719 | public override Guid Id |
||
720 | { |
||
721 | get |
||
722 | { |
||
723 | return id; |
||
724 | } |
||
725 | } |
||
726 | |||
727 | readonly string name = "BingMap"; |
||
728 | public override string Name |
||
729 | { |
||
730 | get |
||
731 | { |
||
732 | return name; |
||
733 | } |
||
734 | } |
||
735 | |||
736 | public override PureImage GetTileImage(GPoint pos, int zoom) |
||
737 | { |
||
738 | string url = MakeTileImageUrl(pos, zoom, LanguageStr); |
||
739 | |||
740 | return GetTileImageUsingHttp(url); |
||
741 | } |
||
742 | |||
743 | public override void OnInitialized() |
||
744 | { |
||
745 | base.OnInitialized(); |
||
746 | |||
747 | if(!DisableDynamicTileUrlFormat) |
||
748 | { |
||
749 | //UrlFormat[Road]: http://ecn.{subdomain}.tiles.virtualearth.net/tiles/r{quadkey}.jpeg?g=3179&mkt={culture}&shading=hill |
||
750 | |||
751 | UrlDynamicFormat = GetTileUrl("Road"); |
||
752 | if(!string.IsNullOrEmpty(UrlDynamicFormat)) |
||
753 | { |
||
754 | UrlDynamicFormat = UrlDynamicFormat.Replace("{subdomain}", "t{0}").Replace("{quadkey}", "{1}").Replace("{culture}", "{2}"); |
||
755 | } |
||
756 | } |
||
757 | } |
||
758 | |||
759 | #endregion |
||
760 | |||
761 | string MakeTileImageUrl(GPoint pos, int zoom, string language) |
||
762 | { |
||
763 | string key = TileXYToQuadKey(pos.X, pos.Y, zoom); |
||
764 | |||
765 | if(!DisableDynamicTileUrlFormat && !string.IsNullOrEmpty(UrlDynamicFormat)) |
||
766 | { |
||
767 | return string.Format(UrlDynamicFormat, GetServerNum(pos, 4), key, language); |
||
768 | } |
||
769 | |||
770 | return string.Format(UrlFormat, GetServerNum(pos, 4), key, Version, language, ForceSessionIdOnTileAccess ? "&key=" + SessionId : string.Empty); |
||
771 | } |
||
772 | |||
773 | string UrlDynamicFormat = string.Empty; |
||
774 | |||
775 | // http://ecn.t0.tiles.virtualearth.net/tiles/r120030?g=875&mkt=en-us&lbl=l1&stl=h&shading=hill&n=z |
||
776 | |||
777 | static readonly string UrlFormat = "http://ecn.t{0}.tiles.virtualearth.net/tiles/r{1}?g={2}&mkt={3}&lbl=l1&stl=h&shading=hill&n=z{4}"; |
||
778 | } |
||
779 | } |