// 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
}
}