using System; using System.Collections.ObjectModel; using System.Drawing; using System.Globalization; using System.IO; using System.Net; using System.Text; using System.Xml; using GeoAPI.Geometries; using SharpMap.Converters.Geometries; namespace SharpMap.Web.Wms { /// /// Class for requesting and parsing a WMS servers capabilities /// [Serializable] public class Client { private XmlNamespaceManager nsmgr; /// /// Initalizes WMS server and parses the Capabilities request /// /// URL of wms server public Client(string url) : this(url, null) {} /// /// Initalizes WMS server and parses the Capabilities request /// /// URL of wms server /// Proxy to use public Client(string url, WebProxy proxy) { StringBuilder strReq = new StringBuilder(url); if (!url.Contains("?")) { strReq.Append("?"); } if (!strReq.ToString().EndsWith("&") && !strReq.ToString().EndsWith("?")) { strReq.Append("&"); } if (!url.ToLower().Contains("service=wms")) { strReq.AppendFormat("SERVICE=WMS&"); } if (!url.ToLower().Contains("request=getcapabilities")) { strReq.AppendFormat("REQUEST=GetCapabilities&"); } XmlDocument xml = GetRemoteXml(strReq.ToString(), proxy); ParseCapabilities(xml); } /// /// Downloads servicedescription from WMS service /// /// XmlDocument from Url. Null if Url is empty or inproper XmlDocument private XmlDocument GetRemoteXml(string Url, WebProxy proxy) { try { WebRequest myRequest = WebRequest.Create(Url); if (proxy != null) { myRequest.Proxy = proxy; } WebResponse myResponse = myRequest.GetResponse(); Stream stream = myResponse.GetResponseStream(); XmlTextReader r = new XmlTextReader(Url, stream); XmlDocument doc = new XmlDocument(); doc.Load(r); stream.Close(); nsmgr = new XmlNamespaceManager(doc.NameTable); return doc; } catch (Exception ex) { throw new ApplicationException("Could now download capabilities", ex); } } /// /// Parses a servicedescription and stores the data in the ServiceDescription property /// /// XmlDocument containing a valid Service Description private void ParseCapabilities(XmlDocument doc) { if (doc.DocumentElement.Attributes["version"] != null) { WmsVersion = doc.DocumentElement.Attributes["version"].Value; if (WmsVersion != "1.0.0" && WmsVersion != "1.1.0" && WmsVersion != "1.1.1" && WmsVersion != "1.3.0") { throw new ApplicationException("WMS Version " + WmsVersion + " not supported"); } nsmgr.AddNamespace(String.Empty, "http://www.opengis.net/wms"); if (WmsVersion == "1.3.0") { nsmgr.AddNamespace("sm", "http://www.opengis.net/wms"); } else { nsmgr.AddNamespace("sm", ""); } nsmgr.AddNamespace("xlink", "http://www.w3.org/1999/xlink"); nsmgr.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"); } else { throw (new ApplicationException("No service version number found!")); } XmlNode xnService = doc.DocumentElement.SelectSingleNode("sm:Service", nsmgr); XmlNode xnCapability = doc.DocumentElement.SelectSingleNode("sm:Capability", nsmgr); if (xnService != null) { ParseServiceDescription(xnService); } else { throw (new ApplicationException("No service tag found!")); } if (xnCapability != null) { ParseCapability(xnCapability); } else { throw (new ApplicationException("No capability tag found!")); } } /// /// Parses service description node /// /// private void ParseServiceDescription(XmlNode xnlServiceDescription) { XmlNode node = xnlServiceDescription.SelectSingleNode("sm:Title", nsmgr); _ServiceDescription.Title = (node != null ? node.InnerText : null); node = xnlServiceDescription.SelectSingleNode("sm:OnlineResource/@xlink:href", nsmgr); _ServiceDescription.OnlineResource = (node != null ? node.InnerText : null); node = xnlServiceDescription.SelectSingleNode("sm:Abstract", nsmgr); _ServiceDescription.Abstract = (node != null ? node.InnerText : null); node = xnlServiceDescription.SelectSingleNode("sm:Fees", nsmgr); _ServiceDescription.Fees = (node != null ? node.InnerText : null); node = xnlServiceDescription.SelectSingleNode("sm:AccessConstraints", nsmgr); _ServiceDescription.AccessConstraints = (node != null ? node.InnerText : null); XmlNodeList xnlKeywords = xnlServiceDescription.SelectNodes("sm:KeywordList/sm:Keyword", nsmgr); if (xnlKeywords != null) { _ServiceDescription.Keywords = new string[xnlKeywords.Count]; for (int i = 0; i < xnlKeywords.Count; i++) { ServiceDescription.Keywords[i] = xnlKeywords[i].InnerText; } } //Contact information _ServiceDescription.ContactInformation = new Capabilities.WmsContactInformation(); node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactAddress/sm:Address", nsmgr); _ServiceDescription.ContactInformation.Address.Address = (node != null ? node.InnerText : null); node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactAddress/sm:AddressType", nsmgr); _ServiceDescription.ContactInformation.Address.AddressType = (node != null ? node.InnerText : null); node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactAddress/sm:City", nsmgr); _ServiceDescription.ContactInformation.Address.City = (node != null ? node.InnerText : null); node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactAddress/sm:Country", nsmgr); _ServiceDescription.ContactInformation.Address.Country = (node != null ? node.InnerText : null); node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactAddress/sm:PostCode", nsmgr); _ServiceDescription.ContactInformation.Address.PostCode = (node != null ? node.InnerText : null); node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactElectronicMailAddress", nsmgr); _ServiceDescription.ContactInformation.Address.StateOrProvince = (node != null ? node.InnerText : null); node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactElectronicMailAddress", nsmgr); _ServiceDescription.ContactInformation.ElectronicMailAddress = (node != null ? node.InnerText : null); node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactFacsimileTelephone", nsmgr); _ServiceDescription.ContactInformation.FacsimileTelephone = (node != null ? node.InnerText : null); node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactPersonPrimary/sm:ContactOrganisation", nsmgr); _ServiceDescription.ContactInformation.PersonPrimary.Organisation = (node != null ? node.InnerText : null); node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactPersonPrimary/sm:ContactPerson", nsmgr); _ServiceDescription.ContactInformation.PersonPrimary.Person = (node != null ? node.InnerText : null); node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactVoiceTelephone", nsmgr); _ServiceDescription.ContactInformation.VoiceTelephone = (node != null ? node.InnerText : null); } /// /// Parses capability node /// /// private void ParseCapability(XmlNode xnCapability) { XmlNode xnRequest = xnCapability.SelectSingleNode("sm:Request", nsmgr); if (xnRequest == null) { throw (new Exception("Request parameter not specified in Service Description")); } ParseRequest(xnRequest); XmlNode xnLayer = xnCapability.SelectSingleNode("sm:Layer", nsmgr); if (xnLayer == null) { throw (new Exception("No layer tag found in Service Description")); } Layer = ParseLayer(xnLayer); XmlNode xnException = xnCapability.SelectSingleNode("sm:Exception", nsmgr); if (xnException != null) { ParseExceptions(xnException); } } /// /// Parses valid exceptions /// /// private void ParseExceptions(XmlNode xnlExceptionNode) { XmlNodeList xnlFormats = xnlExceptionNode.SelectNodes("sm:Format", nsmgr); if (xnlFormats != null) { ExceptionFormats = new string[xnlFormats.Count]; for (int i = 0; i < xnlFormats.Count; i++) { ExceptionFormats[i] = xnlFormats[i].InnerText; } } } /// /// Parses request node /// /// private void ParseRequest(XmlNode xmlRequestNode) { XmlNode xnGetMap = xmlRequestNode.SelectSingleNode("sm:GetMap", nsmgr); ParseGetMapRequest(xnGetMap); //TODO: //XmlNode xnGetFeatureInfo = xmlRequestNodes.SelectSingleNode("sm:GetFeatureInfo", nsmgr); //XmlNode xnCapa = xmlRequestNodes.SelectSingleNode("sm:GetCapabilities", nsmgr); <-- We don't really need this do we? } /// /// Parses GetMap request nodes /// /// private void ParseGetMapRequest(XmlNode GetMapRequestNodes) { XmlNode xnlHttp = GetMapRequestNodes.SelectSingleNode("sm:DCPType/sm:HTTP", nsmgr); if (xnlHttp != null && xnlHttp.HasChildNodes) { GetMapRequests = new WmsOnlineResource[xnlHttp.ChildNodes.Count]; for (int i = 0; i < xnlHttp.ChildNodes.Count; i++) { WmsOnlineResource wor = new WmsOnlineResource(); wor.Type = xnlHttp.ChildNodes[i].Name; wor.OnlineResource = xnlHttp.ChildNodes[i].SelectSingleNode("sm:OnlineResource", nsmgr).Attributes["xlink:href"].InnerText; GetMapRequests[i] = wor; } } XmlNodeList xnlFormats = GetMapRequestNodes.SelectNodes("sm:Format", nsmgr); //_GetMapOutputFormats = new Collection(xnlFormats.Count); GetMapOutputFormats = new Collection(); for (int i = 0; i < xnlFormats.Count; i++) { GetMapOutputFormats.Add(xnlFormats[i].InnerText); } } /// /// Iterates through the layer nodes recursively /// /// /// private WmsServerLayer ParseLayer(XmlNode xmlLayer) { WmsServerLayer layer = new WmsServerLayer(); XmlNode node = xmlLayer.SelectSingleNode("sm:Name", nsmgr); layer.Name = (node != null ? node.InnerText : null); node = xmlLayer.SelectSingleNode("sm:Title", nsmgr); layer.Title = (node != null ? node.InnerText : null); if (string.IsNullOrEmpty(layer.Name)) { layer.Name = layer.Title; } node = xmlLayer.SelectSingleNode("sm:Abstract", nsmgr); layer.Abstract = (node != null ? node.InnerText : null); XmlAttribute attr = xmlLayer.Attributes["queryable"]; layer.Queryable = (attr != null && attr.InnerText == "1"); XmlNodeList xnlKeywords = xmlLayer.SelectNodes("sm:KeywordList/sm:Keyword", nsmgr); if (xnlKeywords != null) { layer.Keywords = new string[xnlKeywords.Count]; for (int i = 0; i < xnlKeywords.Count; i++) { layer.Keywords[i] = xnlKeywords[i].InnerText; } } XmlNodeList xnlCrs = xmlLayer.SelectNodes("sm:CRS", nsmgr); if (xnlCrs != null) { layer.CRS = new string[xnlCrs.Count]; for (int i = 0; i < xnlCrs.Count; i++) { layer.CRS[i] = xnlCrs[i].InnerText; } } XmlNodeList xnlStyle = xmlLayer.SelectNodes("sm:Style", nsmgr); if (xnlStyle != null) { layer.Style = new WmsLayerStyle[xnlStyle.Count]; for (int i = 0; i < xnlStyle.Count; i++) { node = xnlStyle[i].SelectSingleNode("sm:Name", nsmgr); layer.Style[i].Name = (node != null ? node.InnerText : null); node = xnlStyle[i].SelectSingleNode("sm:Title", nsmgr); layer.Style[i].Title = (node != null ? node.InnerText : null); node = xnlStyle[i].SelectSingleNode("sm:Abstract", nsmgr); layer.Style[i].Abstract = (node != null ? node.InnerText : null); node = xnlStyle[i].SelectSingleNode("sm:LegendUrl", nsmgr); if (node != null) { layer.Style[i].LegendUrl = new WmsStyleLegend(); layer.Style[i].LegendUrl.Size = new Size( int.Parse(node.Attributes["width"].InnerText), int.Parse(node.Attributes["height"].InnerText)); layer.Style[i].LegendUrl.OnlineResource.OnlineResource = node.SelectSingleNode("sm:OnlineResource", nsmgr).Attributes["xlink:href"].InnerText; layer.Style[i].LegendUrl.OnlineResource.Type = node.SelectSingleNode("sm:Format", nsmgr).InnerText; } node = xnlStyle[i].SelectSingleNode("sm:StyleSheetURL", nsmgr); if (node != null) { layer.Style[i].StyleSheetUrl = new WmsOnlineResource(); layer.Style[i].StyleSheetUrl.OnlineResource = node.SelectSingleNode("sm:OnlineResource", nsmgr).Attributes["xlink:href"].InnerText; //layer.Style[i].StyleSheetUrl.OnlineResource = node.SelectSingleNode("sm:Format", nsmgr).InnerText; } } } XmlNodeList xnlLayers = xmlLayer.SelectNodes("sm:Layer", nsmgr); if (xnlLayers != null) { layer.ChildLayers = new WmsServerLayer[xnlLayers.Count]; for (int i = 0; i < xnlLayers.Count; i++) { layer.ChildLayers[i] = ParseLayer(xnlLayers[i]); } } node = xmlLayer.SelectSingleNode("sm:LatLonBoundingBox", nsmgr); if (node == null) { node = xmlLayer.SelectSingleNode("sm:BoundingBox", nsmgr); } if (node != null) { double minx = 0; double miny = 0; double maxx = 0; double maxy = 0; if (!double.TryParse(node.Attributes["minx"].Value, NumberStyles.Any, Map.numberFormat_EnUS, out minx) & !double.TryParse(node.Attributes["miny"].Value, NumberStyles.Any, Map.numberFormat_EnUS, out miny) & !double.TryParse(node.Attributes["maxx"].Value, NumberStyles.Any, Map.numberFormat_EnUS, out maxx) & !double.TryParse(node.Attributes["maxy"].Value, NumberStyles.Any, Map.numberFormat_EnUS, out maxy)) { throw new ArgumentException("Invalid LatLonBoundingBox on layer '" + layer.Name + "'"); } layer.LatLonBoundingBox = GeometryFactory.CreateEnvelope(minx, maxx, miny, maxy); } return layer; } #region WMS Data structures /// /// Structure for holding information about a WMS Layer /// public struct WmsServerLayer { /// /// Layer title /// public string Title; /// /// Unique name of this layer used for requesting layer /// public string Name; /// /// Abstract /// public string Abstract; /// /// Specifies whether this layer is queryable using GetFeatureInfo requests /// public bool Queryable; /// /// Keywords /// public string[] Keywords; /// /// List of styles supported by layer /// public WmsLayerStyle[] Style; /// /// Coordinate Reference Systems supported by layer /// public string[] CRS; /// /// Collection of child layers /// public WmsServerLayer[] ChildLayers; /// /// Latitudal/longitudal extent of this layer /// public IEnvelope LatLonBoundingBox; } /// /// Structure for storing information about a WMS Layer Style /// public struct WmsLayerStyle { /// /// Name /// public string Name; /// /// Title /// public string Title; /// /// Abstract /// public string Abstract; /// /// Legend /// public WmsStyleLegend LegendUrl; /// /// Style Sheet Url /// public WmsOnlineResource StyleSheetUrl; } /// /// Structure for storing WMS Legend information /// public struct WmsStyleLegend { /// /// Online resource for legend style /// public WmsOnlineResource OnlineResource; /// /// Size of legend /// public Size Size; } /// /// Structure for storing info on an Online Resource /// public struct WmsOnlineResource { /// /// Type of online resource (Ex. request method 'Get' or 'Post') /// public string Type; /// /// URI of online resource /// public string OnlineResource; } #endregion #region Properties private Capabilities.WmsServiceDescription _ServiceDescription; /// /// Gets the service description /// public Capabilities.WmsServiceDescription ServiceDescription { get { return _ServiceDescription; } } /// /// Gets the version of the WMS server (ex. "1.3.0") /// public string WmsVersion { get; private set; } /// /// Gets a list of available image mime type formats /// public Collection GetMapOutputFormats { get; private set; } /// /// Gets a list of available exception mime type formats /// public string[] ExceptionFormats { get; private set; } /// /// Gets the available GetMap request methods and Online Resource URI /// public WmsOnlineResource[] GetMapRequests { get; private set; } /// /// Gets the hiarchial layer structure /// public WmsServerLayer Layer { get; private set; } #endregion } }