WMSMapServiceLayer.cs
上传用户:huazai0421
上传日期:2008-05-30
资源大小:405k
文件大小:15k
源码类别:

SilverLight

开发平台:

C#

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.Globalization;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Text;
  8. using System.Windows;
  9. using System.Xml.Linq;
  10. using ESRI.ArcGIS.Client;
  11. using ESRI.ArcGIS.Client.Geometry;
  12. namespace ESRI.ArcGIS.Samples
  13. {
  14. /// <summary>
  15. /// A WMS service layer for the ArcGIS API for Microsoft Silverlight/WPF
  16. /// </summary>
  17. public class WMSMapServiceLayer : DynamicMapServiceLayer
  18. {
  19. string[] _layersArray;
  20. string _proxyUrl;
  21. BoundingExtent _boundingExtent = new BoundingExtent();
  22. // Coordinate system WKIDs in WMS 1.3 where X,Y (Long,Lat) is switched to Y,X (Lat,Long)
  23. private int[,] LatLongCRSRanges = new int[,] { { 4001, 4999 },
  24. {2044, 2045},   {2081, 2083},   {2085, 2086},   {2093, 2093},
  25. {2096, 2098},   {2105, 2132},   {2169, 2170},   {2176, 2180},
  26. {2193, 2193},   {2200, 2200},   {2206, 2212},   {2319, 2319},
  27. {2320, 2462},   {2523, 2549},   {2551, 2735},   {2738, 2758},
  28. {2935, 2941},   {2953, 2953},   {3006, 3030},   {3034, 3035},
  29. {3058, 3059},   {3068, 3068},   {3114, 3118},   {3126, 3138},
  30. {3300, 3301},   {3328, 3335},   {3346, 3346},   {3350, 3352},
  31. {3366, 3366},   {3416, 3416},   {20004, 20032}, {20064, 20092},
  32. {21413, 21423}, {21473, 21483}, {21896, 21899}, {22171, 22177},
  33. {22181, 22187}, {22191, 22197}, {25884, 25884}, {27205, 27232},
  34. {27391, 27398}, {27492, 27492}, {28402, 28432}, {28462, 28492},
  35. {30161, 30179}, {30800, 30800}, {31251, 31259}, {31275, 31279},
  36. {31281, 31290}, {31466, 31700} };
  37. public WMSMapServiceLayer()
  38. : base()
  39. {
  40. LayerList = new ObservableCollection<LayerInfo>();
  41. }
  42. #region Public Properties
  43. /// <summary>
  44. /// Required.  Gets or sets the URL to a WMS service endpoint.  
  45. /// For example, 
  46. /// http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer,
  47. /// http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi.
  48. /// </summary>
  49. /// <value>The URL.</value>
  50. public string Url { get; set; }
  51. /// <summary>
  52. /// Required. Gets or sets the unique layer ids in a WMS service.  
  53. /// Each id is a string value.  At least one layer id must be defined.   
  54. /// </summary>
  55. /// <value>A string array of layer ids.</value>
  56. [System.ComponentModel.TypeConverter(typeof(StringToStringArrayConverter))]
  57. public string[] Layers
  58. {
  59. get { return _layersArray; }
  60. set
  61. {
  62. _layersArray = value;
  63. OnLayerChanged();
  64. }
  65. }
  66. /// <summary>
  67. /// Optional. Gets or sets the URL to a proxy service that brokers Web requests between the Silverlight 
  68. /// client and a WMS service.  Use a proxy service when the WMS service is not hosted on a site that provides
  69. /// a cross domain policy file (clientaccesspolicy.xml or crossdomain.xml).  You can also use a proxy to
  70. /// convert png images to a bit-depth that supports transparency in Silverlight.  
  71. /// </summary>
  72. /// <value>The proxy URL string.</value>
  73. public string ProxyUrl
  74. {
  75. get { return _proxyUrl; }
  76. set { _proxyUrl = value; }
  77. }
  78. /// <summary>
  79. /// Optional. Gets or sets the WMS version.  If SkipGetCapabilities property is set to true, this value determines version requested.  
  80. /// If SkipGetCapabilities is false, this value determines version to retrieve.  If no value specified, default value returned from 
  81. /// the site will be used.
  82. /// </summary>
  83. /// <value>The version string.</value>
  84. public string Version { get; set; }
  85. /// <summary>
  86. /// Optional. Gets or sets a value indicating whether to skip a request to get capabilities. 
  87. /// Default value is false.  Set SkipGetCapabilities if the site hosting the WMS service does not provide a
  88. /// cross domain policy file and you do not have a proxy page.  In this case, you must set the WMS service version.
  89. /// If true, the initial and full extent of the WMS Silverlight layer will not be defined.
  90. /// </summary>
  91. public bool SkipGetCapabilities { get; set; }
  92. /// <summary>
  93. /// Gets the title metadata for this service.
  94. /// </summary>
  95. public string Title { get; private set; }
  96. /// <summary>
  97. /// Gets the abstract metadata for this service.
  98. /// </summary>
  99. public string Abstract { get; private set; }
  100. /// <summary>
  101. /// Gets a list of layers available in this service.
  102. /// </summary>
  103. public ObservableCollection<LayerInfo> LayerList { get; private set; }
  104. #endregion
  105. private bool initializing;
  106. /// <summary>
  107. /// Initializes this a WMS layer.  Calls GetCapabilities if SkipGetCapabilities is false. 
  108. /// </summary>
  109. public override void Initialize()
  110. {
  111. if (initializing || IsInitialized) return;
  112. initializing = true;
  113. if (SkipGetCapabilities)
  114. {
  115. base.Initialize();
  116. }
  117. else
  118. {
  119. string wmsUrl = string.Format("{0}{1}{2}",
  120. Url,
  121. "?service=WMS&request=GetCapabilities&version=",
  122. Version);
  123. WebClient client = new WebClient();
  124. client.DownloadStringCompleted += client_DownloadStringCompleted;
  125. client.DownloadStringAsync(PrefixProxy(wmsUrl));
  126. }
  127. }
  128. private Uri PrefixProxy(string url)
  129. {
  130. if (string.IsNullOrEmpty(ProxyUrl))
  131. return new Uri(url, UriKind.RelativeOrAbsolute);
  132. string proxyUrl = ProxyUrl;
  133. if (!proxyUrl.Contains("?"))
  134. {
  135. if (!proxyUrl.EndsWith("?"))
  136. proxyUrl = ProxyUrl + "?";
  137. }
  138. else
  139. {
  140. if (!proxyUrl.EndsWith("&"))
  141. proxyUrl = ProxyUrl + "&";
  142. }
  143. if (ProxyUrl.StartsWith("~") || ProxyUrl.StartsWith("../")) //relative to xap root
  144. {
  145. string uri = Application.Current.Host.Source.AbsoluteUri;
  146. int count = proxyUrl.Split(new string[] { "../" }, StringSplitOptions.None).Length;
  147. for (int i = 0; i < count; i++)
  148. {
  149. uri = uri.Substring(0, uri.LastIndexOf("/"));
  150. }
  151. if (!uri.EndsWith("/"))
  152. uri += "/";
  153. proxyUrl = uri + proxyUrl.Replace("~", "").Replace("../", "");
  154. }
  155. else if (ProxyUrl.StartsWith("/")) //relative to domain root
  156. {
  157. proxyUrl = ProxyUrl.Replace("/", string.Format("{0}://{1}:{2}",
  158. Application.Current.Host.Source.Scheme,
  159. Application.Current.Host.Source.Host,
  160. Application.Current.Host.Source.Port));
  161. }
  162. UriBuilder b = new UriBuilder(proxyUrl);
  163. b.Query = url;
  164. return b.Uri;
  165. }
  166. private string httpGetResource;
  167. public class LayerInfo
  168. {
  169. public string Name { get; set; }
  170. public string Title { get; set; }
  171. public string Abstract { get; set; }
  172. public Envelope Extent { get; set; }
  173. public IList<LayerInfo> ChildLayers { get; set; }
  174. }
  175. internal void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
  176. {
  177. if (!CheckForError(e))
  178. {
  179. // Process capabilities file
  180. XDocument xDoc = XDocument.Parse(e.Result);
  181. ParseCapabilities(xDoc);
  182. }
  183. // Call initialize regardless of error
  184. base.Initialize();
  185. }
  186. private void ParseCapabilities(XDocument xDoc)
  187. {
  188. //Get service info
  189. var info = (from Service in xDoc.Descendants("Service")
  190.    select new
  191.    {
  192.    Title = Service.Element("Title") == null ? null : Service.Element("Title").Value,
  193.    Abstract = Service.Element("Abstract") == null ? null : Service.Element("Abstract").Value
  194.    }).First();
  195. if (info != null)
  196. {
  197. this.Title = info.Title;
  198. //OnPropertyChanged("Title");
  199. this.Abstract = info.Abstract;
  200. //OnPropertyChanged("Abstract");
  201. }
  202. //Get a list of layers
  203. var layerList =  from Layers in xDoc.Descendants("Layer")
  204.  where Layers.Descendants("Layer").Count() == 0
  205.  select new LayerInfo()
  206.  {
  207.  Name = Layers.Element("Name") == null ? null : Layers.Element("Name").Value,
  208.  Title = Layers.Element("Title") == null ? null : Layers.Element("Title").Value,
  209.  Abstract = Layers.Element("Abstract") == null ? null : Layers.Element("Abstract").Value
  210.  };
  211. foreach (LayerInfo i in layerList)
  212. this.LayerList.Add(i);
  213. //OnPropertyChanged("LayerList");
  214. try 
  215. {
  216. //Get endpoint for GetMap requests
  217. //I wish SL supported XPath...
  218. var capabilities = (from c in xDoc.Descendants("Capability") select c).First();
  219. var request = (from c in capabilities.Descendants("Request") select c).First();
  220. var GetMap = (from c in request.Descendants("GetMap") select c).First();
  221. var DCPType = (from c in request.Descendants("DCPType") select c).First();
  222. var HTTP = (from c in request.Descendants("HTTP") select c).First();
  223. var Get = (from c in request.Descendants("Get") select c).First();
  224. var OnlineResource = (from c in request.Descendants("OnlineResource") select c).First();
  225. var href = OnlineResource.Attribute(XName.Get("href", "http://www.w3.org/1999/xlink"));
  226. httpGetResource = href.Value;
  227. }
  228. catch
  229. {   //Default to WMS url
  230. httpGetResource = this.Url;
  231. }
  232. //Get the full extent of all layers
  233. var box = (from Layers in xDoc.Descendants("LatLonBoundingBox")
  234.      select new
  235.  {
  236.  minx = double.Parse(Layers.Attribute("minx").Value),
  237.  miny = double.Parse(Layers.Attribute("miny").Value),
  238.  maxx = double.Parse(Layers.Attribute("maxx").Value),
  239.  maxy = double.Parse(Layers.Attribute("maxy").Value)
  240.  }).ToList();
  241. var minx = (from minmax in box select minmax.minx).Min();
  242. var miny = (from minmax in box select minmax.miny).Min();
  243. var maxx = (from minmax in box select minmax.maxx).Max();
  244. var maxy = (from minmax in box select minmax.maxy).Max();
  245. this.FullExtent = new Envelope(minx, miny, maxx, maxy) { SpatialReference = new SpatialReference(4326) };
  246. }
  247. private bool CheckForError(DownloadStringCompletedEventArgs e)
  248. {
  249. if (e.Cancelled)
  250. {
  251. InitializationFailure = new Exception("Request Cancelled");
  252. return true;
  253. }
  254. if (e.Error != null)
  255. {
  256. Exception ex = e.Error;
  257. if (ex is System.Security.SecurityException)
  258. {
  259. ex = new System.Security.SecurityException(
  260. @"A security exception occured while trying to connect to the WMS service. 
  261.                         Make sure you have a cross domain policy file available at the root for your server that allows for requests from this application.  
  262.                         If not, use a proxy page (handler) to broker communication.",
  263. ex);
  264. }
  265. InitializationFailure = ex;
  266. return true;
  267. }
  268. return false;
  269. }
  270. /// <summary>
  271. /// Gets the URL. Override from DynamicMapServiceLayer
  272. /// </summary>
  273. /// <param name="extent">The extent.</param>
  274. /// <param name="width">The width.</param>
  275. /// <param name="height">The height.</param>
  276. /// <param name="onComplete">OnUrlComplete delegate.</param>
  277. /// <remarks>
  278. /// The Map has a private method loadLayerInView which calls Layer.Draw.   
  279. /// The DynamicMapServiceLayer abstract class overrides the Draw method and calls 
  280. /// DynamicMapServiceLayer.GetUrl which must be implemented in a subclass.   
  281. /// The last parameter is the OnUrlComplete delegate, which is used to pass the appropriate values 
  282. /// (url, width, height, envelope) to the private DynamicMapServiceLayer.getUrlComplete method.
  283. /// </remarks>
  284. public override void GetUrl(ESRI.ArcGIS.Client.Geometry.Envelope extent, int width, int height,
  285. DynamicMapServiceLayer.OnUrlComplete onComplete)
  286. {
  287. int extentWKID = extent.SpatialReference.WKID;
  288. string baseUrl = httpGetResource ?? Url;
  289. StringBuilder mapURL = new StringBuilder(baseUrl);
  290. if (!baseUrl.Contains("?"))
  291. mapURL.Append("?");
  292. else if (!baseUrl.EndsWith("&"))
  293. mapURL.Append("&");
  294. mapURL.Append("SERVICE=WMS");
  295. mapURL.Append("&REQUEST=GetMap");
  296. mapURL.AppendFormat("&WIDTH={0}", width);
  297. mapURL.AppendFormat("&HEIGHT={0}", height);
  298. mapURL.AppendFormat("&FORMAT={0}", "image/png");
  299. mapURL.AppendFormat("&LAYERS={0}", String.Join(",", Layers));
  300. mapURL.Append("&STYLE=");
  301. mapURL.AppendFormat("&BGCOLOR={0}", "0xFFFFFF");
  302. mapURL.AppendFormat("&TRANSPARENT={0}", "true");
  303. mapURL.AppendFormat("&VERSION={0}", Version);
  304. switch (Version)
  305. {
  306. case ("1.1.1"):
  307. mapURL.AppendFormat("&SRS=EPSG:{0}", extentWKID);
  308. mapURL.AppendFormat("&bbox={0},{1},{2},{3}", extent.XMin, extent.YMin, extent.XMax, extent.YMax);
  309. break;
  310. case ("1.3"):
  311. case ("1.3.0"):
  312. mapURL.AppendFormat("&CRS=EPSG:{0}", extentWKID);
  313. bool useLatLong = false;
  314. int length = LatLongCRSRanges.Length / 2;
  315. for (int count = 0; count < length; count++)
  316. {
  317. if (extentWKID >= LatLongCRSRanges[count, 0] && extentWKID <= LatLongCRSRanges[count, 1])
  318. {
  319. useLatLong = true;
  320. break;
  321. }
  322. }
  323. if (useLatLong)
  324. mapURL.AppendFormat(CultureInfo.InvariantCulture,
  325. "&BBOX={0},{1},{2},{3}", extent.YMin, extent.XMin, extent.YMax, extent.XMax);
  326. else
  327. mapURL.AppendFormat(CultureInfo.InvariantCulture, 
  328. "&BBOX={0},{1},{2},{3}", extent.XMin, extent.YMin, extent.XMax, extent.YMax);
  329. break;
  330. }
  331. onComplete(PrefixProxy(mapURL.ToString()).AbsoluteUri, width, height, new ESRI.ArcGIS.Client.Geometry.Envelope()
  332. {
  333. XMin = extent.XMin,
  334. YMin = extent.YMin,
  335. XMax = extent.XMax,
  336. YMax = extent.YMax
  337. });
  338. }
  339. internal class BoundingExtent
  340. {
  341. double _xmin = double.MaxValue;
  342. double _ymin = double.MaxValue;
  343. double _xmax = double.MinValue;
  344. double _ymax = double.MinValue;
  345. public double XMin
  346. {
  347. get { return _xmin; }
  348. set { if (value < _xmin) _xmin = value; }
  349. }
  350. public double YMin
  351. {
  352. get { return _ymin; }
  353. set { if (value < _ymin) _ymin = value; }
  354. }
  355. public double XMax
  356. {
  357. get { return _xmax; }
  358. set { if (value > _xmax) _xmax = value; }
  359. }
  360. public double YMax
  361. {
  362. get { return _ymax; }
  363. set { if (value > _ymax) _ymax = value; }
  364. }
  365. }
  366. /// <summary>
  367. /// String To String Array Converter
  368. /// </summary>
  369. public sealed class StringToStringArrayConverter : System.ComponentModel.TypeConverter
  370. {
  371. public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, Type sourceType)
  372. {
  373. return (sourceType == typeof(string));
  374. }
  375. public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
  376. {
  377. if (value == null)
  378. return null;
  379. if (value is string)
  380. {
  381. string[] values = (value as string).Replace(" ", "").Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
  382. if (values.Length == 0)
  383. return null;
  384. return values;
  385. }
  386. throw new NotSupportedException("Cannot convert to string array");
  387. }
  388. }
  389. }
  390. }