2012-11-22 21:42:29 +01:00
|
|
|
|
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
2013-05-07 18:12:25 +02:00
|
|
|
|
// Copyright © Clemens Fischer 2012-2013
|
2012-11-22 21:42:29 +01:00
|
|
|
|
// Licensed under the Microsoft Public License (Ms-PL)
|
|
|
|
|
|
|
|
|
|
|
|
using System;
|
2013-10-22 18:18:47 +02:00
|
|
|
|
using System.Collections.Generic;
|
2012-11-22 21:42:29 +01:00
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Windows;
|
|
|
|
|
|
using System.Windows.Media;
|
|
|
|
|
|
|
|
|
|
|
|
namespace MapControl
|
|
|
|
|
|
{
|
2013-04-12 19:59:16 +02:00
|
|
|
|
public partial class MapGraticule : MapOverlay
|
2012-11-22 21:42:29 +01:00
|
|
|
|
{
|
2013-11-18 19:49:17 +01:00
|
|
|
|
private class LonLabel
|
2013-10-22 18:18:47 +02:00
|
|
|
|
{
|
2013-11-18 19:49:17 +01:00
|
|
|
|
public readonly double Longitude;
|
2013-10-22 18:18:47 +02:00
|
|
|
|
public readonly string Text;
|
|
|
|
|
|
|
2013-11-18 19:49:17 +01:00
|
|
|
|
public LonLabel(double longitude, string text)
|
2013-10-22 18:18:47 +02:00
|
|
|
|
{
|
2013-11-18 19:49:17 +01:00
|
|
|
|
Longitude = longitude;
|
|
|
|
|
|
Text = text;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private class LatLabel
|
|
|
|
|
|
{
|
|
|
|
|
|
public readonly double TransformedLatitude;
|
|
|
|
|
|
public readonly double Latitude;
|
|
|
|
|
|
public readonly string Text;
|
|
|
|
|
|
|
|
|
|
|
|
public LatLabel(double transformedLatitude, double latitude, string text)
|
|
|
|
|
|
{
|
|
|
|
|
|
TransformedLatitude = transformedLatitude;
|
|
|
|
|
|
Latitude = latitude;
|
2013-10-22 18:18:47 +02:00
|
|
|
|
Text = text;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private Dictionary<string, GlyphRun> glyphRuns = new Dictionary<string, GlyphRun>();
|
|
|
|
|
|
|
2013-04-12 19:59:16 +02:00
|
|
|
|
static MapGraticule()
|
|
|
|
|
|
{
|
|
|
|
|
|
UIElement.IsHitTestVisibleProperty.OverrideMetadata(
|
|
|
|
|
|
typeof(MapGraticule), new FrameworkPropertyMetadata(false));
|
|
|
|
|
|
|
|
|
|
|
|
MapOverlay.StrokeThicknessProperty.OverrideMetadata(
|
2013-10-22 18:18:47 +02:00
|
|
|
|
typeof(MapGraticule), new FrameworkPropertyMetadata(0.5, (o, e) => ((MapGraticule)o).glyphRuns.Clear()));
|
2013-04-12 19:59:16 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override void OnViewportChanged()
|
2012-11-22 21:42:29 +01:00
|
|
|
|
{
|
2013-04-12 19:59:16 +02:00
|
|
|
|
InvalidateVisual();
|
|
|
|
|
|
}
|
2012-11-22 21:42:29 +01:00
|
|
|
|
|
2013-04-12 19:59:16 +02:00
|
|
|
|
protected override void OnRender(DrawingContext drawingContext)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ParentMap != null)
|
2012-11-22 21:42:29 +01:00
|
|
|
|
{
|
2013-04-12 19:59:16 +02:00
|
|
|
|
var bounds = ParentMap.ViewportTransform.Inverse.TransformBounds(new Rect(ParentMap.RenderSize));
|
2013-11-18 19:49:17 +01:00
|
|
|
|
var startPoint = new Point(bounds.X, bounds.Y);
|
|
|
|
|
|
var endPoint = new Point(bounds.X + bounds.Width, bounds.Y + bounds.Height);
|
|
|
|
|
|
var startLocation = ParentMap.MapTransform.Transform(startPoint);
|
|
|
|
|
|
var endLocation = ParentMap.MapTransform.Transform(endPoint);
|
2013-05-07 18:12:25 +02:00
|
|
|
|
var minSpacing = MinLineSpacing * 360d / (Math.Pow(2d, ParentMap.ZoomLevel) * TileSource.TileSize);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
var spacing = LineSpacings[LineSpacings.Length - 1];
|
|
|
|
|
|
|
|
|
|
|
|
if (spacing >= minSpacing)
|
|
|
|
|
|
{
|
|
|
|
|
|
spacing = LineSpacings.FirstOrDefault(s => s >= minSpacing);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-10-22 18:18:47 +02:00
|
|
|
|
var labelFormat = spacing < 1d ? "{0} {1}°{2:00}'" : "{0} {1}°";
|
|
|
|
|
|
var labelStart = new Location(
|
2013-11-18 19:49:17 +01:00
|
|
|
|
Math.Ceiling(startLocation.Latitude / spacing) * spacing,
|
|
|
|
|
|
Math.Ceiling(startLocation.Longitude / spacing) * spacing);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
|
2013-11-18 19:49:17 +01:00
|
|
|
|
var latLabels = new List<LatLabel>((int)((endLocation.Latitude - labelStart.Latitude) / spacing) + 1);
|
|
|
|
|
|
var lonLabels = new List<LonLabel>((int)((endLocation.Longitude - labelStart.Longitude) / spacing) + 1);
|
2013-10-22 18:18:47 +02:00
|
|
|
|
|
2013-11-18 19:49:17 +01:00
|
|
|
|
for (var lat = labelStart.Latitude; lat <= endLocation.Latitude; lat += spacing)
|
2012-11-22 21:42:29 +01:00
|
|
|
|
{
|
2013-11-18 19:49:17 +01:00
|
|
|
|
var location = new Location(lat, startLocation.Longitude);
|
|
|
|
|
|
var p1 = ParentMap.LocationToViewportPoint(location);
|
|
|
|
|
|
location.Longitude = endLocation.Longitude;
|
|
|
|
|
|
var p2 = ParentMap.LocationToViewportPoint(location);
|
2013-10-22 18:18:47 +02:00
|
|
|
|
|
2013-11-18 19:49:17 +01:00
|
|
|
|
latLabels.Add(new LatLabel(location.TransformedLatitude, lat, CoordinateString(lat, labelFormat, "NS")));
|
|
|
|
|
|
|
|
|
|
|
|
drawingContext.DrawLine(Pen, p1, p2);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-11-18 19:49:17 +01:00
|
|
|
|
for (var lon = labelStart.Longitude; lon <= endLocation.Longitude; lon += spacing)
|
2012-11-22 21:42:29 +01:00
|
|
|
|
{
|
2013-11-18 19:49:17 +01:00
|
|
|
|
lonLabels.Add(new LonLabel(lon, CoordinateString(Location.NormalizeLongitude(lon), labelFormat, "EW")));
|
2013-10-22 18:18:47 +02:00
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
drawingContext.DrawLine(Pen,
|
2013-11-18 19:49:17 +01:00
|
|
|
|
ParentMap.LocationToViewportPoint(new Location(startPoint.Y, startLocation.Latitude, lon)),
|
|
|
|
|
|
ParentMap.LocationToViewportPoint(new Location(endPoint.Y, endLocation.Latitude, lon)));
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-10-22 18:18:47 +02:00
|
|
|
|
if (Foreground != null && Foreground != Brushes.Transparent && latLabels.Count > 0 && lonLabels.Count > 0)
|
2012-11-22 21:42:29 +01:00
|
|
|
|
{
|
2013-10-22 18:18:47 +02:00
|
|
|
|
var latLabelOrigin = new Point(StrokeThickness / 2d + 2d, -StrokeThickness / 2d - FontSize / 4d);
|
|
|
|
|
|
var lonLabelOrigin = new Point(StrokeThickness / 2d + 2d, StrokeThickness / 2d + FontSize);
|
|
|
|
|
|
var transform = Matrix.Identity;
|
|
|
|
|
|
transform.Rotate(ParentMap.Heading);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
|
2013-10-22 18:18:47 +02:00
|
|
|
|
foreach (var latLabel in latLabels)
|
2012-11-22 21:42:29 +01:00
|
|
|
|
{
|
2013-10-22 18:18:47 +02:00
|
|
|
|
foreach (var lonLabel in lonLabels)
|
2012-11-22 21:42:29 +01:00
|
|
|
|
{
|
2013-10-22 18:18:47 +02:00
|
|
|
|
GlyphRun latGlyphRun;
|
|
|
|
|
|
GlyphRun lonGlyphRun;
|
|
|
|
|
|
|
|
|
|
|
|
if (!glyphRuns.TryGetValue(latLabel.Text, out latGlyphRun))
|
|
|
|
|
|
{
|
|
|
|
|
|
latGlyphRun = GlyphRunText.Create(latLabel.Text, Typeface, FontSize, latLabelOrigin);
|
|
|
|
|
|
glyphRuns.Add(latLabel.Text, latGlyphRun);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!glyphRuns.TryGetValue(lonLabel.Text, out lonGlyphRun))
|
|
|
|
|
|
{
|
|
|
|
|
|
lonGlyphRun = GlyphRunText.Create(lonLabel.Text, Typeface, FontSize, lonLabelOrigin);
|
|
|
|
|
|
glyphRuns.Add(lonLabel.Text, lonGlyphRun);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-11-18 19:49:17 +01:00
|
|
|
|
var position = ParentMap.LocationToViewportPoint(new Location(latLabel.TransformedLatitude, latLabel.Latitude, lonLabel.Longitude));
|
2013-10-22 18:18:47 +02:00
|
|
|
|
|
|
|
|
|
|
drawingContext.PushTransform(new MatrixTransform(
|
|
|
|
|
|
transform.M11, transform.M12, transform.M21, transform.M22, position.X, position.Y));
|
|
|
|
|
|
|
|
|
|
|
|
drawingContext.DrawGlyphRun(Foreground, latGlyphRun);
|
|
|
|
|
|
drawingContext.DrawGlyphRun(Foreground, lonGlyphRun);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
drawingContext.Pop();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-10-22 18:18:47 +02:00
|
|
|
|
|
|
|
|
|
|
var removeKeys = glyphRuns.Keys.Where(k => !latLabels.Any(l => l.Text == k) && !lonLabels.Any(l => l.Text == k));
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var key in removeKeys.ToList())
|
|
|
|
|
|
{
|
|
|
|
|
|
glyphRuns.Remove(key);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
glyphRuns.Clear();
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|