Added classes MapOverlay and MapScale.

This commit is contained in:
ClemensF 2012-10-25 08:42:51 +02:00
parent 27cdfe77ae
commit e52698586b
8 changed files with 461 additions and 232 deletions

View file

@ -56,6 +56,8 @@
<Compile Include="MapPanel.cs" />
<Compile Include="MapPolygon.cs" />
<Compile Include="MapPolyline.cs" />
<Compile Include="MapOverlay.cs" />
<Compile Include="MapScale.cs" />
<Compile Include="Pushpin.cs" />
<Compile Include="MapTransform.cs" />
<Compile Include="MapBase.cs" />

View file

@ -5,107 +5,24 @@
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
namespace MapControl
{
/// <summary>
/// Draws a graticule overlay. The minimum spacing in pixels between adjacent
/// graticule lines is specified by the MinLineSpacing property.
/// Draws a graticule overlay.
/// </summary>
public class MapGraticule : MapElement
public class MapGraticule : MapOverlay
{
public static readonly DependencyProperty ForegroundProperty = Control.ForegroundProperty.AddOwner(
typeof(MapGraticule), new FrameworkPropertyMetadata((o, e) => ((MapGraticule)o).UpdateBrush()));
public static readonly DependencyProperty FontSizeProperty = Control.FontSizeProperty.AddOwner(
typeof(MapGraticule));
public static readonly DependencyProperty FontFamilyProperty = Control.FontFamilyProperty.AddOwner(
typeof(MapGraticule), new FrameworkPropertyMetadata((o, e) => ((MapGraticule)o).typeface = null));
public static readonly DependencyProperty FontStyleProperty = Control.FontStyleProperty.AddOwner(
typeof(MapGraticule), new FrameworkPropertyMetadata((o, e) => ((MapGraticule)o).typeface = null));
public static readonly DependencyProperty FontWeightProperty = Control.FontWeightProperty.AddOwner(
typeof(MapGraticule), new FrameworkPropertyMetadata((o, e) => ((MapGraticule)o).typeface = null));
public static readonly DependencyProperty FontStretchProperty = Control.FontStretchProperty.AddOwner(
typeof(MapGraticule), new FrameworkPropertyMetadata((o, e) => ((MapGraticule)o).typeface = null));
public static readonly DependencyProperty StrokeProperty = Shape.StrokeProperty.AddOwner(
typeof(MapGraticule), new FrameworkPropertyMetadata((o, e) => ((MapGraticule)o).UpdateBrush()));
public static readonly DependencyProperty StrokeThicknessProperty = Shape.StrokeThicknessProperty.AddOwner(
typeof(MapGraticule), new FrameworkPropertyMetadata(0.5, (o, e) => ((MapGraticule)o).pen.Thickness = (double)e.NewValue));
public static readonly DependencyProperty MinLineSpacingProperty = DependencyProperty.Register(
"MinLineSpacing", typeof(double), typeof(MapGraticule), new FrameworkPropertyMetadata(100d));
public static double[] Spacings =
/// <summary>
/// Graticule line spacings in degrees.
/// </summary>
public static double[] LineSpacings =
new double[] { 1d / 60d, 1d / 30d, 1d / 12d, 1d / 6d, 1d / 4d, 1d / 3d, 1d / 2d, 1d, 2d, 5d, 10d, 15d, 20d, 30d, 45d };
private readonly DrawingVisual visual = new DrawingVisual();
private readonly Pen pen;
private Typeface typeface;
public MapGraticule()
{
pen = new Pen(null, StrokeThickness);
IsHitTestVisible = false;
AddVisualChild(visual);
}
public Brush Foreground
{
get { return (Brush)GetValue(ForegroundProperty); }
set { SetValue(ForegroundProperty, value); }
}
public double FontSize
{
get { return (double)GetValue(FontSizeProperty); }
set { SetValue(FontSizeProperty, value); }
}
public FontFamily FontFamily
{
get { return (FontFamily)GetValue(FontFamilyProperty); }
set { SetValue(FontFamilyProperty, value); }
}
public FontStyle FontStyle
{
get { return (FontStyle)GetValue(FontStyleProperty); }
set { SetValue(FontStyleProperty, value); }
}
public FontWeight FontWeight
{
get { return (FontWeight)GetValue(FontWeightProperty); }
set { SetValue(FontWeightProperty, value); }
}
public FontStretch FontStretch
{
get { return (FontStretch)GetValue(FontStretchProperty); }
set { SetValue(FontStretchProperty, value); }
}
public Brush Stroke
{
get { return (Brush)GetValue(StrokeProperty); }
set { SetValue(StrokeProperty, value); }
}
public double StrokeThickness
{
get { return (double)GetValue(StrokeThicknessProperty); }
set { SetValue(StrokeThicknessProperty, value); }
}
/// <summary>
/// Minimum spacing in pixels between adjacent graticule lines.
/// </summary>
@ -115,90 +32,61 @@ namespace MapControl
set { SetValue(MinLineSpacingProperty, value); }
}
protected override int VisualChildrenCount
{
get { return 1; }
}
protected override Visual GetVisualChild(int index)
{
return visual;
}
protected override void OnViewportChanged()
protected override void OnRender(DrawingContext drawingContext)
{
MapBase parentMap = ParentMap;
Rect bounds = parentMap.ViewportTransform.Inverse.TransformBounds(new Rect(parentMap.RenderSize));
Location loc1 = parentMap.MapTransform.TransformBack(bounds.TopLeft);
Location loc2 = parentMap.MapTransform.TransformBack(bounds.BottomRight);
double minSpacing = MinLineSpacing * 360d / (Math.Pow(2d, parentMap.ZoomLevel) * 256d);
double spacing = Spacings[Spacings.Length - 1];
double spacing = LineSpacings[LineSpacings.Length - 1];
if (spacing >= minSpacing)
{
spacing = Spacings.FirstOrDefault(s => s >= minSpacing);
spacing = LineSpacings.FirstOrDefault(s => s >= minSpacing);
}
double latitudeStart = Math.Ceiling(loc1.Latitude / spacing) * spacing;
double longitudeStart = Math.Ceiling(loc1.Longitude / spacing) * spacing;
if (pen.Brush == null)
for (double lat = latitudeStart; lat <= loc2.Latitude; lat += spacing)
{
pen.Brush = Stroke != null ? Stroke : Foreground;
drawingContext.DrawLine(Pen,
parentMap.LocationToViewportPoint(new Location(lat, loc1.Longitude)),
parentMap.LocationToViewportPoint(new Location(lat, loc2.Longitude)));
}
using (DrawingContext drawingContext = visual.RenderOpen())
for (double lon = longitudeStart; lon <= loc2.Longitude; lon += spacing)
{
drawingContext.DrawLine(Pen,
parentMap.LocationToViewportPoint(new Location(loc1.Latitude, lon)),
parentMap.LocationToViewportPoint(new Location(loc2.Latitude, lon)));
}
if (Foreground != null && Foreground != Brushes.Transparent)
{
string format = spacing < 1d ? "{0} {1}°{2:00}'" : "{0} {1}°";
for (double lat = latitudeStart; lat <= loc2.Latitude; lat += spacing)
{
drawingContext.DrawLine(pen,
parentMap.LocationToViewportPoint(new Location(lat, loc1.Longitude)),
parentMap.LocationToViewportPoint(new Location(lat, loc2.Longitude)));
}
for (double lon = longitudeStart; lon <= loc2.Longitude; lon += spacing)
{
drawingContext.DrawLine(pen,
parentMap.LocationToViewportPoint(new Location(loc1.Latitude, lon)),
parentMap.LocationToViewportPoint(new Location(loc2.Latitude, lon)));
}
if (Foreground != null && Foreground != Brushes.Transparent)
{
string format = spacing < 1d ? "{0} {1}°{2:00}'" : "{0} {1}°";
if (typeface == null)
for (double lon = longitudeStart; lon <= loc2.Longitude; lon += spacing)
{
typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
}
double t = StrokeThickness / 2d;
Point p = parentMap.LocationToViewportPoint(new Location(lat, lon));
Point latPos = new Point(p.X + t + 2d, p.Y - t - FontSize / 4d);
Point lonPos = new Point(p.X + t + 2d, p.Y + t + FontSize);
string latString = CoordinateString(lat, format, "NS");
string lonString = CoordinateString(Location.NormalizeLongitude(lon), format, "EW");
for (double lat = latitudeStart; lat <= loc2.Latitude; lat += spacing)
{
for (double lon = longitudeStart; lon <= loc2.Longitude; lon += spacing)
{
double t = StrokeThickness / 2d;
Point p = parentMap.LocationToViewportPoint(new Location(lat, lon));
Point latPos = new Point(p.X + t + 2d, p.Y - t - FontSize / 4d);
Point lonPos = new Point(p.X + t + 2d, p.Y + t + FontSize);
string latString = CoordinateString(lat, format, "NS");
string lonString = CoordinateString(Location.NormalizeLongitude(lon), format, "EW");
drawingContext.PushTransform(new RotateTransform(parentMap.Heading, p.X, p.Y));
drawingContext.DrawGlyphRun(Foreground, GlyphRunText.Create(latString, typeface, FontSize, latPos));
drawingContext.DrawGlyphRun(Foreground, GlyphRunText.Create(lonString, typeface, FontSize, lonPos));
drawingContext.Pop();
}
drawingContext.PushTransform(new RotateTransform(parentMap.Heading, p.X, p.Y));
drawingContext.DrawGlyphRun(Foreground, GlyphRunText.Create(latString, Typeface, FontSize, latPos));
drawingContext.DrawGlyphRun(Foreground, GlyphRunText.Create(lonString, Typeface, FontSize, lonPos));
drawingContext.Pop();
}
}
}
}
private void UpdateBrush()
{
pen.Brush = null;
OnViewportChanged();
}
private static string CoordinateString(double value, string format, string hemispheres)
{
char hemisphere = hemispheres[0];

224
MapControl/MapOverlay.cs Normal file
View file

@ -0,0 +1,224 @@
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
// Copyright © 2012 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
namespace MapControl
{
/// <summary>
/// Base class for map overlays with font, background, foreground and stroke properties.
/// </summary>
public class MapOverlay : MapElement
{
public static readonly DependencyProperty FontFamilyProperty = Control.FontFamilyProperty.AddOwner(
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).typeface = null));
public static readonly DependencyProperty FontStyleProperty = Control.FontStyleProperty.AddOwner(
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).typeface = null));
public static readonly DependencyProperty FontWeightProperty = Control.FontWeightProperty.AddOwner(
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).typeface = null));
public static readonly DependencyProperty FontStretchProperty = Control.FontStretchProperty.AddOwner(
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).typeface = null));
public static readonly DependencyProperty FontSizeProperty = Control.FontSizeProperty.AddOwner(
typeof(MapOverlay));
public static readonly DependencyProperty BackgroundProperty = Control.BackgroundProperty.AddOwner(
typeof(MapOverlay));
public static readonly DependencyProperty ForegroundProperty = Control.ForegroundProperty.AddOwner(
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).ForegroundChanged((Brush)e.NewValue)));
public static readonly DependencyProperty StrokeProperty = Shape.StrokeProperty.AddOwner(
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.Brush = (Brush)e.NewValue));
public static readonly DependencyProperty StrokeThicknessProperty = Shape.StrokeThicknessProperty.AddOwner(
typeof(MapOverlay), new FrameworkPropertyMetadata(0.5, FrameworkPropertyMetadataOptions.AffectsMeasure, (o, e) => ((MapOverlay)o).pen.Thickness = (double)e.NewValue));
public static readonly DependencyProperty StrokeDashArrayProperty = Shape.StrokeDashArrayProperty.AddOwner(
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.DashStyle = new DashStyle((DoubleCollection)e.NewValue, ((MapOverlay)o).StrokeDashOffset)));
public static readonly DependencyProperty StrokeDashOffsetProperty = Shape.StrokeDashOffsetProperty.AddOwner(
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.DashStyle = new DashStyle(((MapOverlay)o).StrokeDashArray, (double)e.NewValue)));
public static readonly DependencyProperty StrokeDashCapProperty = Shape.StrokeDashCapProperty.AddOwner(
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.DashCap = (PenLineCap)e.NewValue));
public static readonly DependencyProperty StrokeStartLineCapProperty = Shape.StrokeStartLineCapProperty.AddOwner(
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.StartLineCap = (PenLineCap)e.NewValue));
public static readonly DependencyProperty StrokeEndLineCapProperty = Shape.StrokeEndLineCapProperty.AddOwner(
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.EndLineCap = (PenLineCap)e.NewValue));
public static readonly DependencyProperty StrokeLineJoinProperty = Shape.StrokeLineJoinProperty.AddOwner(
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.LineJoin = (PenLineJoin)e.NewValue));
public static readonly DependencyProperty StrokeMiterLimitProperty = Shape.StrokeMiterLimitProperty.AddOwner(
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.MiterLimit = (double)e.NewValue));
private readonly Pen pen;
private Typeface typeface;
static MapOverlay()
{
UIElement.IsHitTestVisibleProperty.OverrideMetadata(
typeof(MapOverlay), new FrameworkPropertyMetadata(false));
}
public MapOverlay()
{
pen = new Pen
{
Brush = Stroke,
Thickness = StrokeThickness,
DashStyle = new DashStyle(StrokeDashArray, StrokeDashOffset),
DashCap = StrokeDashCap,
StartLineCap = StrokeStartLineCap,
EndLineCap = StrokeEndLineCap,
LineJoin = StrokeLineJoin,
MiterLimit = StrokeMiterLimit
};
}
public FontFamily FontFamily
{
get { return (FontFamily)GetValue(FontFamilyProperty); }
set { SetValue(FontFamilyProperty, value); }
}
public FontStyle FontStyle
{
get { return (FontStyle)GetValue(FontStyleProperty); }
set { SetValue(FontStyleProperty, value); }
}
public FontWeight FontWeight
{
get { return (FontWeight)GetValue(FontWeightProperty); }
set { SetValue(FontWeightProperty, value); }
}
public FontStretch FontStretch
{
get { return (FontStretch)GetValue(FontStretchProperty); }
set { SetValue(FontStretchProperty, value); }
}
public double FontSize
{
get { return (double)GetValue(FontSizeProperty); }
set { SetValue(FontSizeProperty, value); }
}
public Brush Background
{
get { return (Brush)GetValue(BackgroundProperty); }
set { SetValue(BackgroundProperty, value); }
}
public Brush Foreground
{
get { return (Brush)GetValue(ForegroundProperty); }
set { SetValue(ForegroundProperty, value); }
}
public Brush Stroke
{
get { return (Brush)GetValue(StrokeProperty); }
set { SetValue(StrokeProperty, value); }
}
public double StrokeThickness
{
get { return (double)GetValue(StrokeThicknessProperty); }
set { SetValue(StrokeThicknessProperty, value); }
}
public DoubleCollection StrokeDashArray
{
get { return (DoubleCollection)GetValue(StrokeDashArrayProperty); }
set { SetValue(StrokeDashArrayProperty, value); }
}
public double StrokeDashOffset
{
get { return (double)GetValue(StrokeDashOffsetProperty); }
set { SetValue(StrokeDashOffsetProperty, value); }
}
public PenLineCap StrokeDashCap
{
get { return (PenLineCap)GetValue(StrokeDashCapProperty); }
set { SetValue(StrokeDashCapProperty, value); }
}
public PenLineCap StrokeStartLineCap
{
get { return (PenLineCap)GetValue(StrokeStartLineCapProperty); }
set { SetValue(StrokeStartLineCapProperty, value); }
}
public PenLineCap StrokeEndLineCap
{
get { return (PenLineCap)GetValue(StrokeEndLineCapProperty); }
set { SetValue(StrokeEndLineCapProperty, value); }
}
public PenLineJoin StrokeLineJoin
{
get { return (PenLineJoin)GetValue(StrokeLineJoinProperty); }
set { SetValue(StrokeLineJoinProperty, value); }
}
public double StrokeMiterLimit
{
get { return (double)GetValue(StrokeMiterLimitProperty); }
set { SetValue(StrokeMiterLimitProperty, value); }
}
protected Pen Pen
{
get
{
if (pen.Brush == null)
{
pen.Brush = Foreground;
}
return pen;
}
}
protected Typeface Typeface
{
get
{
if (typeface == null)
{
typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
}
return typeface;
}
}
protected override void OnViewportChanged()
{
InvalidateVisual();
}
private void ForegroundChanged(Brush foreground)
{
if (Stroke == null)
{
pen.Brush = foreground;
}
}
}
}

View file

@ -14,11 +14,11 @@ namespace MapControl
public class MapPolygon : MapPolyline
{
public static readonly DependencyProperty FillProperty = Shape.FillProperty.AddOwner(
typeof(MapPolygon), new FrameworkPropertyMetadata((o, e) => ((MapPolygon)o).drawing.Brush = (Brush)e.NewValue));
typeof(MapPolygon), new FrameworkPropertyMetadata((o, e) => ((MapPolygon)o).Drawing.Brush = (Brush)e.NewValue));
public MapPolygon()
{
drawing.Brush = Fill;
Drawing.Brush = Fill;
}
public Brush Fill

View file

@ -3,8 +3,10 @@
// Licensed under the Microsoft Public License (Ms-PL)
using System;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Shapes;
@ -13,54 +15,68 @@ namespace MapControl
/// <summary>
/// An open map polygon, defined by a collection of geographic locations in the Locations property.
/// </summary>
public class MapPolyline : MapElement
public class MapPolyline : FrameworkElement
{
public static readonly DependencyProperty StrokeProperty = Shape.StrokeProperty.AddOwner(
typeof(MapPolyline), new FrameworkPropertyMetadata(Brushes.Black, (o, e) => ((MapPolyline)o).drawing.Pen.Brush = (Brush)e.NewValue));
public static readonly DependencyProperty StrokeDashArrayProperty = Shape.StrokeDashArrayProperty.AddOwner(
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).drawing.Pen.DashStyle = new DashStyle((DoubleCollection)e.NewValue, ((MapPolyline)o).StrokeDashOffset)));
public static readonly DependencyProperty StrokeDashOffsetProperty = Shape.StrokeDashOffsetProperty.AddOwner(
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).drawing.Pen.DashStyle = new DashStyle(((MapPolyline)o).StrokeDashArray, (double)e.NewValue)));
public static readonly DependencyProperty StrokeDashCapProperty = Shape.StrokeDashCapProperty.AddOwner(
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).drawing.Pen.DashCap = (PenLineCap)e.NewValue));
public static readonly DependencyProperty StrokeStartLineCapProperty = Shape.StrokeStartLineCapProperty.AddOwner(
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).drawing.Pen.StartLineCap = (PenLineCap)e.NewValue));
public static readonly DependencyProperty StrokeEndLineCapProperty = Shape.StrokeEndLineCapProperty.AddOwner(
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).drawing.Pen.EndLineCap = (PenLineCap)e.NewValue));
public static readonly DependencyProperty StrokeLineJoinProperty = Shape.StrokeLineJoinProperty.AddOwner(
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).drawing.Pen.LineJoin = (PenLineJoin)e.NewValue));
public static readonly DependencyProperty StrokeMiterLimitProperty = Shape.StrokeMiterLimitProperty.AddOwner(
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).drawing.Pen.MiterLimit = (double)e.NewValue));
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.Brush = (Brush)e.NewValue));
public static readonly DependencyProperty StrokeThicknessProperty = Shape.StrokeThicknessProperty.AddOwner(
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).UpdatePenThickness()));
typeof(MapPolyline));
public static readonly DependencyProperty StrokeDashArrayProperty = Shape.StrokeDashArrayProperty.AddOwner(
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.DashStyle = new DashStyle((DoubleCollection)e.NewValue, ((MapPolyline)o).StrokeDashOffset)));
public static readonly DependencyProperty StrokeDashOffsetProperty = Shape.StrokeDashOffsetProperty.AddOwner(
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.DashStyle = new DashStyle(((MapPolyline)o).StrokeDashArray, (double)e.NewValue)));
public static readonly DependencyProperty StrokeDashCapProperty = Shape.StrokeDashCapProperty.AddOwner(
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.DashCap = (PenLineCap)e.NewValue));
public static readonly DependencyProperty StrokeStartLineCapProperty = Shape.StrokeStartLineCapProperty.AddOwner(
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.StartLineCap = (PenLineCap)e.NewValue));
public static readonly DependencyProperty StrokeEndLineCapProperty = Shape.StrokeEndLineCapProperty.AddOwner(
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.EndLineCap = (PenLineCap)e.NewValue));
public static readonly DependencyProperty StrokeLineJoinProperty = Shape.StrokeLineJoinProperty.AddOwner(
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.LineJoin = (PenLineJoin)e.NewValue));
public static readonly DependencyProperty StrokeMiterLimitProperty = Shape.StrokeMiterLimitProperty.AddOwner(
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.MiterLimit = (double)e.NewValue));
public static readonly DependencyProperty TransformStrokeProperty = DependencyProperty.Register(
"TransformStroke", typeof(bool), typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).UpdatePenThickness()));
"TransformStroke", typeof(bool), typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).SetPenThicknessBinding()));
public static readonly DependencyProperty LocationsProperty = DependencyProperty.Register(
"Locations", typeof(LocationCollection), typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).UpdateGeometry()));
protected readonly DrawingVisual visual = new DrawingVisual();
protected readonly GeometryDrawing drawing = new GeometryDrawing();
protected readonly GeometryDrawing Drawing = new GeometryDrawing();
static MapPolyline()
{
MapPanel.ParentMapPropertyKey.OverrideMetadata(
typeof(MapPolyline),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, ParentMapPropertyChanged));
}
public MapPolyline()
{
drawing.Pen = new Pen(Stroke, StrokeThickness);
using (DrawingContext drawingContext = visual.RenderOpen())
Drawing.Pen = new Pen
{
drawingContext.DrawDrawing(drawing);
}
Brush = Stroke,
Thickness = StrokeThickness,
DashStyle = new DashStyle(StrokeDashArray, StrokeDashOffset),
DashCap = StrokeDashCap,
StartLineCap = StrokeStartLineCap,
EndLineCap = StrokeEndLineCap,
LineJoin = StrokeLineJoin,
MiterLimit = StrokeMiterLimit
};
}
Loaded += (o, e) => UpdateGeometry();
public MapBase ParentMap
{
get { return MapPanel.GetParentMap(this); }
}
public Brush Stroke
@ -69,6 +85,12 @@ namespace MapControl
set { SetValue(StrokeProperty, value); }
}
public double StrokeThickness
{
get { return (double)GetValue(StrokeThicknessProperty); }
set { SetValue(StrokeThicknessProperty, value); }
}
public DoubleCollection StrokeDashArray
{
get { return (DoubleCollection)GetValue(StrokeDashArrayProperty); }
@ -111,12 +133,6 @@ namespace MapControl
set { SetValue(StrokeMiterLimitProperty, value); }
}
public double StrokeThickness
{
get { return (double)GetValue(StrokeThicknessProperty); }
set { SetValue(StrokeThicknessProperty, value); }
}
public bool TransformStroke
{
get { return (bool)GetValue(TransformStrokeProperty); }
@ -129,43 +145,19 @@ namespace MapControl
set { SetValue(LocationsProperty, value); }
}
public double TransformedStrokeThickness
{
get { return drawing.Pen.Thickness; }
}
public PathGeometry TransformedGeometry
{
get { return drawing.Geometry as PathGeometry; }
get { return Drawing.Geometry as PathGeometry; }
}
protected override int VisualChildrenCount
public double TransformedStrokeThickness
{
get { return 1; }
get { return Drawing.Pen.Thickness; }
}
protected override Visual GetVisualChild(int index)
protected override void OnRender(DrawingContext drawingContext)
{
return visual;
}
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
AddVisualChild(visual);
}
protected override void OnViewportChanged()
{
double scale = 1d;
if (TransformStroke)
{
scale = ParentMap.CenterScale * MapBase.MeterPerDegree;
}
drawing.Pen.Thickness = scale * StrokeThickness;
drawingContext.DrawDrawing(Drawing);
}
protected virtual void UpdateGeometry()
@ -177,20 +169,11 @@ namespace MapControl
{
if (ParentMap != null && Locations != null && Locations.Count > 0)
{
drawing.Geometry = CreateGeometry(Locations, closed);
OnViewportChanged();
Drawing.Geometry = CreateGeometry(Locations, closed);
}
else
{
drawing.Geometry = null;
}
}
private void UpdatePenThickness()
{
if (ParentMap != null)
{
OnViewportChanged();
Drawing.Geometry = null;
}
}
@ -213,5 +196,44 @@ namespace MapControl
return geometry;
}
private void SetPenThicknessBinding()
{
BindingBase binding = new Binding { Source = this, Path = new PropertyPath(MapPolyline.StrokeThicknessProperty)};
if (TransformStroke && ParentMap != null)
{
MultiBinding multiBinding = new MultiBinding { Converter = new PenThicknessConverter() };
multiBinding.Bindings.Add(binding);
multiBinding.Bindings.Add(new Binding { Source = ParentMap, Path = new PropertyPath(MapBase.CenterScaleProperty)});
binding = multiBinding;
}
BindingOperations.SetBinding(Drawing.Pen, Pen.ThicknessProperty, binding);
}
private static void ParentMapPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
MapPolyline polyline = obj as MapPolyline;
if (polyline != null)
{
polyline.UpdateGeometry();
polyline.SetPenThicknessBinding();
}
}
private class PenThicknessConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return (double)values[0] * (double)values[1] * MapBase.MeterPerDegree;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
}

92
MapControl/MapScale.cs Normal file
View file

@ -0,0 +1,92 @@
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
// Copyright © 2012 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace MapControl
{
/// <summary>
/// Draws a map scale overlay.
/// </summary>
public class MapScale : MapOverlay
{
public static readonly DependencyProperty PaddingProperty = Control.PaddingProperty.AddOwner(
typeof(MapOverlay), new FrameworkPropertyMetadata(new Thickness(2d)));
private double length;
private Size size;
static MapScale()
{
FrameworkElement.MinWidthProperty.OverrideMetadata(
typeof(MapScale), new FrameworkPropertyMetadata(100d));
FrameworkElement.HorizontalAlignmentProperty.OverrideMetadata(
typeof(MapScale), new FrameworkPropertyMetadata(HorizontalAlignment.Right));
FrameworkElement.VerticalAlignmentProperty.OverrideMetadata(
typeof(MapScale), new FrameworkPropertyMetadata(VerticalAlignment.Bottom));
MapOverlay.StrokeStartLineCapProperty.OverrideMetadata(
typeof(MapScale), new FrameworkPropertyMetadata(PenLineCap.Round));
MapOverlay.StrokeEndLineCapProperty.OverrideMetadata(
typeof(MapScale), new FrameworkPropertyMetadata(PenLineCap.Round));
}
public Thickness Padding
{
get { return (Thickness)GetValue(PaddingProperty); }
set { SetValue(PaddingProperty, value); }
}
protected override Size MeasureOverride(Size availableSize)
{
double scale = ParentMap.CenterScale; // px/m
length = MinWidth / scale;
double magnitude = Math.Pow(10d, Math.Floor(Math.Log10(length)));
if (length / magnitude < 2d)
{
length = 2d * magnitude;
}
else if (length / magnitude < 5d)
{
length = 5d * magnitude;
}
else
{
length = 10d * magnitude;
}
size.Width = length * scale + StrokeThickness + Padding.Left + Padding.Right;
size.Height = FontSize + 2d * StrokeThickness + Padding.Top + Padding.Bottom;
return size;
}
protected override void OnRender(DrawingContext drawingContext)
{
double x1 = Padding.Left + StrokeThickness / 2d;
double x2 = size.Width - Padding.Right - StrokeThickness / 2d;
double y1 = size.Height / 2d;
double y2 = size.Height - Padding.Bottom - StrokeThickness / 2d;
string text = length >= 1000d ? string.Format("{0:0} km", length / 1000d) : string.Format("{0:0} m", length);
drawingContext.DrawRectangle(Background ?? ParentMap.Background, null, new Rect(size));
drawingContext.DrawLine(Pen, new Point(x1, y1), new Point(x1, y2));
drawingContext.DrawLine(Pen, new Point(x2, y1), new Point(x2, y2));
drawingContext.DrawLine(Pen, new Point(x1, y2), new Point(x2, y2));
drawingContext.DrawGlyphRun(Foreground,
GlyphRunText.Create(text, Typeface, FontSize, new Vector(size.Width / 2d, y1 - StrokeThickness - 1d)));
}
protected override void OnViewportChanged()
{
InvalidateMeasure();
}
}
}

View file

@ -136,6 +136,7 @@
ManipulationInertiaStarting="MapManipulationInertiaStarting"
MouseMove="MapMouseMove" MouseLeave="MapMouseLeave">
<map:MapGraticule Opacity="0.6"/>
<map:MapScale Margin="4" Opacity="0.8"/>
<map:MapItemsControl ItemsSource="{StaticResource Polylines}"
ItemTemplate="{StaticResource PolylineItemTemplate}"/>
<map:MapItemsControl ItemsSource="{StaticResource Points}"
@ -152,7 +153,7 @@
</MultiBinding>
</map:Pushpin.Visibility>
</map:Pushpin>
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="2,0,0,0" FontSize="10"
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="4" FontSize="10"
Text="{Binding BaseTileLayer.Description, ElementName=map}"/>
</map:Map>
<Grid Grid.Row="1" Margin="2">

View file

@ -10,17 +10,17 @@ namespace SampleApplication
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
Visibility visibility = Visibility.Visible;
Visibility visibility = Visibility.Hidden;
if (values.Length == 2 && values[0] is Map && values[1] is Point? && ((Point?)values[1]).HasValue)
{
Map parentMap = (Map)values[0];
Point position = ((Point?)values[1]).Value;
if (position.X < 0d || position.X > parentMap.ActualWidth ||
position.Y < 0d || position.Y > parentMap.ActualHeight)
if (position.X >= 0d && position.X <= parentMap.ActualWidth &&
position.Y >= 0d && position.Y <= parentMap.ActualHeight)
{
visibility = Visibility.Hidden;
visibility = Visibility.Visible;
}
}