PageRenderTime 51ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/Silverlight/EsriSupport/EsriMilSymLayer.cs

#
C# | 300 lines | 155 code | 40 blank | 105 comment | 20 complexity | 8d88ac33cac8b6dadfd166e235605378 MD5 | raw file
  1. // --------------------------------------------------------------------------------------------------------------------
  2. // <copyright company="HillHouse" file="EsriMilSymLayer.cs">
  3. // Copyright © 2009-2011 HillHouse
  4. // </copyright>
  5. // <summary>
  6. // Methods supporting IMilSymLayer for use by the Esri maps.
  7. // </summary>
  8. // <license>
  9. // Licensed under the Ms-PL license.
  10. // </license>
  11. // <homepage>
  12. // http://milsym.codeplex.com
  13. // </homepage>
  14. // --------------------------------------------------------------------------------------------------------------------
  15. namespace MilSym.EsriSupport
  16. {
  17. using System;
  18. using System.Linq;
  19. using System.Windows;
  20. using ESRI.ArcGIS.Client;
  21. using ESRI.ArcGIS.Client.Bing;
  22. using ESRI.ArcGIS.Client.Geometry;
  23. using MilGraph;
  24. using MilGraph.Support;
  25. using MilSymbol;
  26. /// <summary>
  27. /// Methods supporting IMilSymLayer for use by the Esri maps.
  28. /// </summary>
  29. public class EsriMilSymLayer : ElementLayer, IMilSymLayer
  30. {
  31. /// <summary>
  32. /// The Esri element layer appears to require a rectangle to place an element.
  33. /// This parameter supports a very small rectangle.
  34. /// </summary>
  35. private const double LonM = 1.0e-5;
  36. /// <summary>
  37. /// The Esri element layer appears to require a rectangle to place an element.
  38. /// This parameter supports a very small rectangle.
  39. /// </summary>
  40. private const double LatM = 1.0e-5;
  41. /// <summary>
  42. /// The Esri element layer appears to require a rectangle to place an element.
  43. /// This parameter supports a very small rectangle.
  44. /// </summary>
  45. private const double LonP = 1.0e-5;
  46. /// <summary>
  47. /// The Esri element layer appears to require a rectangle to place an element.
  48. /// This parameter supports a very small rectangle.
  49. /// </summary>
  50. private const double LatP = 1.0e-5;
  51. // Ran -85.05112878 degrees (the minimum mercator latitude value) through the converter
  52. // Ran -180.0 degrees (the minimum mercator longitude value) through the converter
  53. /// <summary>
  54. /// Min/max web mercator coordinates supported by Esri which apparently uses a non-degreed extent.
  55. /// </summary>
  56. private const double MinWebY = -20037508.3430389;
  57. /// <summary>
  58. /// Min/max web mercator coordinates supported by Esri which apparently uses a non-degreed extent.
  59. /// </summary>
  60. private const double MaxWebY = 20037508.3430389;
  61. /// <summary>
  62. /// Min/max web mercator coordinates supported by Esri which apparently uses a non-degreed extent.
  63. /// </summary>
  64. private const double MinWebX = -20037508.3427892;
  65. /// <summary>
  66. /// Min/max web mercator coordinates supported by Esri which apparently uses a non-degreed extent.
  67. /// </summary>
  68. private const double MaxWebX = 20037508.3427892;
  69. /// <summary>
  70. /// The backing store for the layer's extent.
  71. /// </summary>
  72. private readonly EsriLocationRect layerExtent =
  73. new EsriLocationRect(180.0, 90.0, -180.0, -90.0);
  74. /// <summary>
  75. /// Supports the MapExtent backing store.
  76. /// </summary>
  77. private Envelope webExtent = new Envelope(); // the map extent in Web mercator coordinates
  78. /// <summary>
  79. /// Supports the MapExtent backing store.
  80. /// </summary>
  81. private EsriLocationRect mapExtent; // the map extent in lat-lon coordinates
  82. /// <summary>
  83. /// The backing store for the Map.
  84. /// </summary>
  85. private Map map;
  86. /// <summary>
  87. /// Supports the ZoomLevel backing store.
  88. /// </summary>
  89. private double lastResolution = -1.0;
  90. /// <summary>
  91. /// Supports the ZoomLevel backing store.
  92. /// </summary>
  93. private double zoomLevel; // = 0.0
  94. /// <summary>
  95. /// Gets the map layer's extent.
  96. /// </summary>
  97. public ILocationRect LayerExtent
  98. {
  99. get
  100. {
  101. return this.layerExtent;
  102. }
  103. }
  104. /// <summary>
  105. /// Gets MapExtent.
  106. /// </summary>
  107. public ILocationRect MapExtent
  108. {
  109. get
  110. {
  111. if (!this.webExtent.Equals(this.TheMap.Extent))
  112. {
  113. this.webExtent = this.TheMap.Extent.Clone();
  114. // There is a hard "x" and "y" limit on the WebMercatorToGeographic conversion functions
  115. var min = new MapPoint(
  116. Math.Max(this.webExtent.XMin, MinWebX), Math.Max(this.webExtent.YMin, MinWebY));
  117. var max = new MapPoint(
  118. Math.Min(this.webExtent.XMax, MaxWebX), Math.Min(this.webExtent.YMax, MaxWebY));
  119. this.mapExtent = new EsriLocationRect(
  120. min.WebMercatorToGeographic(),
  121. max.WebMercatorToGeographic());
  122. }
  123. return this.mapExtent;
  124. }
  125. }
  126. /// <summary>
  127. /// Gets the Map that contains this layer.
  128. /// </summary>
  129. public Map TheMap
  130. {
  131. get
  132. {
  133. if (this.map != null)
  134. {
  135. return this.map;
  136. }
  137. foreach (var esriMap in
  138. #if SILVERLIGHT
  139. Application.Current.RootVisual
  140. #else
  141. Application.Current.MainWindow
  142. #endif
  143. .GetVisuals().OfType<Map>()
  144. .Select(control => control)
  145. .Where(esriMap => esriMap.Layers.Contains(this)))
  146. {
  147. this.map = esriMap;
  148. return this.map; // in general this should be called once
  149. }
  150. return null;
  151. }
  152. }
  153. /// <summary>
  154. /// Gets the zoom level for the Esri map.
  155. /// </summary>
  156. public double ZoomLevel
  157. {
  158. get
  159. {
  160. if (this.TheMap == null)
  161. {
  162. return 0.0;
  163. }
  164. var resolution = this.map.Resolution;
  165. if (double.IsNaN(resolution))
  166. {
  167. return this.zoomLevel;
  168. }
  169. if (Math.Abs(resolution - this.lastResolution) < double.Epsilon)
  170. {
  171. return this.zoomLevel;
  172. }
  173. this.lastResolution = resolution;
  174. // If we were matching to ESRI's lat-lon mapping this would be it
  175. this.zoomLevel = (Math.Log((360.0 / 512.0) / resolution) / Math.Log(2)) + 1.0;
  176. // But we also need this empirically derived value to match
  177. // ESRI's Web zoom level to the true Bing map zoom level
  178. this.zoomLevel += 16.7643466889403;
  179. return this.zoomLevel;
  180. }
  181. }
  182. /// <summary>
  183. /// Gets a value indicating whether this map layer can be seen,
  184. /// specifically the map exists, has a real extent, and the layer's Visibility is true.
  185. /// </summary>
  186. public bool CanBeSeen
  187. {
  188. get
  189. {
  190. if (!this.Visible)
  191. {
  192. return false;
  193. }
  194. if (this.TheMap == null)
  195. {
  196. return false;
  197. }
  198. if (this.map.Extent == null)
  199. {
  200. return false;
  201. }
  202. return true;
  203. }
  204. }
  205. /// <summary>
  206. /// Adds a UIElement to the layer at the indicated location.
  207. /// </summary>
  208. /// <param name="ue">
  209. /// The UIElement to add to the layer.
  210. /// </param>
  211. /// <param name="geo">
  212. /// The map location at which to add the UIElement.
  213. /// </param>
  214. public void AddSymbol(UIElement ue, ILocation geo)
  215. {
  216. if (geo == null)
  217. {
  218. return;
  219. }
  220. // ESRI apparently doesn't use lat-lon with Bing maps
  221. var web = (geo as MapPoint).GeographicToWebMercator();
  222. var webEnvelope = new Envelope(web.X - LonM, web.Y - LatM, web.X + LonP, web.Y + LatP);
  223. // ReSharper disable RedundantNameQualifier
  224. ElementLayer.SetEnvelope(ue, webEnvelope);
  225. // ReSharper restore RedundantNameQualifier
  226. this.layerExtent.Expand(geo);
  227. // Extra work for special symbol types
  228. if (ue is MapMilSymbol)
  229. {
  230. ((MapMilSymbol)ue).Layer = this;
  231. // Similar computation in lat-lon space
  232. var geoEnvelope = new EsriLocationRect(
  233. geo.Longitude - LonM, geo.Latitude - LatM, geo.Longitude + LonP, geo.Latitude + LatP);
  234. ((MapMilSymbol)ue).SymbolExtent = geoEnvelope;
  235. }
  236. else if (ue is MilGraphic)
  237. {
  238. ((MilGraphic)ue).Layer = this;
  239. }
  240. if (!Children.Contains(ue))
  241. {
  242. Children.Add(ue);
  243. }
  244. }
  245. /// <summary>
  246. /// Adds a symbol to the map, assuming the symbol has an origin value set.
  247. /// </summary>
  248. /// <param name="symbol">
  249. /// The symbol to add to the map.
  250. /// </param>
  251. public void AddSymbol(ILocatable symbol)
  252. {
  253. var uiElement = symbol as UIElement;
  254. if (uiElement != null)
  255. {
  256. this.AddSymbol(uiElement, symbol.Origin);
  257. }
  258. }
  259. }
  260. }