// Copyright 2005, 2006 - Morten Nielsen (www.iter.dk) // // This file is part of SharpMap. // SharpMap is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // SharpMap is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // You should have received a copy of the GNU Lesser General Public License // along with SharpMap; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Drawing; using System.Drawing.Imaging; using System.Text; using GeoAPI.Geometries; using SharpMap.Api; namespace SharpMap.Layers { /// /// Web Map Service layer /// /// /// The WmsLayer is currently very basic and doesn't support automatic fetching of the WMS Service Description. /// Instead you would have to add the nessesary parameters to the URL, /// and the WmsLayer will set the remaining BoundingBox property and proper requests that changes between the requests. /// See the example below. /// /// /// The following example creates a map with a WMS layer the Demis WMS Server /// /// myMap = new SharpMap.Map(new System.Drawing.Size(500,250); /// string wmsUrl = "http://www2.demis.nl/mapserver/request.asp"; /// SharpMap.Layers.WmsLayer myLayer = new SharpMap.Layers.WmsLayer("Demis WMS", myLayer); /// myLayer.AddLayer("Bathymetry"); /// myLayer.AddLayer("Countries"); /// myLayer.AddLayer("Topography"); /// myLayer.AddLayer("Hillshading"); /// myLayer.SetImageFormat(layWms.OutputFormats[0]); /// myLayer.SpatialReferenceSystem = "EPSG:4326"; /// myMap.Layers.Add(myLayer); /// myMap.Center = new SharpMap.Geometries.Point(0, 0); /// myMap.Zoom = 360; /// myMap.MaximumZoom = 360; /// myMap.MinimumZoom = 0.1; /// /// public class WmsLayer : SharpMap.Layers.Layer { private SharpMap.Web.Wms.Client wmsClient; private string mimeType = ""; /// /// Initializes a new layer, and downloads and parses the service description /// /// In and ASP.NET application the service description is automatically cached for 24 hours when not specified public WmsLayer() : this("", "", new TimeSpan(24, 0, 0)) { } /// /// Initializes a new layer, and downloads and parses the service description /// /// In and ASP.NET application the service description is automatically cached for 24 hours when not specified /// Layername /// Url of WMS server public WmsLayer(string layername, string url) : this(layername, url, new TimeSpan(24, 0, 0)) { } /// /// Initializes a new layer, and downloads and parses the service description /// /// Layername /// Url of WMS server /// Time for caching Service Description (ASP.NET only) public WmsLayer(string layername, string url, TimeSpan cachetime) : this(layername, url, cachetime, null) { } /// /// Initializes a new layer, and downloads and parses the service description /// /// In and ASP.NET application the service description is automatically cached for 24 hours when not specified /// Layername /// Url of WMS server /// Proxy public WmsLayer(string layername, string url, System.Net.WebProxy proxy) : this(layername, url, new TimeSpan(24,0,0), proxy) { } /// /// Initializes a new layer, and downloads and parses the service description /// /// Layername /// Url of WMS server /// Time for caching Service Description (ASP.NET only) /// Proxy public WmsLayer(string layername, string url, TimeSpan cachetime, System.Net.WebProxy proxy) { this.proxy = proxy; timeOut = 10000; this.Name = layername; this.Cachetime = cachetime; this.Url = url; // Also does the initialization if an non-empty url was given } private void Initialize() { continueOnError = true; if (System.Web.HttpContext.Current != null && System.Web.HttpContext.Current.Cache["SharpMap_WmsClient_" + url] != null) { wmsClient = (SharpMap.Web.Wms.Client)System.Web.HttpContext.Current.Cache["SharpMap_WmsClient_" + url]; } else { wmsClient = new SharpMap.Web.Wms.Client(url, proxy); if (System.Web.HttpContext.Current != null) System.Web.HttpContext.Current.Cache.Insert("SharpMap_WmsClient_" + url, wmsClient, null, System.Web.Caching.Cache.NoAbsoluteExpiration, cachetime); } //Set default mimetype - We prefer compressed formats if (OutputFormats.Contains("image/jpeg")) mimeType = "image/jpeg"; else if (OutputFormats.Contains("image/png")) mimeType = "image/png"; else if (OutputFormats.Contains("image/gif")) mimeType = "image/gif"; else //None of the default formats supported - Look for the first supported output format { bool formatSupported = false; foreach (System.Drawing.Imaging.ImageCodecInfo encoder in System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders()) if (OutputFormats.Contains(encoder.MimeType.ToLower())) { formatSupported = true; mimeType = encoder.MimeType; break; } if (!formatSupported) throw new ArgumentException("GDI+ doesn't not support any of the mimetypes supported by this WMS service"); } layerList = new Collection(); stylesList = new Collection(); } private TimeSpan cachetime; public virtual TimeSpan Cachetime { get { return cachetime; } set { cachetime = value; } } private string url; public virtual string Url { get { return url; } set { url = value; if (url != string.Empty) { Initialize(); } } } public virtual string MimeType { get { return mimeType; } set { mimeType = value; } } private IList layerList; /// /// Gets the list of enabled layers /// public virtual IList LayerList { get { return layerList; } set { layerList = value; } } /// /// Adds a layer to WMS request /// /// Layer names are case sensitive. /// Name of layer /// Throws an exception is an unknown layer is added public virtual void AddLayer(string name) { if(!LayerExists(wmsClient.Layer,name)) throw new ArgumentException("Cannot add WMS Layer - Unknown layername"); layerList.Add(name); } /// /// Recursive method for checking whether a layername exists /// /// /// /// private bool LayerExists(SharpMap.Web.Wms.Client.WmsServerLayer layer, string name) { if(name == layer.Name) return true; foreach (SharpMap.Web.Wms.Client.WmsServerLayer childlayer in layer.ChildLayers) if (LayerExists(childlayer,name)) return true; return false; } /// /// Removes a layer from the layer list /// /// Name of layer to remove public virtual void RemoveLayer(string name) { layerList.Remove(name); } /// /// Removes the layer at the specified index /// /// public virtual void RemoveLayerAt(int index) { layerList.RemoveAt(index); } /// /// Removes all layers /// public virtual void RemoveAllLayers() { layerList.Clear(); } private Collection stylesList; /// /// Gets the list of enabled styles /// public virtual Collection StylesList { get { return stylesList; } } /// /// Adds a style to the style collection /// /// Name of style /// Throws an exception is an unknown layer is added public virtual void AddStyle(string name) { if (!StyleExists(wmsClient.Layer, name)) throw new ArgumentException("Cannot add WMS Layer - Unknown layername"); stylesList.Add(name); } /// /// Recursive method for checking whether a layername exists /// /// layer /// name of style /// True of style exists private bool StyleExists(SharpMap.Web.Wms.Client.WmsServerLayer layer, string name) { foreach(SharpMap.Web.Wms.Client.WmsLayerStyle style in layer.Style) if (name == style.Name) return true; foreach (SharpMap.Web.Wms.Client.WmsServerLayer childlayer in layer.ChildLayers) if (StyleExists(childlayer, name)) return true; return false; } /// /// Removes a style from the collection /// /// Name of style public virtual void RemoveStyle(string name) { stylesList.Remove(name); } /// /// Removes a style at specified index /// /// Index public virtual void RemoveStyleAt(int index) { stylesList.RemoveAt(index); } /// /// Removes all styles from the list /// public virtual void RemoveAllStyles() { stylesList.Clear(); } /// /// Sets the image type to use when requesting images from the WMS server /// /// /// See the property for a list of available mime types supported by the WMS server /// /// Throws an exception if either the mime type isn't offered by the WMS /// or GDI+ doesn't support this mime type. /// Mime type of image format public virtual void SetImageFormat(string mimeType) { if (!OutputFormats.Contains(mimeType)) throw new ArgumentException("WMS service doesn't not offer mimetype '" + mimeType + "'"); //Check whether SharpMap supports the specified mimetype bool formatSupported = false; foreach (System.Drawing.Imaging.ImageCodecInfo encoder in System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders()) if (encoder.MimeType.ToLower() == mimeType.ToLower()) { formatSupported = true; break; } if (!formatSupported) throw new ArgumentException("GDI+ doesn't not support mimetype '" + mimeType + "'"); this.mimeType = mimeType; } /// /// Gets the hiarchial list of available WMS layers from this service /// public virtual SharpMap.Web.Wms.Client.WmsServerLayer RootLayer { get { return wmsClient.Layer; } } /// /// Gets the list of available formats /// public virtual Collection OutputFormats { get { return wmsClient.GetMapOutputFormats; } } private string spatialReferenceSystem; /// /// Gets or sets the spatial reference used for the WMS server request /// public virtual string SpatialReferenceSystem { get { return spatialReferenceSystem; } set { spatialReferenceSystem = value; } } /// /// Gets the service description from this server /// public virtual SharpMap.Web.Wms.Capabilities.WmsServiceDescription ServiceDescription { get { return wmsClient.ServiceDescription; } } /// /// Gets the WMS Server version of this service /// public virtual string Version { get { return wmsClient.WmsVersion; } } private ImageAttributes imageAttributes; /// /// When specified, applies image attributes at image (fx. make WMS layer semi-transparent) /// /// /// You can make the WMS layer semi-transparent by settings a up a ColorMatrix, /// or scale/translate the colors in any other way you like. /// /// Setting the WMS layer to be semi-transparent. /// /// float[][] colorMatrixElements = { /// new float[] {1, 0, 0, 0, 0}, /// new float[] {0, 1, 0, 0, 0}, /// new float[] {0, 0, 1, 0, 0}, /// new float[] {0, 0, 0, 0.5, 0}, /// new float[] {0, 0, 0, 0, 1}}; /// ColorMatrix colorMatrix = new ColorMatrix(colorMatrixElements); /// ImageAttributes imageAttributes = new ImageAttributes(); /// imageAttributes.SetColorMatrix( /// colorMatrix, /// ColorMatrixFlag.Default, /// ColorAdjustType.Bitmap); /// myWmsLayer.ImageAttributes = imageAttributes; /// /// /// public virtual ImageAttributes ImageAttributes { get { return imageAttributes; } set { imageAttributes = value; } } #region ILayer Members /// /// Renders the layer /// /// Graphics object reference /// Map which is rendered public override void OnRender(System.Drawing.Graphics g, IMap map) { SharpMap.Web.Wms.Client.WmsOnlineResource resource = GetPreferredMethod(); Uri myUri = new Uri(GetRequestUrl(map.Envelope, map.Size)); System.Net.WebRequest myWebRequest = System.Net.WebRequest.Create(myUri); myWebRequest.Method = resource.Type; myWebRequest.Timeout = timeOut; if (credentials != null) myWebRequest.Credentials = credentials; else myWebRequest.Credentials = System.Net.CredentialCache.DefaultCredentials; if (proxy != null) myWebRequest.Proxy = proxy; try { System.Net.HttpWebResponse myWebResponse = (System.Net.HttpWebResponse)myWebRequest.GetResponse(); System.IO.Stream dataStream = myWebResponse.GetResponseStream(); if (myWebResponse.ContentType.StartsWith("image")) { System.Drawing.Image img = System.Drawing.Image.FromStream(myWebResponse.GetResponseStream()); if (imageAttributes != null) g.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, this.ImageAttributes); else g.DrawImageUnscaled(img, 0, 0,map.Size.Width,map.Size.Height); } dataStream.Close(); myWebResponse.Close(); } catch (System.Net.WebException webEx) { if (!continueOnError) throw (new SharpMap.Rendering.Exceptions.RenderException("There was a problem connecting to the WMS server when rendering layer '" + this.Name + "'", webEx)); else //Write out a trace warning instead of throwing an error to help debugging WMS problems System.Diagnostics.Trace.Write("There was a problem connecting to the WMS server when rendering layer '" + this.Name + "': " + webEx.Message); } catch (System.Exception ex) { if (!continueOnError) throw (new SharpMap.Rendering.Exceptions.RenderException("There was a problem rendering layer '" + this.Name + "'", ex)); else //Write out a trace warning instead of throwing an error to help debugging WMS problems System.Diagnostics.Trace.Write("There was a problem connecting to the WMS server when rendering layer '" + this.Name + "': " + ex.Message); } } /// /// Gets the URL for a map request base on current settings, the image size and boundingbox /// /// Area the WMS request should cover /// Size of image /// URL for WMS request public virtual string GetRequestUrl(IEnvelope box, System.Drawing.Size size) { SharpMap.Web.Wms.Client.WmsOnlineResource resource = GetPreferredMethod(); System.Text.StringBuilder strReq = new StringBuilder(resource.OnlineResource); if(!resource.OnlineResource.Contains("?")) strReq.Append("?"); if (!strReq.ToString().EndsWith("&") && !strReq.ToString().EndsWith("?")) strReq.Append("&"); strReq.AppendFormat(SharpMap.Map.numberFormat_EnUS, "REQUEST=GetMap&BBOX={0},{1},{2},{3}", box.MinX, box.MinY, box.MaxX, box.MaxY); strReq.AppendFormat("&WIDTH={0}&Height={1}", size.Width, size.Height); strReq.Append("&Layers="); if (layerList != null && layerList.Count > 0) { foreach (string layer in layerList) strReq.AppendFormat("{0},", layer); strReq.Remove(strReq.Length - 1, 1); } strReq.AppendFormat("&FORMAT={0}", mimeType); if (spatialReferenceSystem == string.Empty) throw new ApplicationException("Spatial reference system not set"); if(wmsClient.WmsVersion=="1.3.0") strReq.AppendFormat("&CRS={0}", spatialReferenceSystem); else strReq.AppendFormat("&SRS={0}", spatialReferenceSystem); strReq.AppendFormat("&VERSION={0}", wmsClient.WmsVersion); strReq.Append("&Styles="); if (stylesList != null && stylesList.Count > 0) { foreach (string style in stylesList) strReq.AppendFormat("{0},", style); strReq.Remove(strReq.Length - 1, 1); } return strReq.ToString(); } /// /// Returns the extent of the layer /// /// Bounding box corresponding to the extent of the features in the layer public override IEnvelope Envelope { get { return RootLayer.LatLonBoundingBox; } } private Boolean continueOnError; /// /// Specifies whether to throw an exception if the Wms request failed, or to just skip rendering the layer /// public virtual Boolean ContinueOnError { get { return continueOnError; } set { continueOnError = value; } } /// /// Returns the type of the layer /// //public override SharpMap.Layers.Layertype LayerType //{ // get { return SharpMap.Layers.Layertype.Wms; } //} #endregion private SharpMap.Web.Wms.Client.WmsOnlineResource GetPreferredMethod() { //We prefer posting. Seek for supported post method for (int i = 0; i < wmsClient.GetMapRequests.Length; i++) if (wmsClient.GetMapRequests[i].Type.ToLower() == "post") { return wmsClient.GetMapRequests[i]; } //Next we prefer the 'get' method for (int i = 0; i < wmsClient.GetMapRequests.Length; i++) { if (wmsClient.GetMapRequests[i].Type.ToLower() == "get") { return wmsClient.GetMapRequests[i]; } } return wmsClient.GetMapRequests[0]; } private System.Net.ICredentials credentials; /// /// Provides the base authentication interface for retrieving credentials for Web client authentication. /// public virtual System.Net.ICredentials Credentials { get { return credentials; } set { credentials = value; } } private System.Net.WebProxy proxy; /// /// Gets or sets the proxy used for requesting a webresource /// public virtual System.Net.WebProxy Proxy { get { return proxy; } set { proxy = value; } } private int timeOut; /// /// Timeout of webrequest in milliseconds. Defaults to 10 seconds /// public virtual int TimeOut { get { return timeOut; } set { timeOut = value; } } #region ICloneable Members /// /// Clones the object /// /// public override object Clone() { return new WmsLayer(Name, Url); } #endregion public virtual string LayerTitle { get { return RootLayer.Name; } } } }