XAML-Map-Control/MapControl/MapGraticule.cs

214 lines
8.6 KiB
C#
Raw Normal View History

2012-05-04 12:52:20 +02:00
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
// Copyright © 2012 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
2012-04-25 22:02:53 +02:00
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
namespace MapControl
{
2012-05-04 12:52:20 +02:00
/// <summary>
/// Draws a graticule overlay. The minimum spacing in pixels between adjacent graticule lines
/// is specified by the MinSpacingPixels property.
/// </summary>
2012-04-25 22:02:53 +02:00
public class MapGraticule : MapElement
{
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 MinSpacingPixelsProperty = DependencyProperty.Register(
"MinSpacingPixels", typeof(double), typeof(MapGraticule), new FrameworkPropertyMetadata(100d));
2012-05-04 12:52:20 +02:00
public static double[] Spacings =
2012-04-25 22:02:53 +02:00
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); }
}
public double MinSpacingPixels
{
get { return (double)GetValue(MinSpacingPixelsProperty); }
set { SetValue(MinSpacingPixelsProperty, value); }
}
protected override int VisualChildrenCount
{
get { return 1; }
}
protected override Visual GetVisualChild(int index)
{
return visual;
}
protected override void OnViewTransformChanged(Map parentMap)
{
2012-05-04 12:52:20 +02:00
Rect bounds = parentMap.ViewportTransform.Inverse.TransformBounds(new Rect(parentMap.RenderSize));
Location loc1 = parentMap.MapTransform.TransformBack(bounds.TopLeft);
Location loc2 = parentMap.MapTransform.TransformBack(bounds.BottomRight);
2012-04-25 22:02:53 +02:00
double minSpacing = MinSpacingPixels * 360d / (Math.Pow(2d, parentMap.ZoomLevel) * 256d);
2012-05-04 12:52:20 +02:00
double spacing = Spacings[Spacings.Length - 1];
2012-04-25 22:02:53 +02:00
if (spacing >= minSpacing)
{
2012-05-04 12:52:20 +02:00
spacing = Spacings.FirstOrDefault(s => s >= minSpacing);
2012-04-25 22:02:53 +02:00
}
2012-05-04 12:52:20 +02:00
double latitudeStart = Math.Ceiling(loc1.Latitude / spacing) * spacing;
double longitudeStart = Math.Ceiling(loc1.Longitude / spacing) * spacing;
2012-04-25 22:02:53 +02:00
if (pen.Brush == null)
{
pen.Brush = Stroke != null ? Stroke : Foreground;
}
using (DrawingContext drawingContext = visual.RenderOpen())
{
2012-05-04 12:52:20 +02:00
for (double lat = latitudeStart; lat <= loc2.Latitude; lat += spacing)
2012-04-25 22:02:53 +02:00
{
drawingContext.DrawLine(pen,
2012-05-04 12:52:20 +02:00
parentMap.LocationToViewportPoint(new Location(lat, loc1.Longitude)),
parentMap.LocationToViewportPoint(new Location(lat, loc2.Longitude)));
2012-04-25 22:02:53 +02:00
}
2012-05-04 12:52:20 +02:00
for (double lon = longitudeStart; lon <= loc2.Longitude; lon += spacing)
2012-04-25 22:02:53 +02:00
{
drawingContext.DrawLine(pen,
2012-05-04 12:52:20 +02:00
parentMap.LocationToViewportPoint(new Location(loc1.Latitude, lon)),
parentMap.LocationToViewportPoint(new Location(loc2.Latitude, lon)));
2012-04-25 22:02:53 +02:00
}
if (Foreground != null && Foreground != Brushes.Transparent)
{
string format = spacing < 1d ? "{0} {1}°{2:00}'" : "{0} {1}°";
if (typeface == null)
{
typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
}
2012-05-04 12:52:20 +02:00
for (double lat = latitudeStart; lat <= loc2.Latitude; lat += spacing)
2012-04-25 22:02:53 +02:00
{
2012-05-04 12:52:20 +02:00
for (double lon = longitudeStart; lon <= loc2.Longitude; lon += spacing)
2012-04-25 22:02:53 +02:00
{
double t = StrokeThickness / 2d;
2012-05-04 12:52:20 +02:00
Point p = parentMap.LocationToViewportPoint(new Location(lat, lon));
2012-04-25 22:02:53 +02:00
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(((lon + 180d) % 360d + 360d) % 360d - 180d, 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();
}
}
}
}
}
private void UpdateBrush()
{
pen.Brush = null;
OnViewTransformChanged(ParentMap);
}
private static string CoordinateString(double value, string format, string hemispheres)
{
char hemisphere = hemispheres[0];
if (value < -1e-8) // ~1mm
{
value = -value;
hemisphere = hemispheres[1];
}
int minutes = (int)(value * 60d + 0.5);
return string.Format(format, hemisphere, minutes / 60, (double)(minutes % 60));
}
}
}