Skip to content

Commit 6e0856c

Browse files
committed
[Branches/1.0]
- LabelLayer: fixed Rotation issue by reusing Label.LabelPoint as rotation center (was obsolete) - LabelLayer: fixed horizontal alignment for multi-line-labels - VectorRenderer: Added new SizeOfString delegate implementation, set as default. - VectorRenderer: removed resizing of labelSize in DrawLabel function - Added unit test git-tfs-id: [https://tfs.codeplex.com/tfs/TFS01]$/SharpMap/Branches/1.0;C105211
1 parent eb5a83b commit 6e0856c

6 files changed

Lines changed: 215 additions & 35 deletions

File tree

SharpMap/Layers/LabelLayer.cs

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,8 @@ public override void Render(Graphics g, Map map)
590590
VectorRenderer.DrawLabel(g, label.Location, label.Style.Offset,
591591
label.Style.Font, label.Style.ForeColor,
592592
label.Style.BackColor, label.Style.Halo, label.Rotation,
593-
label.Text, map);
593+
label.Text, map, label.Style.HorizontalAlignment,
594+
label.LabelPoint);
594595
}
595596
else
596597
{
@@ -673,29 +674,34 @@ private static BaseLabel CreateLabel(FeatureDataRow fdr, IGeometry feature, stri
673674
return lbl;
674675
}
675676

676-
PointF position = Transform.WorldtoMap(feature.EnvelopeInternal.Centre, map);
677-
if (_getLocationMethod != null)
678-
{
679-
var p = _getLocationMethod(fdr);
680-
if (p !=null)
681-
position = Transform.WorldtoMap(p, map);
682-
}
683-
position.X = position.X - size.Width*(short) style.HorizontalAlignment*0.5f;
684-
position.Y = position.Y - size.Height*(short) (2-(int)style.VerticalAlignment)*0.5f;
685-
if (position.X - size.Width > map.Size.Width || position.X + size.Width < 0 ||
686-
position.Y - size.Height > map.Size.Height || position.Y + size.Height < 0)
677+
var worldPosition = _getLocationMethod == null
678+
? feature.EnvelopeInternal.Centre
679+
: _getLocationMethod(fdr);
680+
681+
if (worldPosition == null) return null;
682+
683+
var position = Transform.WorldtoMap(worldPosition, map);
684+
685+
var location = new PointF(
686+
position.X - size.Width*(short) style.HorizontalAlignment*0.5f,
687+
position.Y - size.Height*(short) (2 - (int) style.VerticalAlignment)*0.5f);
688+
689+
if (location.X - size.Width > map.Size.Width || location.X + size.Width < 0 ||
690+
location.Y - size.Height > map.Size.Height || location.Y + size.Height < 0)
687691
return null;
688692

689693
if (!style.CollisionDetection)
690-
lbl = new Label(text, position, rotation, priority, null, style);
694+
lbl = new Label(text, location, rotation, priority, null, style)
695+
{LabelPoint = position};
691696
else
692697
{
693698
//Collision detection is enabled so we need to measure the size of the string
694-
lbl = new Label(text, position, rotation, priority,
695-
new LabelBox(position.X - size.Width*0.5f - style.CollisionBuffer.Width,
696-
position.Y + size.Height*0.5f + style.CollisionBuffer.Height,
699+
lbl = new Label(text, location, rotation, priority,
700+
new LabelBox(location.X - size.Width*0.5f - style.CollisionBuffer.Width,
701+
location.Y + size.Height*0.5f + style.CollisionBuffer.Height,
697702
size.Width + 2f*style.CollisionBuffer.Width,
698-
size.Height + style.CollisionBuffer.Height*2f), style);
703+
size.Height + style.CollisionBuffer.Height*2f), style)
704+
{ LabelPoint = position };
699705
}
700706

701707
/*

SharpMap/Rendering/Label.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@ public class Label : BaseLabel<PointF>
424424
public Label(string text, PointF location, float rotation, int priority, LabelBox collisionbox, LabelStyle style)
425425
: base(text, location, rotation, priority, collisionbox, style)
426426
{
427+
LabelPoint = location;
427428
}
428429

429430
/// <summary>
@@ -437,15 +438,15 @@ public Label(string text, PointF location, float rotation, int priority, LabelBo
437438
public Label(string text, PointF location, float rotation, int priority, LabelStyle style)
438439
: base(text, location, rotation, priority, style)
439440
{
441+
LabelPoint = location;
440442
}
441443
/// <summary>
442444
/// Label position
443445
/// </summary>
444-
[Obsolete("Use Location")]
445446
public PointF LabelPoint
446447
{
447-
get { return Location; }
448-
set { Location = value; }
448+
get;
449+
set;
449450
}
450451

451452
}

SharpMap/Rendering/VectorRenderer.cs

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
using System.Reflection;
2323
using GeoAPI.Geometries;
2424
using SharpMap.Rendering.Symbolizer;
25+
using SharpMap.Styles;
2526
using SharpMap.Utilities;
2627
using Point=GeoAPI.Geometries.Coordinate;
2728
using System.Runtime.CompilerServices;
@@ -38,7 +39,7 @@ public static class VectorRenderer
3839

3940
static VectorRenderer()
4041
{
41-
SizeOfString = SizeOfStringCeiling;
42+
SizeOfString = SizeOfString74;
4243
}
4344

4445
private static readonly Bitmap Defaultsymbol =
@@ -271,7 +272,7 @@ private static PointF[] LimitValues(PointF[] vertices, float limit)
271272
/// </summary>
272273
public static SizeOfStringDelegate SizeOfString
273274
{
274-
get { return _sizeOfString; }
275+
get { return _sizeOfString ?? (_sizeOfString = SizeOfString74); }
275276
set
276277
{
277278
if (value != null )
@@ -291,6 +292,18 @@ public static SizeF SizeOfStringBase(Graphics g, string text, Font font)
291292
return g.MeasureString(text, font);
292293
}
293294

295+
/// <summary>
296+
/// Function to get the <see cref="SizeF"/> of a string when rendered with the given font.
297+
/// </summary>
298+
/// <param name="g"><see cref="Graphics"/> object</param>
299+
/// <param name="text">the text to render</param>
300+
/// <param name="font">the font to use</param>
301+
/// <returns>the size</returns>
302+
public static SizeF SizeOfString74(Graphics g, string text, Font font)
303+
{
304+
var s = g.MeasureString(text, font);
305+
return new SizeF(s.Width * 0.74f+1f, s.Height * 0.74f);
306+
}
294307
/// <summary>
295308
/// Function to get the <see cref="SizeF"/> of a string when rendered with the given font.
296309
/// </summary>
@@ -305,7 +318,6 @@ public static SizeF SizeOfStringCeiling(Graphics g, string text, Font font)
305318
}
306319

307320

308-
309321
/// <summary>
310322
/// Renders a label to the map.
311323
/// </summary>
@@ -319,37 +331,76 @@ public static SizeF SizeOfStringCeiling(Graphics g, string text, Font font)
319331
/// <param name="rotation">Text rotation in degrees</param>
320332
/// <param name="text">Text to render</param>
321333
/// <param name="map">Map reference</param>
334+
/// <param name="alignment">Horizontal alignment for multi line labels. If not set <see cref="StringAlignment.Near"/> is used</param>
335+
/// <param name="rotationPoint">Point where the rotation should take place</param>
322336
[MethodImpl(MethodImplOptions.Synchronized)]
323337
public static void DrawLabel(Graphics g, PointF labelPoint, PointF offset, Font font, Color forecolor,
324-
Brush backcolor, Pen halo, float rotation, string text, Map map)
338+
Brush backcolor, Pen halo, float rotation, string text, Map map,
339+
LabelStyle.HorizontalAlignmentEnum alignment = LabelStyle.HorizontalAlignmentEnum.Left,
340+
PointF? rotationPoint = null)
341+
325342
{
326-
SizeF fontSize = _sizeOfString(g, text, font); //Calculate the size of the text
343+
//Calculate the size of the text
344+
var labelSize = _sizeOfString(g, text, font);
345+
346+
//Add label offset
327347
labelPoint.X += offset.X;
328-
labelPoint.Y += offset.Y; //add label offset
348+
labelPoint.Y += offset.Y;
349+
350+
//Translate alignment to stringalignment
351+
StringAlignment salign;
352+
switch (alignment)
353+
{
354+
case LabelStyle.HorizontalAlignmentEnum.Left:
355+
salign = StringAlignment.Near;
356+
break;
357+
case LabelStyle.HorizontalAlignmentEnum.Center:
358+
salign = StringAlignment.Center;
359+
break;
360+
default:
361+
salign = StringAlignment.Far;
362+
break;
363+
}
364+
329365
if (rotation != 0 && !float.IsNaN(rotation))
330366
{
331-
g.TranslateTransform(labelPoint.X, labelPoint.Y);
367+
rotationPoint = rotationPoint ?? labelPoint;
368+
369+
g.FillEllipse(Brushes.LawnGreen, rotationPoint.Value.X - 1, rotationPoint.Value.Y - 1, 2, 2);
370+
371+
var t = g.Transform.Clone();
372+
g.TranslateTransform(rotationPoint.Value.X, rotationPoint.Value.Y);
332373
g.RotateTransform(rotation);
333-
g.TranslateTransform(-fontSize.Width/2, -fontSize.Height/2);
374+
//g.TranslateTransform(-labelSize.Width/2, -labelSize.Height/2);
375+
376+
labelPoint = new PointF(labelPoint.X - rotationPoint.Value.X,
377+
labelPoint.Y - rotationPoint.Value.Y);
378+
379+
//labelSize = new SizeF(labelSize.Width*0.74f + 1f, labelSize.Height*0.74f);
334380
if (backcolor != null && backcolor != Brushes.Transparent)
335-
g.FillRectangle(backcolor, 0, 0, fontSize.Width*0.74f + 1f, fontSize.Height*0.74f);
381+
g.FillRectangle(backcolor, labelPoint.X, labelPoint.Y, labelSize.Width, labelSize.Height);
382+
336383
var path = new GraphicsPath();
337-
path.AddString(text, font.FontFamily, (int) font.Style, font.Size, new System.Drawing.Point(0, 0), null);
384+
path.AddString(text, font.FontFamily, (int) font.Style, font.Size,
385+
new RectangleF(labelPoint, labelSize) /* labelPoint*/,
386+
new StringFormat { Alignment = salign } /*null*/);
338387
if (halo != null)
339388
g.DrawPath(halo, path);
389+
340390
g.FillPath(new SolidBrush(forecolor), path);
341391
//g.DrawString(text, font, new System.Drawing.SolidBrush(forecolor), 0, 0);
342-
g.Transform = map.MapTransform;
392+
g.Transform = t;
343393
}
344394
else
345395
{
346396
if (backcolor != null && backcolor != Brushes.Transparent)
347-
g.FillRectangle(backcolor, labelPoint.X, labelPoint.Y, fontSize.Width*0.74f + 1,
348-
fontSize.Height*0.74f);
397+
g.FillRectangle(backcolor, labelPoint.X, labelPoint.Y, labelSize.Width,
398+
labelSize.Height);
349399

350400
var path = new GraphicsPath();
351-
352-
path.AddString(text, font.FontFamily, (int) font.Style, font.Size, labelPoint, null);
401+
path.AddString(text, font.FontFamily, (int) font.Style, font.Size,
402+
new RectangleF(labelPoint, labelSize) /* labelPoint*/,
403+
new StringFormat { Alignment = salign } /*null*/);
353404
if (halo != null)
354405
g.DrawPath(halo, path);
355406
g.FillPath(new SolidBrush(forecolor), path);

UnitTests/Layers/LabelLayerTest.cs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
using System.Data;
2+
using System.Drawing;
3+
using System.Drawing.Imaging;
4+
using System.Runtime.InteropServices;
5+
using GeoAPI.Geometries;
6+
using NUnit.Framework;
7+
using SharpMap.Data;
8+
using SharpMap.Data.Providers;
9+
using SharpMap.Layers;
10+
using SharpMap.Rendering.Thematics;
11+
using SharpMap.Styles;
12+
13+
namespace UnitTests.Layers
14+
{
15+
public class LabelLayerTest
16+
{
17+
private FeatureDataTable _featureDataTable;
18+
19+
[TestFixtureSetUp]
20+
public void FixtureSetUp()
21+
{
22+
var fdt = new FeatureDataTable();
23+
fdt.Columns.Add(new DataColumn("ID", typeof (int)));
24+
fdt.Columns.Add(new DataColumn("LABEL", typeof (string)));
25+
fdt.Columns.Add(new DataColumn("HALIGN", typeof (int)));
26+
fdt.Columns.Add(new DataColumn("VALIGN", typeof (int)));
27+
28+
var factory = GeoAPI.GeometryServiceProvider.Instance.CreateGeometryFactory(4236);
29+
for (var i = 0; i < 3; i++)
30+
{
31+
for (var j = 0; j < 3; j++)
32+
{
33+
var fdr = fdt.NewRow();
34+
fdr[0] = i*3 + j;
35+
fdr[1] = string.Format("Point({0}, {1})\nID {2}", i, j, i*3 + j);
36+
fdr[2] = j;
37+
fdr[3] = i;
38+
fdr.Geometry = factory.CreatePoint(new Coordinate(j*100, i*100));
39+
fdt.AddRow(fdr);
40+
}
41+
}
42+
_featureDataTable = fdt;
43+
}
44+
45+
private static ITheme CreateTheme()
46+
{
47+
return new CustomTheme(StyleBasedOnAlignment);
48+
}
49+
50+
private static IStyle StyleBasedOnAlignment(FeatureDataRow dr)
51+
{
52+
var style = new LabelStyle
53+
{
54+
HorizontalAlignment = (LabelStyle.HorizontalAlignmentEnum)(int) dr[2],
55+
VerticalAlignment = (LabelStyle.VerticalAlignmentEnum)(int) dr[3],
56+
Rotation = -20,
57+
BackColor = Brushes.Pink,
58+
Halo = new Pen(Brushes.LightBlue, 2)
59+
};
60+
return style;
61+
}
62+
63+
[Test]
64+
public void MultiLineCenterAlignedTest()
65+
{
66+
using (var m = new SharpMap.Map(new Size(600, 400)))
67+
{
68+
m.BackColor = Color.SeaShell;
69+
//_featureDataTable.Clear();
70+
var gfp = new GeometryFeatureProvider(_featureDataTable);
71+
var vl = new VectorLayer("VL", gfp);
72+
var ll = new LabelLayer("MultiLineCenterAligned") {DataSource = gfp};
73+
ll.Theme = CreateTheme();
74+
ll.LabelColumn = "LABEL";
75+
m.Layers.Add(vl);
76+
m.Layers.Add(ll);
77+
78+
m.ZoomToExtents();
79+
using (var mapImage = m.GetMap())
80+
mapImage.Save("MultiLineCenterAligned.png", ImageFormat.Png);
81+
}
82+
}
83+
84+
[TestFixtureTearDown]
85+
public void FixtureTearDown()
86+
{
87+
_featureDataTable.Dispose();
88+
}
89+
}
90+
}

UnitTests/UnitTests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@
170170
<Compile Include="Data\Providers\SQLServer2008Tests.cs" />
171171
<Compile Include="Data\Providers\ThreadingTest.cs" />
172172
<Compile Include="Layers\GdiImageLayerTest.cs" />
173+
<Compile Include="Layers\LabelLayerTest.cs" />
173174
<Compile Include="Layers\TileLayerIssues.cs" />
174175
<Compile Include="MapDecorationTest.cs" />
175176
<Compile Include="MapTest.cs" />
@@ -201,6 +202,7 @@
201202
<Compile Include="Serialization\ThemeTest.cs" />
202203
<Compile Include="TestData\CreatingData.cs" />
203204
<Compile Include="TestWmsCapabilityParser.cs" />
205+
<Compile Include="UnitTestsFixture.cs" />
204206
<Compile Include="WFS\RequestValidationTest.cs" />
205207
<Compile Include="WMS\Encoding.cs" />
206208
<Compile Include="WMS\SpatialReferencedBoundingBoxTest.cs" />

UnitTests/UnitTestsFixture.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System;
2+
using System.Diagnostics;
3+
using GeoAPI;
4+
using NUnit.Framework;
5+
6+
namespace UnitTests
7+
{
8+
[SetUpFixture]
9+
public class UnitTestsFixture
10+
{
11+
private Stopwatch _stopWatch;
12+
13+
[SetUp]
14+
public void RunBeforeAnyTests()
15+
{
16+
GeometryServiceProvider.Instance = NetTopologySuite.NtsGeometryServices.Instance;
17+
18+
_stopWatch = new Stopwatch();
19+
Console.WriteLine("Starting tests");
20+
_stopWatch.Start();
21+
}
22+
23+
[TearDown]
24+
public void RunAfterAllTests()
25+
{
26+
_stopWatch.Stop();
27+
Console.WriteLine("All tests accomplished in {0}ms", _stopWatch.ElapsedMilliseconds);
28+
}
29+
}
30+
}

0 commit comments

Comments
 (0)