// 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.Drawing.Text; using System.Linq; using Core.Common.Utils.Aop; using Core.Common.Utils.Aop.Markers; using Core.GIS.GeoAPI.CoordinateSystems.Transformations; using Core.GIS.GeoAPI.Extensions.Feature; using Core.GIS.GeoAPI.Geometries; using Core.GIS.NetTopologySuite.Extensions.Features; using Core.GIS.SharpMap.Api; using Core.GIS.SharpMap.Api.Delegates; using Core.GIS.SharpMap.Api.Enums; using Core.GIS.SharpMap.Api.Layers; using Core.GIS.SharpMap.Converters.Geometries; using Core.GIS.SharpMap.CoordinateSystems.Transformations; using Core.GIS.SharpMap.Rendering; using Core.GIS.SharpMap.Styles; namespace Core.GIS.SharpMap.Layers { /// /// Label layer class /// TODO: Auto-configure LabelColumn etc to some default on DataSource change (?) /// TODO: Change Map Rendering to check LabelLayer RenderRequired (more efficient: currently parent layer must be redrawn) /// TODO: Remove classes that override this class only to get access to GetText: there's a delegate for that. /// /// /// Creates a new label layer and sets the label text to the "Name" column in the FeatureDataTable of the datasource /// /// //Set up a label layer /// SharpMap.Layers.LabelLayer layLabel = new SharpMap.Layers.LabelLayer("Country labels"); /// layLabel.DataSource = layCountries.DataSource; /// layLabel.Enabled = true; /// layLabel.LabelColumn = "Name"; /// layLabel.Style = new SharpMap.Styles.LabelStyle(); /// layLabel.Style.CollisionDetection = true; /// layLabel.Style.CollisionBuffer = new SizeF(20, 20); /// layLabel.Style.ForeColor = Color.White; /// layLabel.Style.Font = new Font(FontFamily.GenericSerif, 8); /// layLabel.MaxVisible = 90; /// layLabel.Style.HorizontalAlignment = SharpMap.Styles.LabelStyle.HorizontalAlignmentEnum.Center; /// /// [Entity(FireOnCollectionChange = false)] public class LabelLayer : Layer, ILabelLayer { private MultipartGeometryBehaviourEnum multipartGeometryBehaviour; private LabelCollisionDetection.LabelFilterMethod labelFilter; private ILabelStyle style; private GetLabelMethod getLabelMethod; public LabelLayer() : this("") {} /// /// Creates a new instance of a LabelLayer /// public LabelLayer(string layername) { style = new LabelStyle(); Name = layername; SmoothingMode = SmoothingMode.AntiAlias; TextRenderingHint = TextRenderingHint.AntiAlias; multipartGeometryBehaviour = MultipartGeometryBehaviourEnum.All; ShowInTreeView = false; ShowInLegend = false; Visible = false; LabelFilter = LabelCollisionDetection.ThoroughCollisionDetection; Style = new LabelStyle { Font = new Font("Arial", 12), Halo = new Pen(Brushes.White, 1f), CollisionDetection = true }; } /// /// Filtermethod delegate for performing filtering /// /// /// Default method is /// public virtual LabelCollisionDetection.LabelFilterMethod LabelFilter { get { return labelFilter; } set { labelFilter = value; } } /// /// Gets or sets labelling behavior on multipart geometries /// /// Default value is public virtual MultipartGeometryBehaviourEnum MultipartGeometryBehaviour { get { return multipartGeometryBehaviour; } set { multipartGeometryBehaviour = value; } } /// /// Render whether smoothing (antialiasing) is applied to lines and curves and the edges of filled areas /// public virtual SmoothingMode SmoothingMode { get; set; } /// /// Specifies the quality of text rendering /// public virtual TextRenderingHint TextRenderingHint { get; set; } /// /// Gets or sets the rendering style of the label layer. /// public virtual ILabelStyle Style { get { return style; } set { style = value; } } /// /// Gets or sets thematic settings for the layer. Set to null to ignore thematics /// public virtual ITheme Theme { get; set; } /// /// Data column or expression where label text is extracted from. /// /// /// This property is overriden by the . /// public virtual string LabelColumn { get; set; } /// /// Gets or sets the method for creating a custom label string based on a feature. /// /// /// If this method is not null, it will override the value. /// The label delegate must take a and return a string. /// /// Creating a label-text by combining attributes "ROADNAME" and "STATE" into one string, using /// an anonymous delegate: /// /// myLabelLayer.LabelStringDelegate = delegate(SharpMap.Data.FeatureDataRow fdr) /// { return fdr["ROADNAME"].ToString() + ", " + fdr["STATE"].ToString(); }; /// /// /// public virtual GetLabelMethod LabelStringDelegate { get { return getLabelMethod; } set { getLabelMethod = value; } } /// /// Data column from where the label rotation is derived. /// If this is empty, rotation will be zero, or aligned to a linestring. /// Rotation are in degrees (positive = clockwise). /// public virtual string RotationColumn { get; set; } /// /// A value indication the priority of the label in cases of label-collision detection /// public virtual int Priority { get; set; } [NoNotifyPropertyChange] public override IFeatureProvider DataSource { get { return base.DataSource ?? ParentDataSource; } //either custom datasource (different from parent, otherwise parent datasource) set { base.DataSource = value; } } [NoNotifyPropertyChange] public override ICoordinateTransformation CoordinateTransformation { get { return base.DataSource == null ? ParentCoordinateTransformation : base.CoordinateTransformation; } set { base.CoordinateTransformation = value; } } /// /// Gets the boundingbox of the entire layer /// public override IEnvelope Envelope { get { if (DataSource == null) { return null; } if (CoordinateTransformation != null) { throw new NotImplementedException(); } return DataSource.GetExtents(); } } [NoNotifyPropertyChange] public virtual ILayer Parent { get; set; } /// /// Renders the layer /// /// Graphics object reference /// Map which is rendered public override void OnRender(Graphics g, IMap map) { if (Style.Enabled && Style.MaxVisible >= map.Zoom && Style.MinVisible < map.Zoom) { if (DataSource == null) { throw (new ApplicationException("DataSource property not set on layer '" + Name + "'")); } g.TextRenderingHint = TextRenderingHint; g.SmoothingMode = SmoothingMode; var features = Parent.GetFeatures(map.Envelope); if (!features.Any()) { return; } //Initialize label collection var labels = new List