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

SilverLight

开发平台:

C#

  1. using System;
  2. using System.Diagnostics;
  3. using System.Net;
  4. using System.Text;
  5. using System.Windows;
  6. using System.Xml;
  7. using System.Xml.Linq;
  8. using ESRI.ArcGIS.Client;
  9. using ESRI.ArcGIS.Client.Geometry;
  10. using System.Globalization;
  11. using System.Collections.Generic;
  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[] _layers;
  20. string[] _layersArray;
  21. string _proxyUrl;
  22. BoundingExtent _boundingExtent = new BoundingExtent();
  23. ESRI.ArcGIS.Client.Geometry.Envelope InitialExtent;
  24. // Coordinate system WKIDs in WMS 1.3 where X,Y (Long,Lat) switched to Y,X (Lat,Long)
  25. private int[,] LatLongCRSRanges = new int[,] { { 4001, 4999 },
  26. {2044, 2045},   {2081, 2083},   {2085, 2086},   {2093, 2093},
  27. {2096, 2098},   {2105, 2132},   {2169, 2170},   {2176, 2180},
  28. {2193, 2193},   {2200, 2200},   {2206, 2212},   {2319, 2319},
  29. {2320, 2462},   {2523, 2549},   {2551, 2735},   {2738, 2758},
  30. {2935, 2941},   {2953, 2953},   {3006, 3030},   {3034, 3035},
  31. {3058, 3059},   {3068, 3068},   {3114, 3118},   {3126, 3138},
  32. {3300, 3301},   {3328, 3335},   {3346, 3346},   {3350, 3352},
  33. {3366, 3366},   {3416, 3416},   {20004, 20032}, {20064, 20092},
  34. {21413, 21423}, {21473, 21483}, {21896, 21899}, {22171, 22177},
  35. {22181, 22187}, {22191, 22197}, {25884, 25884}, {27205, 27232},
  36. {27391, 27398}, {27492, 27492}, {28402, 28432}, {28462, 28492},
  37. {30161, 30179}, {30800, 30800}, {31251, 31259}, {31275, 31279},
  38. {31281, 31290}, {31466, 31700} };
  39. public WMSMapServiceLayer()
  40. : base()
  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
  51. {
  52. get;
  53. set;
  54. }
  55. /// <summary>
  56. /// Required. Gets or sets the unique layer ids in a WMS service.  
  57. /// Each id is a string value.  At least one layer id must be defined.   
  58. /// </summary>
  59. /// <value>A string array of layer ids.</value>
  60. [System.ComponentModel.TypeConverter(typeof(StringToStringArrayConverter))]
  61. public string[] Layers
  62. {
  63. get { return _layersArray; }
  64. set
  65. {
  66. _layersArray = value;
  67. OnLayerChanged();
  68. }
  69. }
  70. /// <summary>
  71. /// Optional. Gets or sets the URL to a proxy service that brokers Web requests between the Silverlight 
  72. /// client and a WMS service.  Use a proxy service when the WMS service is not hosted on a site that provides
  73. /// a cross domain policy file (clientaccesspolicy.xml or crossdomain.xml).  You can also use a proxy to
  74. /// convert png images to a bit-depth that supports transparency in Silverlight.  
  75. /// </summary>
  76. /// <value>The proxy URL string.</value>
  77. public string ProxyUrl
  78. {
  79. get { return _proxyUrl; }
  80. set { _proxyUrl = value; }
  81. }
  82. /// <summary>
  83. /// Optional. Gets or sets the WMS version.  If SkipGetCapabilities property is set to true, this value determines version requested.  
  84. /// If SkipGetCapabilities is false, this value determines version to retrieve.  If no value specified, default value returned from 
  85. /// the site will be used.
  86. /// </summary>
  87. /// <value>The version string.</value>
  88. public string Version
  89. {
  90. get;
  91. set;
  92. }
  93. /// <summary>
  94. /// Optional. Gets or sets a value indicating whether to skip a request to get capabilities. 
  95. /// Default value is false.  Set SkipGetCapabilities if the site hosting the WMS service does not provide a
  96. /// cross domain policy file and you do not have a proxy page.  In this case, you must set the WMS service version.
  97. /// If true, the initial and full extent of the WMS Silverlight layer will not be defined.
  98. /// </summary>
  99. public bool SkipGetCapabilities
  100. {
  101. get;
  102. set;
  103. }
  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 List<string> LayerList;
  167. 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. // Will be set when capabilities processed
  180. InitialExtent = new ESRI.ArcGIS.Client.Geometry.Envelope();
  181. // Process capabilities file
  182. XDocument xDoc = XDocument.Parse(e.Result);
  183. LayerList = new List<string>();
  184. processNode(xDoc);
  185. // Define full extent when all included layers are processed
  186. FullExtent = new Envelope(_boundingExtent.XMin, _boundingExtent.XMax,
  187. _boundingExtent.YMin, _boundingExtent.YMax);
  188. }
  189. // Call initialize regardless of error
  190. base.Initialize();
  191. }
  192. #region Process Capabilities
  193. bool initExtent = false;
  194. bool addToFullExtent = false;
  195. private void processNode(XNode node)
  196. {
  197. switch (node.NodeType)
  198. {
  199. case XmlNodeType.Document:
  200. XDocument document = (XDocument)node;
  201. foreach (XNode n in document.Nodes())
  202. {
  203. processNode(n);
  204. }
  205. break;
  206. case XmlNodeType.Element:
  207. XElement element = (XElement)node;
  208. Debug.WriteLine("StartElement: {0}", element.Name);
  209. if (element.Name.LocalName == "Layer")
  210. {
  211. LayerInfo layer = ParseLayerNode(element);
  212. }
  213. // 1.1.1 layer name
  214. if (element.Name.LocalName == "Name" && element.Value == _layersArray[0])
  215. {
  216. initExtent = true;
  217. addToFullExtent = true;
  218. }
  219. else if (element.Name.LocalName == "Name")
  220. for (int i = 0; i < _layersArray.Length; i++)
  221. {
  222. if (element.Value == _layersArray[i])
  223. addToFullExtent = true;
  224. }
  225. // 1.3
  226. if (element.Name.LocalName == "EX_GeographicBoundingBox")
  227. initExtent = true;
  228. if (initExtent && element.Parent.Name.LocalName == "EX_GeographicBoundingBox")
  229. {
  230. switch (element.Name.LocalName)
  231. {
  232. case "westBoundLongitude":
  233. InitialExtent.XMin = Double.Parse(element.Value);
  234. break;
  235. case "eastBoundLongitude":
  236. InitialExtent.XMax = Double.Parse(element.Value);
  237. break;
  238. case "southBoundLatitude":
  239. InitialExtent.YMin = Double.Parse(element.Value);
  240. break;
  241. case "northBoundLatitude":
  242. InitialExtent.YMax = Double.Parse(element.Value);
  243. break;
  244. }
  245. }
  246. if (element.HasAttributes)
  247. {
  248. foreach (XAttribute attribute in element.Attributes())
  249. {
  250. if ((element.Name.LocalName == "WMT_MS_Capabilities" ||
  251. element.Name.LocalName == "WMS_Capabilities") &&
  252. attribute.Name.LocalName == "version")
  253. {
  254. Version = attribute.Value;
  255. }
  256. //--- 1.1.1 
  257. if (initExtent && element.Name.LocalName == "BoundingBox")
  258. {
  259. switch (attribute.Name.LocalName)
  260. {
  261. case "minx":
  262. InitialExtent.XMin = Double.Parse(attribute.Value);
  263. break;
  264. case "miny":
  265. InitialExtent.YMin = Double.Parse(attribute.Value);
  266. break;
  267. case "maxx":
  268. InitialExtent.XMax = Double.Parse(attribute.Value);
  269. break;
  270. case "maxy":
  271. InitialExtent.YMax = Double.Parse(attribute.Value);
  272. break;
  273. }
  274. }
  275. if (addToFullExtent && element.Name.LocalName == "BoundingBox")
  276. {
  277. switch (attribute.Name.LocalName)
  278. {
  279. case "minx":
  280. _boundingExtent.XMin = Double.Parse(attribute.Value);
  281. break;
  282. case "miny":
  283. _boundingExtent.YMin = Double.Parse(attribute.Value);
  284. break;
  285. case "maxx":
  286. _boundingExtent.XMax = Double.Parse(attribute.Value);
  287. break;
  288. case "maxy":
  289. _boundingExtent.YMax = Double.Parse(attribute.Value);
  290. break;
  291. }
  292. }
  293. Debug.WriteLine("Attribute: {0} = {1}", attribute.Name, attribute.Value);
  294. }
  295. // Define initial extent (first layer) and full extent (all layers)
  296. if (initExtent && element.Name.ToString().Contains("BoundingBox"))
  297. initExtent = false;
  298. if (addToFullExtent && element.Name.ToString().Contains("BoundingBox"))
  299. addToFullExtent = false;
  300. if (!element.IsEmpty)
  301. {
  302. foreach (XNode n in element.Nodes())
  303. {
  304. processNode(n);
  305. }
  306. }
  307. Debug.WriteLine("EndElement: {0}", element.Name);
  308. break;
  309. }
  310. break;
  311. default:
  312. break;
  313. }
  314. }
  315. private LayerInfo ParseLayerNode(XElement element)
  316. {
  317. LayerInfo info = new LayerInfo();
  318. foreach (XNode node in element.Nodes())
  319. {
  320. if (node.NodeType == XmlNodeType.Element)
  321. {
  322. XElement e = (XElement)node;
  323. if (e.Name.LocalName == "Name")
  324. info.Name = e.Value;
  325. else if (e.Name.LocalName == "Title")
  326. info.Title = e.Value;
  327. else if (e.Name.LocalName == "Abstract")
  328. info.Abstract = e.Value;
  329. else if (e.Name.LocalName == "Layer")
  330. {
  331. if (info.ChildLayers == null)
  332. info.ChildLayers = new List<LayerInfo>();
  333. info.ChildLayers.Add(ParseLayerNode(e));
  334. }
  335. }
  336. }
  337. return info;
  338. }
  339. #endregion
  340. private bool CheckForError(DownloadStringCompletedEventArgs e)
  341. {
  342. if (e.Cancelled)
  343. {
  344. InitializationFailure = new Exception("Request Cancelled");
  345. return true;
  346. }
  347. if (e.Error != null)
  348. {
  349. Exception ex = e.Error;
  350. if (ex is System.Security.SecurityException)
  351. {
  352. ex = new System.Security.SecurityException(
  353. @"A security exception occured while trying to connect to the WMS service. 
  354.                         Make sure you have a cross domain policy file available at the root for your server that allows for requests from this application.  
  355.                         If not, use a proxy page (handler) to broker communication.",
  356. ex);
  357. }
  358. InitializationFailure = ex;
  359. return true;
  360. }
  361. return false;
  362. }
  363. /// <summary>
  364. /// Gets the URL. Override from DynamicMapServiceLayer
  365. /// </summary>
  366. /// <param name="extent">The extent.</param>
  367. /// <param name="width">The width.</param>
  368. /// <param name="height">The height.</param>
  369. /// <param name="onComplete">OnUrlComplete delegate.</param>
  370. /// <remarks>
  371. /// The Map has a private method loadLayerInView which calls Layer.Draw.   
  372. /// The DynamicMapServiceLayer abstract class overrides the Draw method and calls 
  373. /// DynamicMapServiceLayer.GetUrl which must be implemented in a subclass.   
  374. /// The last parameter is the OnUrlComplete delegate, which is used to pass the appropriate values 
  375. /// (url, width, height, envelope) to the private DynamicMapServiceLayer.getUrlComplete method.
  376. /// </remarks>
  377. public override void GetUrl(ESRI.ArcGIS.Client.Geometry.Envelope extent, int width, int height,
  378. DynamicMapServiceLayer.OnUrlComplete onComplete)
  379. {
  380. int extentWKID = extent.SpatialReference.WKID;
  381. StringBuilder mapURL = new StringBuilder(Url);
  382. mapURL.Append("?SERVICE=WMS");
  383. mapURL.Append("&REQUEST=GetMap");
  384. mapURL.AppendFormat("&WIDTH={0}", width);
  385. mapURL.AppendFormat("&HEIGHT={0}", height);
  386. mapURL.AppendFormat("&FORMAT={0}", "image/png");
  387. mapURL.AppendFormat("&LAYERS={0}", String.Join(",", Layers));
  388. mapURL.Append("&STYLE=");
  389. mapURL.AppendFormat("&BGCOLOR={0}", "0xFFFFFF");
  390. mapURL.AppendFormat("&TRANSPARENT={0}", "true");
  391. mapURL.AppendFormat("&VERSION={0}", Version);
  392. switch (Version)
  393. {
  394. case ("1.1.1"):
  395. mapURL.AppendFormat("&SRS=EPSG:{0}", extentWKID);
  396. mapURL.AppendFormat("&bbox={0},{1},{2},{3}", extent.XMin, extent.YMin, extent.XMax, extent.YMax);
  397. break;
  398. case ("1.3"):
  399. case ("1.3.0"):
  400. mapURL.AppendFormat("&CRS=EPSG:{0}", extentWKID);
  401. bool useLatLong = false;
  402. int length = LatLongCRSRanges.Length / 2;
  403. for (int count = 0; count < length; count++)
  404. {
  405. if (extentWKID >= LatLongCRSRanges[count, 0] && extentWKID <= LatLongCRSRanges[count, 1])
  406. {
  407. useLatLong = true;
  408. break;
  409. }
  410. }
  411. if (useLatLong)
  412. mapURL.AppendFormat(CultureInfo.InvariantCulture,
  413. "&BBOX={0},{1},{2},{3}", extent.YMin, extent.XMin, extent.YMax, extent.XMax);
  414. else
  415. mapURL.AppendFormat(CultureInfo.InvariantCulture, 
  416. "&BBOX={0},{1},{2},{3}", extent.XMin, extent.YMin, extent.XMax, extent.YMax);
  417. break;
  418. }
  419. onComplete(PrefixProxy(mapURL.ToString()).AbsoluteUri, width, height, new ESRI.ArcGIS.Client.Geometry.Envelope()
  420. {
  421. XMin = extent.XMin,
  422. YMin = extent.YMin,
  423. XMax = extent.XMax,
  424. YMax = extent.YMax
  425. });
  426. }
  427. internal class BoundingExtent
  428. {
  429. double _xmin = double.MaxValue;
  430. double _ymin = double.MaxValue;
  431. double _xmax = double.MinValue;
  432. double _ymax = double.MinValue;
  433. public double XMin
  434. {
  435. get { return _xmin; }
  436. set { if (value < _xmin) _xmin = value; }
  437. }
  438. public double YMin
  439. {
  440. get { return _ymin; }
  441. set { if (value < _ymin) _ymin = value; }
  442. }
  443. public double XMax
  444. {
  445. get { return _xmax; }
  446. set { if (value > _xmax) _xmax = value; }
  447. }
  448. public double YMax
  449. {
  450. get { return _ymax; }
  451. set { if (value > _ymax) _ymax = value; }
  452. }
  453. }
  454. /// <summary>
  455. /// String To String Array Converter
  456. /// </summary>
  457. public sealed class StringToStringArrayConverter : System.ComponentModel.TypeConverter
  458. {
  459. public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, Type sourceType)
  460. {
  461. return (sourceType == typeof(string));
  462. }
  463. public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
  464. {
  465. if (value == null)
  466. return null;
  467. if (value is string)
  468. {
  469. string[] values = (value as string).Replace(" ", "").Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
  470. if (values.Length == 0)
  471. return null;
  472. return values;
  473. }
  474. throw new NotSupportedException("Cannot convert to string array");
  475. }
  476. }
  477. }
  478. }