// 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.Drawing; using System.Drawing.Drawing2D; using System.Reflection; using DelftTools.Utils.Aop; using GeoAPI.Extensions.Feature; using GeoAPI.Geometries; using GisSharpBlog.NetTopologySuite.Geometries; using log4net; using SharpMap.Api; using SharpMap.CoordinateSystems.Transformations; using SharpMap.Editors; using SharpMap.Rendering; using SharpMap.Rendering.Thematics; using SharpMap.Styles; using Point = GisSharpBlog.NetTopologySuite.Geometries.Point; namespace SharpMap.Layers { /// /// Class for vector layer properties /// /// /// Adding a VectorLayer to a map: /// /// //Initialize a new map /// SharpMap.Map myMap = new SharpMap.Map(new System.Drawing.Size(300,600)); /// //Create a layer /// SharpMap.Layers.VectorLayer myLayer = new SharpMap.Layers.VectorLayer("My layer"); /// //Add datasource /// myLayer.DataSource = new SharpMap.Data.Providers.ShapeFile(@"C:\data\MyShapeData.shp"); /// //Set up styles /// myLayer.Style.Outline = new Pen(Color.Magenta, 3f); /// myLayer.Style.EnableOutline = true; /// myMap.Layers.Add(myLayer); /// //Zoom to fit the data in the view /// myMap.ZoomToExtents(); /// //Render the map: /// System.Drawing.Image mapImage = myMap.GetMap(); /// /// [Entity(FireOnCollectionChange = false)] public class VectorLayer : Layer { public static readonly Bitmap DefaultPointSymbol = (Bitmap) Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream("SharpMap.Styles.DefaultSymbol.png")); private static readonly ILog log = LogManager.GetLogger(typeof(VectorLayer)); private bool clippingEnabled; private SmoothingMode smoothingMode; private VectorStyle style; private bool isStyleDirty; /// /// Create vectorlayer with default name. /// public VectorLayer() : this("") {} /// /// Initializes a new layer /// /// Name of layer public VectorLayer(string layername) { SimplifyGeometryDuringRendering = true; SkipRenderingOfVerySmallFeatures = true; name = layername; // smoothingMode = SmoothingMode.AntiAlias; smoothingMode = SmoothingMode.HighSpeed; FeatureEditor = new FeatureEditor(); } /// /// Creates a clone of the vectorlayer given as parameter to the constructor /// /// public VectorLayer(VectorLayer layer) { if (layer != null) { if (layer.Style != null) { style = (VectorStyle) layer.Style.Clone(); isStyleDirty = true; } name = layer.Name; } smoothingMode = SmoothingMode.HighSpeed; FeatureEditor = new FeatureEditor(); SimplifyGeometryDuringRendering = layer.SimplifyGeometryDuringRendering; SkipRenderingOfVerySmallFeatures = layer.SkipRenderingOfVerySmallFeatures; CoordinateTransformation = layer.CoordinateTransformation; } /// /// Initializes a new layer with a specified datasource /// /// Name of layer /// Data source public VectorLayer(string layername, IFeatureProvider dataSource) : this(layername) { DataSource = dataSource; } /// /// Gets or sets the datasource /// public override IFeatureProvider DataSource { get { return base.DataSource; } set { base.DataSource = value; isStyleDirty = true; } } /// /// Specifies whether polygons should be clipped prior to rendering /// /// /// Clipping will clip Polygon and /// /// Enabling clipping might improve rendering speed if you are rendering /// only small portions of very large objects. /// public virtual bool ClippingEnabled { get { return clippingEnabled; } set { clippingEnabled = value; } } /// /// Render whether smoothing (antialiasing) is applied to lines and curves and the edges of filled areas /// public virtual SmoothingMode SmoothingMode { get { return smoothingMode; } set { smoothingMode = value; } } /// /// Gets or sets the rendering style of the vector layer. /// public virtual VectorStyle Style { get { if (style == null) { OnInitializeDefaultStyle(); } return style; } set { style = value; isStyleDirty = true; } } #region ICloneable Members /// /// Clones the layer /// /// cloned object public override object Clone() { var vectorLayer = (VectorLayer) base.Clone(); vectorLayer.Style = (VectorStyle) Style.Clone(); vectorLayer.SmoothingMode = SmoothingMode; vectorLayer.ClippingEnabled = ClippingEnabled; return vectorLayer; } #endregion protected virtual void OnInitializeDefaultStyle() { style = new VectorStyle(); UpdateStyleGeometry(); } protected override void UpdateCurrentTheme() { base.UpdateCurrentTheme(); if (!AutoUpdateThemeOnDataSourceChanged) { return; //we don't have to update } var gradientTheme = theme as GradientTheme; if (gradientTheme != null) { double min, max; if (GetDataMinMax(gradientTheme.AttributeName, out min, out max)) { gradientTheme.ScaleTo(min, max); } } } private bool GetDataMinMax(string attributeName, out double min, out double max) { if (!string.IsNullOrEmpty(ThemeGroup)) { return ((Map) Map).GetDataMinMaxForThemeGroup(ThemeGroup, attributeName, out min, out max); } min = MinDataValue; max = MaxDataValue; return true; } private void UpdateStyleGeometry() { if (DataSource != null && style != null) { if (DataSource.GetFeatureCount() > 0) { IFeature feature = DataSource.GetFeature(0); IGeometry geometry = feature.Geometry; if (geometry != null) { // hack set interface of geometry as VectorStyle.GeometryType if (geometry is Point) { style.GeometryType = typeof(IPoint); } else if (geometry is LineString) { style.GeometryType = typeof(ILineString); } else if (geometry is MultiLineString) { style.GeometryType = typeof(IMultiLineString); } else if (geometry is Polygon) { style.GeometryType = typeof(IPolygon); } else if (geometry is MultiPolygon) { style.GeometryType = typeof(IMultiPolygon); } } } } isStyleDirty = false; } #region ILayer Members /// /// Renders the layer to a graphics object /// /// Graphics object reference /// Map which is rendered public override void OnRender(Graphics g, IMap map) // TODO: remove map as parameter { if (map.Center == null) { throw (new ApplicationException("Cannot render map. View center not specified")); } if (g == null) { return; } g.SmoothingMode = SmoothingMode; //View to render IEnvelope envelope = map.Envelope; if (DataSource == null) { throw (new ApplicationException("DataSource property not set on layer '" + Name + "'")); } try { RenderFeatures(g, envelope, map); } catch (Exception e) { log.WarnFormat("Error during rendering: {0}", e.Message); } } /* // recalculate min/max, strange bug, probably due to float/double conversion if (DataSource.IsPoint) { for (int i = 0; i < _FeatureCount; i++) { var pt = GetBounds(i); envelope.ExpandToInclude(pt.X, pt.Y); } envelope.ExpandBy(envelope.Width * 0.01, envelope.Height * 0.01); _Envelope = envelope; } */ /// /// Performance. When true - geometry is simplified before it is rendered. /// public virtual bool SimplifyGeometryDuringRendering { get; set; } private void RenderFeatures(Graphics g, IEnvelope envelope, IMap map) { bool customRendererUsed = false; bool themeOn = Theme != null; lastRenderedCoordinatesCount = 0; int featureCount = 0; var startRenderingTime = DateTime.Now; VectorRenderingHelper.SimplifyGeometryDuringRendering = SimplifyGeometryDuringRendering; // TODO: pass as argument to make parallel render possible var features = GetFeatures(envelope); foreach (IFeature feature in features) { // TODO: improve performance by first decimating geometry and then transforming) // get geometry IGeometry currentGeometry = CoordinateTransformation != null ? GeometryTransform.TransformGeometry(feature.Geometry, CoordinateTransformation.MathTransform) : feature.Geometry; VectorStyle currentVectorStyle = themeOn ? Theme.GetStyle(feature) as VectorStyle : Style; // TODO: make it render only one time foreach (IFeatureRenderer r in CustomRenderers) { customRendererUsed = r.Render(feature, g, this); } if (!customRendererUsed) { //Linestring outlines is drawn by drawing the layer once with a thicker line //before drawing the "inline" on top. if (Style.EnableOutline) { //Draw background of all line-outlines first if (!themeOn || (currentVectorStyle != null && currentVectorStyle.Enabled && currentVectorStyle.EnableOutline)) { switch (currentGeometry.GeometryType) { case "LineString": VectorRenderingHelper.DrawLineString(g, currentGeometry as ILineString, currentVectorStyle.Outline, map); break; case "MultiLineString": VectorRenderingHelper.DrawMultiLineString(g, currentGeometry as IMultiLineString, currentVectorStyle.Outline, map); break; default: break; } } } VectorRenderingHelper.RenderGeometry(g, map, currentGeometry, currentVectorStyle, DefaultPointSymbol, clippingEnabled); lastRenderedCoordinatesCount += currentGeometry.Coordinates.Length; } featureCount++; } lastRenderedFeaturesCount = featureCount; lastRenderDuration = (DateTime.Now - startRenderingTime).TotalMilliseconds; } private long lastRenderedFeaturesCount; public virtual long LastRenderedFeaturesCount { get { return lastRenderedFeaturesCount; } } private long lastRenderedCoordinatesCount; public virtual long LastRenderedCoordinatesCount { get { return lastRenderedCoordinatesCount; } } private double lastRenderDuration; private IEnumerable times; public override double LastRenderDuration { get { return lastRenderDuration; } } /// /// Used for rendering benchmarking purposes. /// /// /// /// public virtual void SetRenderingTimeParameters(int featureCount, int coordinateCount, double durationInMillis) { lastRenderedFeaturesCount = featureCount; lastRenderedCoordinatesCount = coordinateCount; lastRenderDuration = durationInMillis; } #endregion } }