LabelLayer.cs
上传用户:sex100000
上传日期:2013-11-09
资源大小:1377k
文件大小:17k
源码类别:

GIS编程

开发平台:

C#

  1. // Copyright 2005, 2006 - Morten Nielsen (www.iter.dk)
  2. //
  3. // This file is part of SharpMap.
  4. // SharpMap is free software; you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation; either version 2 of the License, or
  7. // (at your option) any later version.
  8. // 
  9. // SharpMap is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. // GNU Lesser General Public License for more details.
  13. // You should have received a copy of the GNU Lesser General Public License
  14. // along with SharpMap; if not, write to the Free Software
  15. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  16. using System;
  17. using System.Collections.Generic;
  18. using System.Text;
  19. namespace SharpMap.Layers
  20. {
  21. /// <summary>
  22. /// Label layer class
  23. /// </summary>
  24. /// <example>
  25. /// Creates a new label layer and sets the label text to the "Name" column in the FeatureDataTable of the datasource
  26. /// <code lang="C#">
  27. /// //Set up a label layer
  28. /// SharpMap.Layers.LabelLayer layLabel = new SharpMap.Layers.LabelLayer("Country labels");
  29. /// layLabel.DataSource = layCountries.DataSource;
  30. /// layLabel.Enabled = true;
  31. /// layLabel.LabelColumn = "Name";
  32. /// layLabel.Style = new SharpMap.Styles.LabelStyle();
  33. /// layLabel.Style.CollisionDetection = true;
  34. /// layLabel.Style.CollisionBuffer = new SizeF(20, 20);
  35. /// layLabel.Style.ForeColor = Color.White;
  36. /// layLabel.Style.Font = new Font(FontFamily.GenericSerif, 8);
  37. /// layLabel.MaxVisible = 90;
  38. /// layLabel.Style.HorizontalAlignment = SharpMap.Styles.LabelStyle.HorizontalAlignmentEnum.Center;
  39. /// </code>
  40. /// </example>
  41. public class LabelLayer : Layer, IDisposable
  42. {
  43. /// <summary>
  44. /// Labelling behaviour for Multipart geometry collections
  45. /// </summary>
  46. public enum MultipartGeometryBehaviourEnum
  47. {
  48. /// <summary>
  49. /// Place label on all parts (default)
  50. /// </summary>
  51. All,
  52. /// <summary>
  53. /// Place label on object which the greatest length or area.
  54. /// </summary>
  55. /// <remarks>
  56. /// Multipoint geometries will default to <see cref="First"/>
  57. /// </remarks>
  58. Largest,
  59. /// <summary>
  60. /// The center of the combined geometries
  61. /// </summary>
  62. CommonCenter,
  63. /// <summary>
  64. /// Center of the first geometry in the collection (fastest method)
  65. /// </summary>
  66. First
  67. }
  68. /// <summary>
  69. /// Delegate method for creating advanced label texts
  70. /// </summary>
  71. /// <param name="fdr"></param>
  72. /// <returns></returns>
  73. public delegate string GetLabelMethod(SharpMap.Data.FeatureDataRow fdr);
  74. /// <summary>
  75. /// Creates a new instance of a LabelLayer
  76. /// </summary>
  77. public LabelLayer(string layername)
  78. {
  79. _Style = new SharpMap.Styles.LabelStyle();
  80. this.LayerName = layername;
  81. this.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
  82. this.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
  83. _MultipartGeometryBehaviour = MultipartGeometryBehaviourEnum.All;
  84. _LabelFilter = SharpMap.Rendering.LabelCollisionDetection.SimpleCollisionDetection;
  85. }
  86. private MultipartGeometryBehaviourEnum _MultipartGeometryBehaviour;
  87. /// <summary>
  88. /// Gets or sets labelling behavior on multipart geometries
  89. /// </summary>
  90. /// <remarks>Default value is <see cref="MultipartGeometryBehaviourEnum.All"/></remarks>
  91. public MultipartGeometryBehaviourEnum MultipartGeometryBehaviour
  92. {
  93. get { return _MultipartGeometryBehaviour; }
  94. set { _MultipartGeometryBehaviour = value; }
  95. }
  96. private SharpMap.Rendering.LabelCollisionDetection.LabelFilterMethod _LabelFilter;
  97. /// <summary>
  98. /// Filtermethod delegate for performing filtering
  99. /// </summary>
  100. /// <remarks>
  101. /// Default method is <see cref="SharpMap.Rendering.LabelCollisionDetection.SimpleCollisionDetection"/>
  102. /// </remarks>
  103. public SharpMap.Rendering.LabelCollisionDetection.LabelFilterMethod LabelFilter
  104. {
  105. get { return _LabelFilter; }
  106. set { _LabelFilter = value; }
  107. }
  108. private System.Drawing.Drawing2D.SmoothingMode _SmoothingMode;
  109. /// <summary>
  110. /// Render whether smoothing (antialiasing) is applied to lines and curves and the edges of filled areas
  111. /// </summary>
  112. public System.Drawing.Drawing2D.SmoothingMode SmoothingMode
  113. {
  114. get { return _SmoothingMode; }
  115. set { _SmoothingMode = value; }
  116. }
  117. private System.Drawing.Text.TextRenderingHint _TextRenderingHint;
  118. /// <summary>
  119. /// Specifies the quality of text rendering
  120. /// </summary>
  121. public System.Drawing.Text.TextRenderingHint TextRenderingHint
  122. {
  123. get { return _TextRenderingHint; }
  124. set { _TextRenderingHint = value; }
  125. }
  126. private SharpMap.Data.Providers.IProvider _DataSource;
  127. /// <summary>
  128. /// Gets or sets the datasource
  129. /// </summary>
  130. public SharpMap.Data.Providers.IProvider DataSource
  131. {
  132. get { return _DataSource; }
  133. set { _DataSource = value; }
  134. }
  135. private SharpMap.Styles.LabelStyle _Style;
  136. /// <summary>
  137. /// Gets or sets the rendering style of the label layer.
  138. /// </summary>
  139. public SharpMap.Styles.LabelStyle Style
  140. {
  141. get { return _Style; }
  142. set { _Style = value; }
  143. }
  144. private SharpMap.Rendering.Thematics.ITheme _theme;
  145. /// <summary>
  146. /// Gets or sets thematic settings for the layer. Set to null to ignore thematics
  147. /// </summary>
  148. public SharpMap.Rendering.Thematics.ITheme Theme
  149. {
  150. get { return _theme; }
  151. set { _theme = value; }
  152. }
  153. private string _LabelColumn;
  154. /// <summary>
  155. /// Data column or expression where label text is extracted from.
  156. /// </summary>
  157. /// <remarks>
  158. /// This property is overriden by the <see cref="LabelStringDelegate"/>.
  159. /// </remarks>
  160. public string LabelColumn
  161. {
  162. get { return _LabelColumn; }
  163. set { _LabelColumn = value; }
  164. }
  165. private GetLabelMethod _getLabelMethod;
  166. /// <summary>
  167. /// Gets or sets the method for creating a custom label string based on a feature.
  168. /// </summary>
  169. /// <remarks>
  170. /// <para>If this method is not null, it will override the <see cref="LabelColumn"/> value.</para>
  171. /// <para>The label delegate must take a <see cref="SharpMap.Data.FeatureDataRow"/> and return a string.</para>
  172. /// <example>
  173. /// Creating a label-text by combining attributes "ROADNAME" and "STATE" into one string, using
  174. /// an anonymous delegate:
  175. /// <code lang="C#">
  176. /// myLabelLayer.LabelStringDelegate = delegate(SharpMap.Data.FeatureDataRow fdr)
  177. /// { return fdr["ROADNAME"].ToString() + ", " + fdr["STATE"].ToString(); };
  178. /// </code>
  179. /// </example>
  180. /// </remarks>
  181. public GetLabelMethod LabelStringDelegate
  182. {
  183. get { return _getLabelMethod; }
  184. set { _getLabelMethod = value; }
  185. }
  186. private string _RotationColumn;
  187. /// <summary>
  188. /// Data column from where the label rotation is derived.
  189. /// If this is empty, rotation will be zero, or aligned to a linestring.
  190. /// Rotation are in degrees (positive = clockwise).
  191. /// </summary>
  192. public string RotationColumn
  193. {
  194. get { return _RotationColumn; }
  195. set { _RotationColumn = value; }
  196. }
  197. private int _Priority;
  198. /// <summary>
  199. /// A value indication the priority of the label in cases of label-collision detection
  200. /// </summary>
  201. public int Priority
  202. {
  203. get { return _Priority; }
  204. set { _Priority = value; }
  205. }
  206. /// <summary>
  207. /// Renders the layer
  208. /// </summary>
  209. /// <param name="g">Graphics object reference</param>
  210. /// <param name="map">Map which is rendered</param>
  211. public override void Render(System.Drawing.Graphics g, Map map)
  212. {
  213. if (this.Style.Enabled && this.Style.MaxVisible >= map.Zoom && this.Style.MinVisible < map.Zoom)
  214. {
  215. if (this.DataSource == null)
  216. throw (new ApplicationException("DataSource property not set on layer '" + this.LayerName + "'"));
  217. g.TextRenderingHint = this.TextRenderingHint;
  218. g.SmoothingMode = this.SmoothingMode;
  219. SharpMap.Geometries.BoundingBox envelope = map.Envelope; //View to render
  220. if (this.CoordinateTransformation != null)
  221. envelope = SharpMap.CoordinateSystems.Transformations.GeometryTransform.TransformBox(envelope, this.CoordinateTransformation.MathTransform.Inverse());
  222. SharpMap.Data.FeatureDataSet ds = new SharpMap.Data.FeatureDataSet();
  223. this.DataSource.Open();
  224. this.DataSource.ExecuteIntersectionQuery(envelope, ds);
  225. this.DataSource.Close();
  226. if (ds.Tables.Count == 0)
  227. {
  228. base.Render(g,map);
  229. return;
  230. }
  231. SharpMap.Data.FeatureDataTable features = (SharpMap.Data.FeatureDataTable)ds.Tables[0];
  232. //Initialize label collection
  233. List<Rendering.Label> labels = new List<SharpMap.Rendering.Label>();
  234. //List<System.Drawing.Rectangle> LabelBoxes; //Used for collision detection
  235. //Render labels
  236. for (int i = 0; i < features.Count; i++)
  237. {
  238. SharpMap.Data.FeatureDataRow feature = features[i];
  239. if (this.CoordinateTransformation != null)
  240. features[i].Geometry = SharpMap.CoordinateSystems.Transformations.GeometryTransform.TransformGeometry(features[i].Geometry, this.CoordinateTransformation.MathTransform);
  241. SharpMap.Styles.LabelStyle style = null;
  242. if (this.Theme != null) //If thematics is enabled, lets override the style
  243.  style = this.Theme.GetStyle(feature) as SharpMap.Styles.LabelStyle;
  244. else
  245. style = this.Style;
  246. float rotation = 0;
  247. if (!String.IsNullOrEmpty(this.RotationColumn))
  248. float.TryParse(feature[this.RotationColumn].ToString(), System.Globalization.NumberStyles.Any,SharpMap.Map.numberFormat_EnUS, out rotation);
  249. string text;
  250. if (_getLabelMethod != null)
  251. text = _getLabelMethod(feature);
  252. else
  253. text = feature[this.LabelColumn].ToString();
  254. if (text != null && text != String.Empty)
  255. {
  256. if (feature.Geometry is SharpMap.Geometries.GeometryCollection)
  257. {
  258. if (this.MultipartGeometryBehaviour == MultipartGeometryBehaviourEnum.All)
  259. {
  260. foreach (SharpMap.Geometries.Geometry geom in (feature.Geometry as Geometries.GeometryCollection))
  261. {
  262. SharpMap.Rendering.Label lbl = CreateLabel(geom, text, rotation, style, map, g);
  263. if (lbl != null)
  264. labels.Add(lbl);
  265. }
  266. }
  267. else if (this.MultipartGeometryBehaviour == MultipartGeometryBehaviourEnum.CommonCenter)
  268. {
  269. SharpMap.Rendering.Label lbl = CreateLabel(feature.Geometry, text, rotation, style, map, g);
  270. if (lbl != null)
  271. labels.Add(lbl);
  272. }
  273. else if (this.MultipartGeometryBehaviour == MultipartGeometryBehaviourEnum.First)
  274. {
  275. if ((feature.Geometry as Geometries.GeometryCollection).Collection.Count > 0)
  276. {
  277. SharpMap.Rendering.Label lbl = CreateLabel((feature.Geometry as Geometries.GeometryCollection).Collection[0], text, rotation, style, map, g);
  278. if (lbl != null)
  279. labels.Add(lbl);
  280. }
  281. }
  282. else if (this.MultipartGeometryBehaviour == MultipartGeometryBehaviourEnum.Largest)
  283. {
  284. Geometries.GeometryCollection coll = (feature.Geometry as Geometries.GeometryCollection);
  285. if (coll.NumGeometries > 0)
  286. {
  287. double largestVal = 0;
  288. int idxOfLargest = 0;
  289. for (int j = 0; j < coll.NumGeometries; j++)
  290. {
  291. SharpMap.Geometries.Geometry geom = coll.Geometry(j);
  292. if (geom is Geometries.LineString && ((Geometries.LineString)geom).Length > largestVal)
  293. {
  294. largestVal = ((Geometries.LineString)geom).Length;
  295. idxOfLargest = j;
  296. }
  297. if (geom is Geometries.MultiLineString && ((Geometries.MultiLineString)geom).Length > largestVal)
  298. {
  299. largestVal = ((Geometries.LineString)geom).Length;
  300. idxOfLargest = j;
  301. }
  302. if (geom is Geometries.Polygon && ((Geometries.Polygon)geom).Area > largestVal)
  303. {
  304. largestVal = ((Geometries.Polygon)geom).Area;
  305. idxOfLargest = j;
  306. }
  307. if (geom is Geometries.MultiPolygon && ((Geometries.MultiPolygon)geom).Area > largestVal)
  308. {
  309. largestVal = ((Geometries.MultiPolygon)geom).Area;
  310. idxOfLargest = j;
  311. }
  312. }
  313. SharpMap.Rendering.Label lbl = CreateLabel(coll.Geometry(idxOfLargest), text, rotation, style, map, g);
  314. if (lbl != null)
  315. labels.Add(lbl);
  316. }
  317. }
  318. }
  319. else
  320. {
  321. SharpMap.Rendering.Label lbl = CreateLabel(feature.Geometry, text, rotation, style, map, g);
  322. if (lbl != null)
  323. labels.Add(lbl);
  324. }
  325. }
  326. }
  327. if (labels.Count > 0) //We have labels to render...
  328. {
  329. if (this.Style.CollisionDetection && this._LabelFilter!=null)
  330. this._LabelFilter(labels);
  331. for (int i = 0; i < labels.Count;i++ )
  332. SharpMap.Rendering.VectorRenderer.DrawLabel(g, labels[i].LabelPoint, labels[i].Style.Offset, labels[i].Style.Font, labels[i].Style.ForeColor, labels[i].Style.BackColor, Style.Halo, labels[i].Rotation, labels[i].Text, map);
  333. }
  334. labels = null;
  335. }
  336. base.Render(g, map);
  337. }
  338. private SharpMap.Rendering.Label CreateLabel(SharpMap.Geometries.Geometry feature,string text, float rotation, SharpMap.Styles.LabelStyle style, Map map, System.Drawing.Graphics g)
  339. {
  340. System.Drawing.SizeF size = g.MeasureString(text, style.Font);
  341. System.Drawing.PointF position = map.WorldToImage(feature.GetBoundingBox().GetCentroid());
  342. position.X = position.X - size.Width * (short)style.HorizontalAlignment * 0.5f;
  343. position.Y = position.Y - size.Height * (short)style.VerticalAlignment * 0.5f;
  344. if (position.X-size.Width > map.Size.Width || position.X+size.Width < 0 ||
  345. position.Y-size.Height > map.Size.Height || position.Y+size.Height < 0)
  346. return null;
  347. else
  348. {
  349. SharpMap.Rendering.Label lbl;
  350. if (!style.CollisionDetection)
  351. lbl = new SharpMap.Rendering.Label(text, position, rotation, this.Priority, null, style);
  352. else
  353. {
  354. //Collision detection is enabled so we need to measure the size of the string
  355. lbl = new SharpMap.Rendering.Label(text, position, rotation, this.Priority,
  356. new SharpMap.Rendering.LabelBox(position.X - size.Width * 0.5f - style.CollisionBuffer.Width, position.Y + size.Height * 0.5f + style.CollisionBuffer.Height,
  357. size.Width + 2f * style.CollisionBuffer.Width, size.Height + style.CollisionBuffer.Height * 2f), style);
  358. }
  359. if (feature.GetType() == typeof(SharpMap.Geometries.LineString))
  360. {
  361. SharpMap.Geometries.LineString line = feature as SharpMap.Geometries.LineString;
  362. if (line.Length / map.PixelSize > size.Width) //Only label feature if it is long enough
  363. CalculateLabelOnLinestring(line, ref lbl, map);
  364. else
  365. return null;
  366. }
  367. return lbl;
  368. }
  369. }
  370. private void CalculateLabelOnLinestring(SharpMap.Geometries.LineString line, ref SharpMap.Rendering.Label label, Map map)
  371. {
  372. double dx, dy;
  373. double tmpx, tmpy;
  374. double angle = 0.0;
  375. // first find the middle segment of the line
  376. int midPoint = (line.Vertices.Count - 1) / 2;
  377. if (line.Vertices.Count > 2)
  378. {
  379. dx = line.Vertices[midPoint + 1].X - line.Vertices[midPoint].X;
  380. dy = line.Vertices[midPoint + 1].Y - line.Vertices[midPoint].Y;
  381. }
  382. else
  383. {
  384. midPoint = 0;
  385. dx = line.Vertices[1].X - line.Vertices[0].X;
  386. dy = line.Vertices[1].Y - line.Vertices[0].Y;
  387. }
  388. if (dy == 0)
  389. label.Rotation = 0;
  390. else if (dx == 0)
  391. label.Rotation = 90;
  392. else
  393. {
  394. // calculate angle of line
  395. angle = -Math.Atan(dy / dx) + Math.PI * 0.5;
  396. angle *= (180d / Math.PI); // convert radians to degrees
  397. label.Rotation = (float)angle - 90; // -90 text orientation
  398. }
  399. tmpx = line.Vertices[midPoint].X + (dx * 0.5);
  400. tmpy = line.Vertices[midPoint].Y + (dy * 0.5);
  401. label.LabelPoint = map.WorldToImage(new SharpMap.Geometries.Point(tmpx, tmpy));
  402. }
  403. /// <summary>
  404. /// Gets the boundingbox of the entire layer
  405. /// </summary>
  406. public override SharpMap.Geometries.BoundingBox Envelope
  407. {
  408. get {
  409. if (this.DataSource == null)
  410. throw (new ApplicationException("DataSource property not set on layer '" + this.LayerName + "'"));
  411. bool wasOpen = this.DataSource.IsOpen;
  412. if (!wasOpen)
  413. this.DataSource.Open();
  414. SharpMap.Geometries.BoundingBox box = this.DataSource.GetExtents();
  415. if (!wasOpen) //Restore state
  416. this.DataSource.Close();
  417. return box;
  418. }
  419. }
  420. /// <summary>
  421. /// Gets or sets the SRID of this VectorLayer's data source
  422. /// </summary>
  423. public override int SRID
  424. {
  425. get {
  426. if (this.DataSource == null)
  427. throw (new ApplicationException("DataSource property not set on layer '" + this.LayerName + "'"));
  428. return this.DataSource.SRID; }
  429. set { this.DataSource.SRID = value; }
  430. }
  431. /// <summary>
  432. /// Clones the object
  433. /// </summary>
  434. /// <returns></returns>
  435. public override object Clone()
  436. {
  437. throw new NotImplementedException();
  438. }
  439. #region IDisposable Members
  440. /// <summary>
  441. /// Disposes the object
  442. /// </summary>
  443. public void Dispose()
  444. {
  445. if (DataSource is IDisposable)
  446. ((IDisposable)DataSource).Dispose();
  447. }
  448. #endregion
  449. }
  450. }