mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2026-04-04 14:08:32 +00:00
Added Location
This commit is contained in:
parent
caab7208a3
commit
a98f59e1a9
35 changed files with 1235 additions and 717 deletions
|
|
@ -1,9 +1,16 @@
|
|||
using System;
|
||||
// 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.Media;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains helper methods for creating GlyphRun objects.
|
||||
/// </summary>
|
||||
public static class GlyphRunText
|
||||
{
|
||||
public static GlyphRun Create(string text, Typeface typeface, double emSize, Point baselineOrigin)
|
||||
|
|
|
|||
51
MapControl/Location.cs
Normal file
51
MapControl/Location.cs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// A geographic location given as latitude and longitude.
|
||||
/// </summary>
|
||||
[TypeConverter(typeof(LocationConverter))]
|
||||
public class Location
|
||||
{
|
||||
public Location()
|
||||
{
|
||||
}
|
||||
|
||||
public Location(double latitude, double longitude)
|
||||
{
|
||||
Latitude = latitude;
|
||||
Longitude = longitude;
|
||||
}
|
||||
|
||||
public double Latitude { get; set; }
|
||||
public double Longitude { get; set; }
|
||||
|
||||
public static Location Parse(string source)
|
||||
{
|
||||
Point p = Point.Parse(source);
|
||||
return new Location(p.X, p.Y);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0:0.00000},{1:0.00000}", Latitude, Longitude);
|
||||
}
|
||||
}
|
||||
|
||||
public class LocationConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
return Location.Parse((string)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
50
MapControl/LocationAnimation.cs
Normal file
50
MapControl/LocationAnimation.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Animates the value of a Location property between two values.
|
||||
/// </summary>
|
||||
public class LocationAnimation : AnimationTimeline
|
||||
{
|
||||
public Location From { get; set; }
|
||||
public Location To { get; set; }
|
||||
public IEasingFunction EasingFunction { get; set; }
|
||||
|
||||
public override Type TargetPropertyType
|
||||
{
|
||||
get { return typeof(Location); }
|
||||
}
|
||||
|
||||
protected override Freezable CreateInstanceCore()
|
||||
{
|
||||
return new LocationAnimation
|
||||
{
|
||||
From = From,
|
||||
To = To,
|
||||
EasingFunction = EasingFunction
|
||||
};
|
||||
}
|
||||
|
||||
public override object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock)
|
||||
{
|
||||
if (!animationClock.CurrentProgress.HasValue)
|
||||
{
|
||||
return defaultOriginValue;
|
||||
}
|
||||
|
||||
double progress = animationClock.CurrentProgress.Value;
|
||||
|
||||
if (EasingFunction != null)
|
||||
{
|
||||
progress = EasingFunction.Ease(progress);
|
||||
}
|
||||
|
||||
return new Location(
|
||||
(1d - progress) * From.Latitude + progress * To.Latitude,
|
||||
(1d - progress) * From.Longitude + progress * To.Longitude);
|
||||
}
|
||||
}
|
||||
}
|
||||
52
MapControl/LocationCollection.cs
Normal file
52
MapControl/LocationCollection.cs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection of geographic locations.
|
||||
/// </summary>
|
||||
[TypeConverter(typeof(LocationCollectionConverter))]
|
||||
public class LocationCollection : ObservableCollection<Location>
|
||||
{
|
||||
public LocationCollection()
|
||||
{
|
||||
}
|
||||
|
||||
public LocationCollection(IEnumerable<Location> locations)
|
||||
{
|
||||
foreach (Location location in locations)
|
||||
{
|
||||
Add(location);
|
||||
}
|
||||
}
|
||||
|
||||
public static LocationCollection Parse(string source)
|
||||
{
|
||||
LocationCollection locations = new LocationCollection();
|
||||
|
||||
foreach (string locString in source.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
locations.Add(Location.Parse(locString));
|
||||
}
|
||||
|
||||
return locations;
|
||||
}
|
||||
}
|
||||
|
||||
public class LocationCollectionConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
return LocationCollection.Parse((string)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,8 @@
|
|||
using System;
|
||||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
|
@ -7,6 +11,12 @@ using System.Windows.Media.Animation;
|
|||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// The main map control. Draws map content provided by the TileLayers or the MainTileLayer property.
|
||||
/// The visible map area is defined by the Center and ZoomLevel properties. The map can be rotated
|
||||
/// by an angle that is given by the Heading property.
|
||||
/// Map is a MapPanel and hence can contain map overlays like other MapPanels or MapItemsControls.
|
||||
/// </summary>
|
||||
public partial class Map : MapPanel
|
||||
{
|
||||
public const double MeterPerDegree = 1852d * 60d;
|
||||
|
|
@ -34,24 +44,24 @@ namespace MapControl
|
|||
"TileLayers", typeof(TileLayerCollection), typeof(Map), new FrameworkPropertyMetadata(
|
||||
(o, e) => ((Map)o).SetTileLayers((TileLayerCollection)e.NewValue)));
|
||||
|
||||
public static readonly DependencyProperty BaseTileLayerProperty = DependencyProperty.Register(
|
||||
"BaseTileLayer", typeof(TileLayer), typeof(Map), new FrameworkPropertyMetadata(
|
||||
(o, e) => ((Map)o).SetBaseTileLayer((TileLayer)e.NewValue),
|
||||
(o, v) => ((Map)o).CoerceBaseTileLayer((TileLayer)v)));
|
||||
public static readonly DependencyProperty MainTileLayerProperty = DependencyProperty.Register(
|
||||
"MainTileLayer", typeof(TileLayer), typeof(Map), new FrameworkPropertyMetadata(
|
||||
(o, e) => ((Map)o).SetMainTileLayer((TileLayer)e.NewValue),
|
||||
(o, v) => ((Map)o).CoerceMainTileLayer((TileLayer)v)));
|
||||
|
||||
public static readonly DependencyProperty TileOpacityProperty = DependencyProperty.Register(
|
||||
"TileOpacity", typeof(double), typeof(Map), new FrameworkPropertyMetadata(1d,
|
||||
(o, e) => ((Map)o).tileContainer.Opacity = (double)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty CenterProperty = DependencyProperty.Register(
|
||||
"Center", typeof(Point), typeof(Map), new FrameworkPropertyMetadata(new Point(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
|
||||
(o, e) => ((Map)o).SetCenter((Point)e.NewValue),
|
||||
(o, v) => ((Map)o).CoerceCenter((Point)v)));
|
||||
"Center", typeof(Location), typeof(Map), new FrameworkPropertyMetadata(new Location(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
|
||||
(o, e) => ((Map)o).SetCenter((Location)e.NewValue),
|
||||
(o, v) => ((Map)o).CoerceCenter((Location)v)));
|
||||
|
||||
public static readonly DependencyProperty TargetCenterProperty = DependencyProperty.Register(
|
||||
"TargetCenter", typeof(Point), typeof(Map), new FrameworkPropertyMetadata(new Point(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
|
||||
(o, e) => ((Map)o).SetTargetCenter((Point)e.NewValue),
|
||||
(o, v) => ((Map)o).CoerceCenter((Point)v)));
|
||||
"TargetCenter", typeof(Location), typeof(Map), new FrameworkPropertyMetadata(new Location(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
|
||||
(o, e) => ((Map)o).SetTargetCenter((Location)e.NewValue),
|
||||
(o, v) => ((Map)o).CoerceCenter((Location)v)));
|
||||
|
||||
public static readonly DependencyProperty ZoomLevelProperty = DependencyProperty.Register(
|
||||
"ZoomLevel", typeof(double), typeof(Map), new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
|
||||
|
|
@ -79,13 +89,13 @@ namespace MapControl
|
|||
public static readonly DependencyProperty CenterScaleProperty = CenterScalePropertyKey.DependencyProperty;
|
||||
|
||||
private readonly TileContainer tileContainer = new TileContainer();
|
||||
private readonly MapViewTransform mapViewTransform = new MapViewTransform();
|
||||
private readonly MapTransform mapTransform = new MercatorTransform();
|
||||
private readonly ScaleTransform scaleTransform = new ScaleTransform();
|
||||
private readonly RotateTransform rotateTransform = new RotateTransform();
|
||||
private readonly MatrixTransform scaleRotateTransform = new MatrixTransform();
|
||||
private Point? transformOrigin;
|
||||
private Point viewOrigin;
|
||||
private PointAnimation centerAnimation;
|
||||
private Location transformOrigin;
|
||||
private Point viewportOrigin;
|
||||
private LocationAnimation centerAnimation;
|
||||
private DoubleAnimation zoomLevelAnimation;
|
||||
private DoubleAnimation headingAnimation;
|
||||
private bool updateTransform = true;
|
||||
|
|
@ -96,9 +106,8 @@ namespace MapControl
|
|||
MaxZoomLevel = 20;
|
||||
|
||||
AddVisualChild(tileContainer);
|
||||
mapViewTransform.ViewTransform = tileContainer.ViewTransform;
|
||||
|
||||
BaseTileLayer = new TileLayer
|
||||
MainTileLayer = new TileLayer
|
||||
{
|
||||
Description = "© {y} OpenStreetMap Contributors, CC-BY-SA",
|
||||
TileSource = new OpenStreetMapTileSource { UriFormat = "http://{c}.tile.openstreetmap.org/{z}/{x}/{y}.png" }
|
||||
|
|
@ -190,12 +199,12 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the base TileLayer used by this Map, i.e. TileLayers[0].
|
||||
/// Gets or sets the main TileLayer used by this Map, i.e. TileLayers[0].
|
||||
/// </summary>
|
||||
public TileLayer BaseTileLayer
|
||||
public TileLayer MainTileLayer
|
||||
{
|
||||
get { return (TileLayer)GetValue(BaseTileLayerProperty); }
|
||||
set { SetValue(BaseTileLayerProperty, value); }
|
||||
get { return (TileLayer)GetValue(MainTileLayerProperty); }
|
||||
set { SetValue(MainTileLayerProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -208,20 +217,20 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the world coordinates (latitude and longitude) of the center point.
|
||||
/// Gets or sets the location of the center point of the Map.
|
||||
/// </summary>
|
||||
public Point Center
|
||||
public Location Center
|
||||
{
|
||||
get { return (Point)GetValue(CenterProperty); }
|
||||
get { return (Location)GetValue(CenterProperty); }
|
||||
set { SetValue(CenterProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the target value of a Center animation.
|
||||
/// </summary>
|
||||
public Point TargetCenter
|
||||
public Location TargetCenter
|
||||
{
|
||||
get { return (Point)GetValue(TargetCenterProperty); }
|
||||
get { return (Location)GetValue(TargetCenterProperty); }
|
||||
set { SetValue(TargetCenterProperty, value); }
|
||||
}
|
||||
|
||||
|
|
@ -262,7 +271,7 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the map scale at the Center point as view coordinate units (pixels) per meter.
|
||||
/// Gets the map scale at the Center location as viewport coordinate units (pixels) per meter.
|
||||
/// </summary>
|
||||
public double CenterScale
|
||||
{
|
||||
|
|
@ -271,34 +280,24 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transformation from world coordinates (latitude and longitude)
|
||||
/// to cartesian map coordinates.
|
||||
/// Gets the transformation from geographic coordinates to cartesian map coordinates.
|
||||
/// </summary>
|
||||
public MapTransform MapTransform
|
||||
{
|
||||
get { return mapViewTransform.MapTransform; }
|
||||
get { return mapTransform; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transformation from cartesian map coordinates to view coordinates.
|
||||
/// Gets the transformation from cartesian map coordinates to viewport coordinates.
|
||||
/// </summary>
|
||||
public Transform ViewTransform
|
||||
public Transform ViewportTransform
|
||||
{
|
||||
get { return mapViewTransform.ViewTransform; }
|
||||
get { return tileContainer.ViewportTransform; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the combination of MapTransform and ViewTransform, i.e. the transformation
|
||||
/// from longitude and latitude values to view coordinates.
|
||||
/// </summary>
|
||||
public GeneralTransform MapViewTransform
|
||||
{
|
||||
get { return mapViewTransform; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scaling transformation from meters to view coordinate units (pixels)
|
||||
/// at the view center point.
|
||||
/// Gets the scaling transformation from meters to viewport coordinate units (pixels)
|
||||
/// at the viewport center point.
|
||||
/// </summary>
|
||||
public Transform ScaleTransform
|
||||
{
|
||||
|
|
@ -322,57 +321,73 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets an intermittent origin point in world coordinates for scaling and rotation transformations.
|
||||
/// Transforms a geographic location to a viewport coordinates point.
|
||||
/// </summary>
|
||||
public Point LocationToViewportPoint(Location location)
|
||||
{
|
||||
return ViewportTransform.Transform(MapTransform.Transform(location));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a viewport coordinates point to a geographic location.
|
||||
/// </summary>
|
||||
public Location ViewportPointToLocation(Point point)
|
||||
{
|
||||
return MapTransform.TransformBack(ViewportTransform.Inverse.Transform(point));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a temporary origin location in geographic coordinates for scaling and rotation transformations.
|
||||
/// This origin location is automatically removed when the Center property is set by application code.
|
||||
/// </summary>
|
||||
public void SetTransformOrigin(Location origin)
|
||||
{
|
||||
transformOrigin = origin;
|
||||
viewportOrigin = LocationToViewportPoint(origin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a temporary origin point in viewport coordinates for scaling and rotation transformations.
|
||||
/// This origin point is automatically removed when the Center property is set by application code.
|
||||
/// </summary>
|
||||
public void SetTransformOrigin(Point origin)
|
||||
{
|
||||
transformOrigin = origin;
|
||||
viewOrigin = MapViewTransform.Transform(origin);
|
||||
viewportOrigin.X = Math.Min(Math.Max(origin.X, 0d), RenderSize.Width);
|
||||
viewportOrigin.Y = Math.Min(Math.Max(origin.Y, 0d), RenderSize.Height);
|
||||
transformOrigin = CoerceCenter(ViewportPointToLocation(viewportOrigin));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets an intermittent origin point in view coordinates for scaling and rotation transformations.
|
||||
/// This origin point is automatically removed when the Center property is set by application code.
|
||||
/// </summary>
|
||||
public void SetTransformViewOrigin(Point origin)
|
||||
{
|
||||
viewOrigin.X = Math.Min(Math.Max(origin.X, 0d), RenderSize.Width);
|
||||
viewOrigin.Y = Math.Min(Math.Max(origin.Y, 0d), RenderSize.Height);
|
||||
transformOrigin = CoerceCenter(MapViewTransform.Inverse.Transform(viewOrigin));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the intermittent transform origin point set by SetTransformOrigin.
|
||||
/// Removes the temporary transform origin point set by SetTransformOrigin.
|
||||
/// </summary>
|
||||
public void ResetTransformOrigin()
|
||||
{
|
||||
transformOrigin = null;
|
||||
viewOrigin = new Point(RenderSize.Width / 2d, RenderSize.Height / 2d);
|
||||
viewportOrigin = new Point(RenderSize.Width / 2d, RenderSize.Height / 2d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the Center property according to the specified translation in view coordinates.
|
||||
/// Changes the Center property according to the specified translation in viewport coordinates.
|
||||
/// </summary>
|
||||
public void TranslateMap(Vector translation)
|
||||
{
|
||||
if (translation.X != 0d || translation.Y != 0d)
|
||||
{
|
||||
ResetTransformOrigin();
|
||||
Center = MapViewTransform.Inverse.Transform(viewOrigin - translation);
|
||||
Center = ViewportPointToLocation(viewportOrigin - translation);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the Center, Heading and ZoomLevel properties according to the specified
|
||||
/// view coordinate translation, rotation and scale delta values. Rotation and scaling
|
||||
/// is performed relative to the specified origin point in view coordinates.
|
||||
/// viewport coordinate translation, rotation and scale delta values. Rotation and scaling
|
||||
/// is performed relative to the specified origin point in viewport coordinates.
|
||||
/// </summary>
|
||||
public void TransformMap(Point origin, Vector translation, double rotation, double scale)
|
||||
{
|
||||
if (rotation != 0d || scale != 1d)
|
||||
{
|
||||
SetTransformViewOrigin(origin);
|
||||
SetTransformOrigin(origin);
|
||||
updateTransform = false;
|
||||
Heading += rotation;
|
||||
ZoomLevel += Math.Log(scale, 2d);
|
||||
|
|
@ -385,21 +400,20 @@ namespace MapControl
|
|||
|
||||
/// <summary>
|
||||
/// Sets the value of the ZoomLevel property while retaining the specified origin point
|
||||
/// in view coordinates.
|
||||
/// in viewport coordinates.
|
||||
/// </summary>
|
||||
public void ZoomMap(Point origin, double zoomLevel)
|
||||
{
|
||||
SetTransformViewOrigin(origin);
|
||||
SetTransformOrigin(origin);
|
||||
TargetZoomLevel = zoomLevel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the map scale at the specified world coordinate point
|
||||
/// as view coordinate units (pixels) per meter.
|
||||
/// Gets the map scale at the specified location as viewport coordinate units (pixels) per meter.
|
||||
/// </summary>
|
||||
public double GetMapScale(Point point)
|
||||
public double GetMapScale(Location location)
|
||||
{
|
||||
return MapTransform.RelativeScale(point) * Math.Pow(2d, ZoomLevel) * 256d / (MeterPerDegree * 360d);
|
||||
return mapTransform.RelativeScale(location) * Math.Pow(2d, ZoomLevel) * 256d / (MeterPerDegree * 360d);
|
||||
}
|
||||
|
||||
protected override int VisualChildrenCount
|
||||
|
|
@ -477,40 +491,40 @@ namespace MapControl
|
|||
|
||||
private void SetTileLayers(TileLayerCollection tileLayers)
|
||||
{
|
||||
BaseTileLayer = tileLayers.Count > 0 ? tileLayers[0] : null;
|
||||
MainTileLayer = tileLayers.Count > 0 ? tileLayers[0] : null;
|
||||
tileContainer.TileLayers = tileLayers;
|
||||
}
|
||||
|
||||
private void SetBaseTileLayer(TileLayer baseTileLayer)
|
||||
private void SetMainTileLayer(TileLayer mainTileLayer)
|
||||
{
|
||||
if (baseTileLayer != null)
|
||||
if (mainTileLayer != null)
|
||||
{
|
||||
if (TileLayers == null)
|
||||
{
|
||||
TileLayers = new TileLayerCollection(baseTileLayer);
|
||||
TileLayers = new TileLayerCollection(mainTileLayer);
|
||||
}
|
||||
else if (TileLayers.Count == 0)
|
||||
{
|
||||
TileLayers.Add(baseTileLayer);
|
||||
TileLayers.Add(mainTileLayer);
|
||||
}
|
||||
else if (TileLayers[0] != baseTileLayer)
|
||||
else if (TileLayers[0] != mainTileLayer)
|
||||
{
|
||||
TileLayers[0] = baseTileLayer;
|
||||
TileLayers[0] = mainTileLayer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TileLayer CoerceBaseTileLayer(TileLayer baseTileLayer)
|
||||
private TileLayer CoerceMainTileLayer(TileLayer mainTileLayer)
|
||||
{
|
||||
if (baseTileLayer == null && TileLayers.Count > 0)
|
||||
if (mainTileLayer == null && TileLayers.Count > 0)
|
||||
{
|
||||
baseTileLayer = TileLayers[0];
|
||||
mainTileLayer = TileLayers[0];
|
||||
}
|
||||
|
||||
return baseTileLayer;
|
||||
return mainTileLayer;
|
||||
}
|
||||
|
||||
private void SetCenter(Point center)
|
||||
private void SetCenter(Location center)
|
||||
{
|
||||
if (updateTransform)
|
||||
{
|
||||
|
|
@ -524,7 +538,7 @@ namespace MapControl
|
|||
}
|
||||
}
|
||||
|
||||
private void SetTargetCenter(Point targetCenter)
|
||||
private void SetTargetCenter(Location targetCenter)
|
||||
{
|
||||
if (targetCenter != Center)
|
||||
{
|
||||
|
|
@ -533,7 +547,7 @@ namespace MapControl
|
|||
centerAnimation.Completed -= CenterAnimationCompleted;
|
||||
}
|
||||
|
||||
centerAnimation = new PointAnimation
|
||||
centerAnimation = new LocationAnimation
|
||||
{
|
||||
From = Center,
|
||||
To = targetCenter,
|
||||
|
|
@ -557,11 +571,11 @@ namespace MapControl
|
|||
centerAnimation = null;
|
||||
}
|
||||
|
||||
private Point CoerceCenter(Point point)
|
||||
private Location CoerceCenter(Location location)
|
||||
{
|
||||
point.X = ((point.X + 180d) % 360d + 360d) % 360d - 180d;
|
||||
point.Y = Math.Min(Math.Max(point.Y, -MapTransform.MaxLatitude), MapTransform.MaxLatitude);
|
||||
return point;
|
||||
location.Latitude = Math.Min(Math.Max(location.Latitude, -MapTransform.MaxLatitude), MapTransform.MaxLatitude);
|
||||
location.Longitude = ((location.Longitude + 180d) % 360d + 360d) % 360d - 180d;
|
||||
return location;
|
||||
}
|
||||
|
||||
private void SetZoomLevel(double zoomLevel)
|
||||
|
|
@ -682,16 +696,16 @@ namespace MapControl
|
|||
{
|
||||
double scale;
|
||||
|
||||
if (transformOrigin.HasValue)
|
||||
if (transformOrigin != null)
|
||||
{
|
||||
scale = tileContainer.SetTransform(ZoomLevel, Heading, MapTransform.Transform(transformOrigin.Value), viewOrigin, RenderSize);
|
||||
scale = tileContainer.SetTransform(ZoomLevel, Heading, MapTransform.Transform(transformOrigin), viewportOrigin, RenderSize);
|
||||
updateTransform = false;
|
||||
Center = MapViewTransform.Inverse.Transform(new Point(RenderSize.Width / 2d, RenderSize.Height / 2d));
|
||||
Center = ViewportPointToLocation(new Point(RenderSize.Width / 2d, RenderSize.Height / 2d));
|
||||
updateTransform = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
scale = tileContainer.SetTransform(ZoomLevel, Heading, MapTransform.Transform(Center), viewOrigin, RenderSize);
|
||||
scale = tileContainer.SetTransform(ZoomLevel, Heading, MapTransform.Transform(Center), viewportOrigin, RenderSize);
|
||||
}
|
||||
|
||||
scale *= MapTransform.RelativeScale(Center) / MeterPerDegree; // Pixels per meter at center latitude
|
||||
|
|
|
|||
|
|
@ -24,10 +24,11 @@
|
|||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<DefineConstants>
|
||||
</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
|
|
@ -42,21 +43,20 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="GlyphRunText.cs" />
|
||||
<Compile Include="Location.cs" />
|
||||
<Compile Include="LocationAnimation.cs" />
|
||||
<Compile Include="LocationCollection.cs" />
|
||||
<Compile Include="MapElement.cs" />
|
||||
<Compile Include="MapGraticule.cs" />
|
||||
<Compile Include="MapItem.cs" />
|
||||
<Compile Include="MapItemsControl.cs" />
|
||||
<Compile Include="MapPanel.cs" />
|
||||
<Compile Include="MapPath.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MapPathGeometry.cs" />
|
||||
<Compile Include="MapStreamGeometry.cs" />
|
||||
<Compile Include="MapPolygon.cs" />
|
||||
<Compile Include="MapPolyline.cs" />
|
||||
<Compile Include="TileImageLoader.cs" />
|
||||
<Compile Include="MapTransform.cs" />
|
||||
<Compile Include="Map.cs" />
|
||||
<Compile Include="MapInput.cs" />
|
||||
<Compile Include="MapViewTransform.cs" />
|
||||
<Compile Include="MercatorTransform.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Tile.cs" />
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
using System;
|
||||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Windows;
|
||||
|
||||
namespace MapControl
|
||||
|
|
@ -8,6 +12,9 @@ namespace MapControl
|
|||
void ParentMapChanged(Map oldParentMap, Map newParentMap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for child elements of a MapPanel.
|
||||
/// </summary>
|
||||
public abstract class MapElement : FrameworkElement, INotifyParentMapChanged
|
||||
{
|
||||
protected MapElement()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
using System;
|
||||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
|
@ -7,6 +11,10 @@ using System.Windows.Shapes;
|
|||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Draws a graticule overlay. The minimum spacing in pixels between adjacent graticule lines
|
||||
/// is specified by the MinSpacingPixels property.
|
||||
/// </summary>
|
||||
public class MapGraticule : MapElement
|
||||
{
|
||||
public static readonly DependencyProperty ForegroundProperty = Control.ForegroundProperty.AddOwner(
|
||||
|
|
@ -36,7 +44,7 @@ namespace MapControl
|
|||
public static readonly DependencyProperty MinSpacingPixelsProperty = DependencyProperty.Register(
|
||||
"MinSpacingPixels", typeof(double), typeof(MapGraticule), new FrameworkPropertyMetadata(100d));
|
||||
|
||||
public static double[] GridSpacings =
|
||||
public static double[] Spacings =
|
||||
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();
|
||||
|
|
@ -116,17 +124,19 @@ namespace MapControl
|
|||
|
||||
protected override void OnViewTransformChanged(Map parentMap)
|
||||
{
|
||||
Rect bounds = parentMap.MapViewTransform.Inverse.TransformBounds(new Rect(parentMap.RenderSize));
|
||||
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 = MinSpacingPixels * 360d / (Math.Pow(2d, parentMap.ZoomLevel) * 256d);
|
||||
double spacing = GridSpacings[GridSpacings.Length - 1];
|
||||
double spacing = Spacings[Spacings.Length - 1];
|
||||
|
||||
if (spacing >= minSpacing)
|
||||
{
|
||||
spacing = GridSpacings.FirstOrDefault(s => s >= minSpacing);
|
||||
spacing = Spacings.FirstOrDefault(s => s >= minSpacing);
|
||||
}
|
||||
|
||||
double longitudeStart = Math.Ceiling(bounds.Left / spacing) * spacing;
|
||||
double latitudeStart = Math.Ceiling(bounds.Top / spacing) * spacing;
|
||||
double latitudeStart = Math.Ceiling(loc1.Latitude / spacing) * spacing;
|
||||
double longitudeStart = Math.Ceiling(loc1.Longitude / spacing) * spacing;
|
||||
|
||||
if (pen.Brush == null)
|
||||
{
|
||||
|
|
@ -135,18 +145,18 @@ namespace MapControl
|
|||
|
||||
using (DrawingContext drawingContext = visual.RenderOpen())
|
||||
{
|
||||
for (double lon = longitudeStart; lon <= bounds.Right; lon += spacing)
|
||||
for (double lat = latitudeStart; lat <= loc2.Latitude; lat += spacing)
|
||||
{
|
||||
drawingContext.DrawLine(pen,
|
||||
parentMap.MapViewTransform.Transform(new Point(lon, bounds.Bottom)),
|
||||
parentMap.MapViewTransform.Transform(new Point(lon, bounds.Top)));
|
||||
parentMap.LocationToViewportPoint(new Location(lat, loc1.Longitude)),
|
||||
parentMap.LocationToViewportPoint(new Location(lat, loc2.Longitude)));
|
||||
}
|
||||
|
||||
for (double lat = latitudeStart; lat <= bounds.Bottom; lat += spacing)
|
||||
for (double lon = longitudeStart; lon <= loc2.Longitude; lon += spacing)
|
||||
{
|
||||
drawingContext.DrawLine(pen,
|
||||
parentMap.MapViewTransform.Transform(new Point(bounds.Left, lat)),
|
||||
parentMap.MapViewTransform.Transform(new Point(bounds.Right, lat)));
|
||||
parentMap.LocationToViewportPoint(new Location(loc1.Latitude, lon)),
|
||||
parentMap.LocationToViewportPoint(new Location(loc2.Latitude, lon)));
|
||||
}
|
||||
|
||||
if (Foreground != null && Foreground != Brushes.Transparent)
|
||||
|
|
@ -158,12 +168,12 @@ namespace MapControl
|
|||
typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
|
||||
}
|
||||
|
||||
for (double lon = longitudeStart; lon <= bounds.Right; lon += spacing)
|
||||
for (double lat = latitudeStart; lat <= loc2.Latitude; lat += spacing)
|
||||
{
|
||||
for (double lat = latitudeStart; lat <= bounds.Bottom; lat += spacing)
|
||||
for (double lon = longitudeStart; lon <= loc2.Longitude; lon += spacing)
|
||||
{
|
||||
double t = StrokeThickness / 2d;
|
||||
Point p = parentMap.MapViewTransform.Transform(new Point(lon, lat));
|
||||
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");
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
using System;
|
||||
// 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.Input;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
using System;
|
||||
// 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.Controls.Primitives;
|
||||
|
|
@ -7,22 +11,21 @@ using System.Windows.Media;
|
|||
|
||||
namespace MapControl
|
||||
{
|
||||
[TemplateVisualState(GroupName = "CommonStates", Name = "Normal")]
|
||||
[TemplateVisualState(GroupName = "CommonStates", Name = "Disabled")]
|
||||
[TemplateVisualState(GroupName = "CommonStates", Name = "MouseOver")]
|
||||
[TemplateVisualState(GroupName = "SelectionStates", Name = "Unselected")]
|
||||
[TemplateVisualState(GroupName = "SelectionStates", Name = "Selected")]
|
||||
[TemplateVisualState(GroupName = "CurrentStates", Name = "NonCurrent")]
|
||||
[TemplateVisualState(GroupName = "CurrentStates", Name = "Current")]
|
||||
/// <summary>
|
||||
/// Container class for an item in a MapItemsControl.
|
||||
/// </summary>
|
||||
[TemplateVisualState(Name = "Normal", GroupName = "CommonStates")]
|
||||
[TemplateVisualState(Name = "MouseOver", GroupName = "CommonStates")]
|
||||
[TemplateVisualState(Name = "Disabled", GroupName = "CommonStates")]
|
||||
[TemplateVisualState(Name = "Unselected", GroupName = "SelectionStates")]
|
||||
[TemplateVisualState(Name = "Selected", GroupName = "SelectionStates")]
|
||||
[TemplateVisualState(Name = "NotCurrent", GroupName = "CurrentStates")]
|
||||
[TemplateVisualState(Name = "Current", GroupName = "CurrentStates")]
|
||||
public class MapItem : ContentControl
|
||||
{
|
||||
public static readonly RoutedEvent SelectedEvent = ListBoxItem.SelectedEvent.AddOwner(typeof(MapItem));
|
||||
public static readonly RoutedEvent UnselectedEvent = ListBoxItem.UnselectedEvent.AddOwner(typeof(MapItem));
|
||||
|
||||
public static readonly DependencyProperty LocationProperty = MapPanel.LocationProperty.AddOwner(typeof(MapItem));
|
||||
public static readonly DependencyProperty ViewPositionProperty = MapPanel.ViewPositionProperty.AddOwner(typeof(MapItem));
|
||||
public static readonly DependencyProperty ViewPositionTransformProperty = MapPanel.ViewPositionTransformProperty.AddOwner(typeof(MapItem));
|
||||
|
||||
public static readonly DependencyProperty IsSelectedProperty = Selector.IsSelectedProperty.AddOwner(
|
||||
typeof(MapItem), new FrameworkPropertyMetadata((o, e) => ((MapItem)o).IsSelectedChanged((bool)e.NewValue)));
|
||||
|
||||
|
|
@ -31,13 +34,6 @@ namespace MapControl
|
|||
|
||||
public static readonly DependencyProperty IsCurrentProperty = IsCurrentPropertyKey.DependencyProperty;
|
||||
|
||||
private static readonly DependencyPropertyKey IsInsideMapBoundsPropertyKey = DependencyProperty.RegisterReadOnly(
|
||||
"IsInsideMapBounds", typeof(bool), typeof(MapItem), null);
|
||||
|
||||
public static readonly DependencyProperty IsInsideMapBoundsProperty = IsInsideMapBoundsPropertyKey.DependencyProperty;
|
||||
|
||||
private object item;
|
||||
|
||||
static MapItem()
|
||||
{
|
||||
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(typeof(MapItem),
|
||||
|
|
@ -45,9 +41,6 @@ namespace MapControl
|
|||
|
||||
UIElement.IsEnabledProperty.OverrideMetadata(typeof(MapItem),
|
||||
new FrameworkPropertyMetadata((o, e) => ((MapItem)o).CommonStateChanged()));
|
||||
|
||||
MapPanel.ViewPositionPropertyKey.OverrideMetadata(typeof(MapItem),
|
||||
new FrameworkPropertyMetadata((o, e) => ((MapItem)o).ViewPositionChanged((Point)e.NewValue)));
|
||||
}
|
||||
|
||||
public event RoutedEventHandler Selected
|
||||
|
|
@ -67,27 +60,6 @@ namespace MapControl
|
|||
get { return MapPanel.GetParentMap(this); }
|
||||
}
|
||||
|
||||
public Point? Location
|
||||
{
|
||||
get { return (Point?)GetValue(LocationProperty); }
|
||||
set { SetValue(LocationProperty, value); }
|
||||
}
|
||||
|
||||
public bool HasViewPosition
|
||||
{
|
||||
get { return ReadLocalValue(ViewPositionProperty) != DependencyProperty.UnsetValue; }
|
||||
}
|
||||
|
||||
public Point ViewPosition
|
||||
{
|
||||
get { return (Point)GetValue(ViewPositionProperty); }
|
||||
}
|
||||
|
||||
public Transform ViewPositionTransform
|
||||
{
|
||||
get { return (Transform)GetValue(ViewPositionTransformProperty); }
|
||||
}
|
||||
|
||||
public bool IsSelected
|
||||
{
|
||||
get { return (bool)GetValue(IsSelectedProperty); }
|
||||
|
|
@ -104,28 +76,12 @@ namespace MapControl
|
|||
SetValue(IsCurrentPropertyKey, value);
|
||||
int zIndex = Panel.GetZIndex(this);
|
||||
Panel.SetZIndex(this, value ? (zIndex | 0x40000000) : (zIndex & ~0x40000000));
|
||||
VisualStateManager.GoToState(this, value ? "Current" : "NonCurrent", true);
|
||||
VisualStateManager.GoToState(this, value ? "Current" : "NotCurrent", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsInsideMapBounds
|
||||
{
|
||||
get { return (bool)GetValue(IsInsideMapBoundsProperty); }
|
||||
}
|
||||
|
||||
public object Item
|
||||
{
|
||||
get { return item; }
|
||||
internal set
|
||||
{
|
||||
item = value;
|
||||
if (HasViewPosition)
|
||||
{
|
||||
ViewPositionChanged(ViewPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
public object Item { get; internal set; }
|
||||
|
||||
protected override void OnMouseEnter(MouseEventArgs e)
|
||||
{
|
||||
|
|
@ -159,24 +115,6 @@ namespace MapControl
|
|||
IsSelected = !IsSelected;
|
||||
}
|
||||
|
||||
protected virtual void OnViewPositionChanged(Point viewPosition)
|
||||
{
|
||||
}
|
||||
|
||||
private void ViewPositionChanged(Point viewPosition)
|
||||
{
|
||||
Map map = ParentMap;
|
||||
|
||||
if (map != null)
|
||||
{
|
||||
SetValue(IsInsideMapBoundsPropertyKey,
|
||||
viewPosition.X >= 0d && viewPosition.X <= map.ActualWidth &&
|
||||
viewPosition.Y >= 0d && viewPosition.Y <= map.ActualHeight);
|
||||
|
||||
OnViewPositionChanged(viewPosition);
|
||||
}
|
||||
}
|
||||
|
||||
private void CommonStateChanged()
|
||||
{
|
||||
if (!IsEnabled)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
using System;
|
||||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls.Primitives;
|
||||
|
|
@ -8,6 +12,10 @@ namespace MapControl
|
|||
{
|
||||
public enum MapItemSelectionMode { Single, Extended }
|
||||
|
||||
/// <summary>
|
||||
/// Manages a collection of selectable items on a Map. Uses MapItem as container for items
|
||||
/// and updates the MapItem.IsCurrent property when Items.CurrentItem changes.
|
||||
/// </summary>
|
||||
public class MapItemsControl : MultiSelector
|
||||
{
|
||||
public static readonly DependencyProperty SelectionModeProperty = DependencyProperty.Register(
|
||||
|
|
@ -114,7 +122,7 @@ namespace MapControl
|
|||
{
|
||||
MapItem mapItem = GetMapItem(item);
|
||||
|
||||
if (mapItem != null && mapItem.HasViewPosition && geometry.FillContains(mapItem.ViewPosition))
|
||||
if (mapItem != null && MapPanel.HasViewportPosition(mapItem) && geometry.FillContains(MapPanel.GetViewportPosition(mapItem)))
|
||||
{
|
||||
SelectedItems.Add(item);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,21 @@
|
|||
using System;
|
||||
// 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>
|
||||
/// Positions child elements on a Map. A child element's position is specified by the
|
||||
/// attached property Location, given as geographic location with latitude and longitude.
|
||||
/// The attached property ViewportPosition gets a child element's position in viewport
|
||||
/// coordinates. IsInsideMapBounds indicates if the viewport coordinates are located
|
||||
/// inside the visible part of the map.
|
||||
/// </summary>
|
||||
public class MapPanel : Panel, INotifyParentMapChanged
|
||||
{
|
||||
public static readonly DependencyProperty ParentMapProperty = DependencyProperty.RegisterAttached(
|
||||
|
|
@ -12,17 +23,17 @@ namespace MapControl
|
|||
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, ParentMapPropertyChanged));
|
||||
|
||||
public static readonly DependencyProperty LocationProperty = DependencyProperty.RegisterAttached(
|
||||
"Location", typeof(Point?), typeof(MapPanel),
|
||||
"Location", typeof(Location), typeof(MapPanel),
|
||||
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, LocationPropertyChanged));
|
||||
|
||||
internal static readonly DependencyPropertyKey ViewPositionPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
|
||||
"ViewPosition", typeof(Point), typeof(MapPanel), null);
|
||||
private static readonly DependencyPropertyKey ViewportPositionPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
|
||||
"ViewportPosition", typeof(Point), typeof(MapPanel), null);
|
||||
|
||||
private static readonly DependencyPropertyKey ViewPositionTransformPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
|
||||
"ViewPositionTransform", typeof(Transform), typeof(MapPanel), null);
|
||||
private static readonly DependencyPropertyKey IsInsideMapBoundsPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
|
||||
"IsInsideMapBounds", typeof(bool), typeof(MapPanel), null);
|
||||
|
||||
public static readonly DependencyProperty ViewPositionProperty = ViewPositionPropertyKey.DependencyProperty;
|
||||
public static readonly DependencyProperty ViewPositionTransformProperty = ViewPositionTransformPropertyKey.DependencyProperty;
|
||||
public static readonly DependencyProperty ViewportPositionProperty = ViewportPositionPropertyKey.DependencyProperty;
|
||||
public static readonly DependencyProperty IsInsideMapBoundsProperty = IsInsideMapBoundsPropertyKey.DependencyProperty;
|
||||
|
||||
public MapPanel()
|
||||
{
|
||||
|
|
@ -39,24 +50,29 @@ namespace MapControl
|
|||
return (Map)element.GetValue(ParentMapProperty);
|
||||
}
|
||||
|
||||
public static Point? GetLocation(UIElement element)
|
||||
public static Location GetLocation(UIElement element)
|
||||
{
|
||||
return (Point?)element.GetValue(LocationProperty);
|
||||
return (Location)element.GetValue(LocationProperty);
|
||||
}
|
||||
|
||||
public static void SetLocation(UIElement element, Point? value)
|
||||
public static void SetLocation(UIElement element, Location value)
|
||||
{
|
||||
element.SetValue(LocationProperty, value);
|
||||
}
|
||||
|
||||
public static Point GetViewPosition(UIElement element)
|
||||
public static bool HasViewportPosition(UIElement element)
|
||||
{
|
||||
return (Point)element.GetValue(ViewPositionProperty);
|
||||
return element.ReadLocalValue(ViewportPositionProperty) != DependencyProperty.UnsetValue;
|
||||
}
|
||||
|
||||
public static Transform GetViewPositionTransform(UIElement element)
|
||||
public static Point GetViewportPosition(UIElement element)
|
||||
{
|
||||
return (Transform)element.GetValue(ViewPositionTransformProperty);
|
||||
return (Point)element.GetValue(ViewportPositionProperty);
|
||||
}
|
||||
|
||||
public static bool GetIsInsideMapBounds(UIElement element)
|
||||
{
|
||||
return (bool)element.GetValue(IsInsideMapBoundsProperty);
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
|
|
@ -75,10 +91,10 @@ namespace MapControl
|
|||
{
|
||||
foreach (UIElement element in InternalChildren)
|
||||
{
|
||||
object viewPosition = element.ReadLocalValue(ViewPositionProperty);
|
||||
object viewportPosition = element.ReadLocalValue(ViewportPositionProperty);
|
||||
|
||||
if (viewPosition == DependencyProperty.UnsetValue ||
|
||||
!ArrangeElement(element, (Point)viewPosition))
|
||||
if (viewportPosition == DependencyProperty.UnsetValue ||
|
||||
!ArrangeElement(element, (Point)viewportPosition))
|
||||
{
|
||||
ArrangeElement(element, finalSize);
|
||||
}
|
||||
|
|
@ -91,11 +107,11 @@ namespace MapControl
|
|||
{
|
||||
foreach (UIElement element in InternalChildren)
|
||||
{
|
||||
Point? location = GetLocation(element);
|
||||
Location location = GetLocation(element);
|
||||
|
||||
if (location.HasValue)
|
||||
if (location != null)
|
||||
{
|
||||
SetViewPosition(element, parentMap, location);
|
||||
SetViewportPosition(element, parentMap, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -131,40 +147,31 @@ namespace MapControl
|
|||
private static void LocationPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs eventArgs)
|
||||
{
|
||||
UIElement element = (UIElement)obj;
|
||||
Point? location = (Point?)eventArgs.NewValue;
|
||||
Location location = (Location)eventArgs.NewValue;
|
||||
Map parentMap;
|
||||
|
||||
if (location.HasValue && (parentMap = Map.GetParentMap(element)) != null)
|
||||
if (location != null && (parentMap = GetParentMap(element)) != null)
|
||||
{
|
||||
SetViewPosition(element, parentMap, location);
|
||||
SetViewportPosition(element, parentMap, location);
|
||||
}
|
||||
else
|
||||
{
|
||||
element.ClearValue(ViewPositionPropertyKey);
|
||||
element.ClearValue(ViewPositionTransformPropertyKey);
|
||||
element.ClearValue(ViewportPositionPropertyKey);
|
||||
element.ClearValue(IsInsideMapBoundsPropertyKey);
|
||||
element.Arrange(new Rect());
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetViewPosition(UIElement element, Map parentMap, Point? location)
|
||||
private static void SetViewportPosition(UIElement element, Map parentMap, Location location)
|
||||
{
|
||||
Point viewPosition = parentMap.MapViewTransform.Transform(location.Value);
|
||||
Point viewportPosition = parentMap.LocationToViewportPoint(location);
|
||||
|
||||
element.SetValue(ViewPositionPropertyKey, viewPosition);
|
||||
element.SetValue(ViewportPositionPropertyKey, viewportPosition);
|
||||
element.SetValue(IsInsideMapBoundsPropertyKey,
|
||||
viewportPosition.X >= 0d && viewportPosition.X <= parentMap.ActualWidth &&
|
||||
viewportPosition.Y >= 0d && viewportPosition.Y <= parentMap.ActualHeight);
|
||||
|
||||
Matrix matrix = new Matrix(1d, 0d, 0d, 1d, viewPosition.X, viewPosition.Y);
|
||||
MatrixTransform viewTransform = element.GetValue(ViewPositionTransformProperty) as MatrixTransform;
|
||||
|
||||
if (viewTransform != null)
|
||||
{
|
||||
viewTransform.Matrix = matrix;
|
||||
}
|
||||
else
|
||||
{
|
||||
element.SetValue(ViewPositionTransformPropertyKey, new MatrixTransform(matrix));
|
||||
}
|
||||
|
||||
ArrangeElement(element, viewPosition);
|
||||
ArrangeElement(element, viewportPosition);
|
||||
}
|
||||
|
||||
private static bool ArrangeElement(UIElement element, Point position)
|
||||
|
|
|
|||
|
|
@ -1,155 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public static class MapPathGeometry
|
||||
{
|
||||
public static PathGeometry Transform(this GeneralTransform transform, Geometry geometry)
|
||||
{
|
||||
PathGeometry pathGeometry = geometry as PathGeometry;
|
||||
|
||||
if (pathGeometry == null)
|
||||
{
|
||||
pathGeometry = PathGeometry.CreateFromGeometry(geometry);
|
||||
}
|
||||
|
||||
if (geometry.Transform != null && geometry.Transform != System.Windows.Media.Transform.Identity)
|
||||
{
|
||||
GeneralTransformGroup transformGroup = new GeneralTransformGroup();
|
||||
transformGroup.Children.Add(geometry.Transform);
|
||||
transformGroup.Children.Add(transform);
|
||||
transform = transformGroup;
|
||||
}
|
||||
|
||||
return new PathGeometry(Transform(transform, pathGeometry.Figures),
|
||||
pathGeometry.FillRule, System.Windows.Media.Transform.Identity);
|
||||
}
|
||||
|
||||
public static PathFigureCollection Transform(this GeneralTransform transform, PathFigureCollection figures)
|
||||
{
|
||||
PathFigureCollection transformedFigures = new PathFigureCollection();
|
||||
|
||||
foreach (PathFigure figure in figures)
|
||||
{
|
||||
transformedFigures.Add(Transform(transform, figure));
|
||||
}
|
||||
|
||||
transformedFigures.Freeze();
|
||||
|
||||
return transformedFigures;
|
||||
}
|
||||
|
||||
public static PathFigure Transform(this GeneralTransform transform, PathFigure figure)
|
||||
{
|
||||
PathSegmentCollection transformedSegments = new PathSegmentCollection(figure.Segments.Count);
|
||||
|
||||
foreach (PathSegment segment in figure.Segments)
|
||||
{
|
||||
PathSegment transformedSegment = null;
|
||||
|
||||
if (segment is LineSegment)
|
||||
{
|
||||
LineSegment lineSegment = (LineSegment)segment;
|
||||
|
||||
transformedSegment = new LineSegment(
|
||||
transform.Transform(lineSegment.Point),
|
||||
lineSegment.IsStroked);
|
||||
}
|
||||
else if (segment is PolyLineSegment)
|
||||
{
|
||||
PolyLineSegment polyLineSegment = (PolyLineSegment)segment;
|
||||
|
||||
transformedSegment = new PolyLineSegment(
|
||||
polyLineSegment.Points.Select(transform.Transform),
|
||||
polyLineSegment.IsStroked);
|
||||
}
|
||||
else if (segment is ArcSegment)
|
||||
{
|
||||
ArcSegment arcSegment = (ArcSegment)segment;
|
||||
Size size = arcSegment.Size;
|
||||
MapTransform mapTransform = transform as MapTransform;
|
||||
|
||||
if (mapTransform != null)
|
||||
{
|
||||
double yScale = mapTransform.RelativeScale(arcSegment.Point);
|
||||
|
||||
if (arcSegment.RotationAngle == 0d)
|
||||
{
|
||||
size.Height *= yScale;
|
||||
}
|
||||
else
|
||||
{
|
||||
double sinR = Math.Sin(arcSegment.RotationAngle * Math.PI / 180d);
|
||||
double cosR = Math.Cos(arcSegment.RotationAngle * Math.PI / 180d);
|
||||
|
||||
size.Width *= Math.Sqrt(yScale * yScale * sinR * sinR + cosR * cosR);
|
||||
size.Height *= Math.Sqrt(yScale * yScale * cosR * cosR + sinR * sinR);
|
||||
}
|
||||
}
|
||||
|
||||
transformedSegment = new ArcSegment(
|
||||
transform.Transform(arcSegment.Point),
|
||||
size,
|
||||
arcSegment.RotationAngle,
|
||||
arcSegment.IsLargeArc,
|
||||
arcSegment.SweepDirection,
|
||||
arcSegment.IsStroked);
|
||||
}
|
||||
else if (segment is BezierSegment)
|
||||
{
|
||||
BezierSegment bezierSegment = (BezierSegment)segment;
|
||||
|
||||
transformedSegment = new BezierSegment(
|
||||
transform.Transform(bezierSegment.Point1),
|
||||
transform.Transform(bezierSegment.Point2),
|
||||
transform.Transform(bezierSegment.Point3),
|
||||
bezierSegment.IsStroked);
|
||||
}
|
||||
else if (segment is PolyBezierSegment)
|
||||
{
|
||||
PolyBezierSegment polyBezierSegment = (PolyBezierSegment)segment;
|
||||
|
||||
transformedSegment = new PolyBezierSegment(
|
||||
polyBezierSegment.Points.Select(transform.Transform),
|
||||
polyBezierSegment.IsStroked);
|
||||
}
|
||||
else if (segment is QuadraticBezierSegment)
|
||||
{
|
||||
QuadraticBezierSegment quadraticBezierSegment = (QuadraticBezierSegment)segment;
|
||||
|
||||
transformedSegment = new QuadraticBezierSegment(
|
||||
transform.Transform(quadraticBezierSegment.Point1),
|
||||
transform.Transform(quadraticBezierSegment.Point2),
|
||||
quadraticBezierSegment.IsStroked);
|
||||
}
|
||||
else if (segment is PolyQuadraticBezierSegment)
|
||||
{
|
||||
PolyQuadraticBezierSegment polyQuadraticBezierSegment = (PolyQuadraticBezierSegment)segment;
|
||||
|
||||
transformedSegment = new PolyQuadraticBezierSegment(
|
||||
polyQuadraticBezierSegment.Points.Select(transform.Transform),
|
||||
polyQuadraticBezierSegment.IsStroked);
|
||||
}
|
||||
|
||||
if (transformedSegment != null)
|
||||
{
|
||||
transformedSegment.IsSmoothJoin = segment.IsSmoothJoin;
|
||||
transformedSegments.Add(transformedSegment);
|
||||
}
|
||||
}
|
||||
|
||||
PathFigure transformedFigure = new PathFigure(
|
||||
transform.Transform(figure.StartPoint),
|
||||
transformedSegments,
|
||||
figure.IsClosed);
|
||||
|
||||
transformedFigure.IsFilled = figure.IsFilled;
|
||||
transformedFigure.Freeze();
|
||||
|
||||
return transformedFigure;
|
||||
}
|
||||
}
|
||||
}
|
||||
35
MapControl/MapPolygon.cs
Normal file
35
MapControl/MapPolygon.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// A closed map polygon, defined by a collection of geographic locations in the Locations property.
|
||||
/// </summary>
|
||||
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));
|
||||
|
||||
public MapPolygon()
|
||||
{
|
||||
drawing.Brush = Fill;
|
||||
}
|
||||
|
||||
public Brush Fill
|
||||
{
|
||||
get { return (Brush)GetValue(FillProperty); }
|
||||
set { SetValue(FillProperty, value); }
|
||||
}
|
||||
|
||||
protected override void UpdateGeometry()
|
||||
{
|
||||
UpdateGeometry(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +1,58 @@
|
|||
using System;
|
||||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public class MapPath : MapElement
|
||||
/// <summary>
|
||||
/// An open map polygon, defined by a collection of geographic locations in the Locations property.
|
||||
/// </summary>
|
||||
public class MapPolyline : MapElement
|
||||
{
|
||||
public static readonly DependencyProperty DataProperty = Path.DataProperty.AddOwner(
|
||||
typeof(MapPath), new FrameworkPropertyMetadata((o, e) => ((MapPath)o).UpdateGeometry()));
|
||||
|
||||
public static readonly DependencyProperty FillProperty = Shape.FillProperty.AddOwner(
|
||||
typeof(MapPath), new FrameworkPropertyMetadata((o, e) => ((MapPath)o).drawing.Brush = (Brush)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeProperty = Shape.StrokeProperty.AddOwner(
|
||||
typeof(MapPath), new FrameworkPropertyMetadata(Brushes.Black, (o, e) => ((MapPath)o).drawing.Pen.Brush = (Brush)e.NewValue));
|
||||
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(MapPath), new FrameworkPropertyMetadata((o, e) => ((MapPath)o).drawing.Pen.DashStyle = new DashStyle((DoubleCollection)e.NewValue, ((MapPath)o).StrokeDashOffset)));
|
||||
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(MapPath), new FrameworkPropertyMetadata((o, e) => ((MapPath)o).drawing.Pen.DashStyle = new DashStyle(((MapPath)o).StrokeDashArray, (double)e.NewValue)));
|
||||
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(MapPath), new FrameworkPropertyMetadata((o, e) => ((MapPath)o).drawing.Pen.DashCap = (PenLineCap)e.NewValue));
|
||||
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).drawing.Pen.DashCap = (PenLineCap)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeStartLineCapProperty = Shape.StrokeStartLineCapProperty.AddOwner(
|
||||
typeof(MapPath), new FrameworkPropertyMetadata((o, e) => ((MapPath)o).drawing.Pen.StartLineCap = (PenLineCap)e.NewValue));
|
||||
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).drawing.Pen.StartLineCap = (PenLineCap)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeEndLineCapProperty = Shape.StrokeEndLineCapProperty.AddOwner(
|
||||
typeof(MapPath), new FrameworkPropertyMetadata((o, e) => ((MapPath)o).drawing.Pen.EndLineCap = (PenLineCap)e.NewValue));
|
||||
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).drawing.Pen.EndLineCap = (PenLineCap)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeLineJoinProperty = Shape.StrokeLineJoinProperty.AddOwner(
|
||||
typeof(MapPath), new FrameworkPropertyMetadata((o, e) => ((MapPath)o).drawing.Pen.LineJoin = (PenLineJoin)e.NewValue));
|
||||
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).drawing.Pen.LineJoin = (PenLineJoin)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeMiterLimitProperty = Shape.StrokeMiterLimitProperty.AddOwner(
|
||||
typeof(MapPath), new FrameworkPropertyMetadata((o, e) => ((MapPath)o).drawing.Pen.MiterLimit = (double)e.NewValue));
|
||||
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).drawing.Pen.MiterLimit = (double)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeThicknessProperty = Shape.StrokeThicknessProperty.AddOwner(
|
||||
typeof(MapPath), new FrameworkPropertyMetadata((o, e) => ((MapPath)o).UpdatePenThickness()));
|
||||
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).UpdatePenThickness()));
|
||||
|
||||
public static readonly DependencyProperty TransformStrokeProperty = DependencyProperty.Register(
|
||||
"TransformStroke", typeof(bool), typeof(MapPath), new FrameworkPropertyMetadata((o, e) => ((MapPath)o).UpdatePenThickness()));
|
||||
"TransformStroke", typeof(bool), typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).UpdatePenThickness()));
|
||||
|
||||
private readonly DrawingVisual visual = new DrawingVisual();
|
||||
private readonly GeometryDrawing drawing = new GeometryDrawing();
|
||||
public static readonly DependencyProperty LocationsProperty = DependencyProperty.Register(
|
||||
"Locations", typeof(LocationCollection), typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).UpdateGeometry()));
|
||||
|
||||
public MapPath()
|
||||
protected readonly DrawingVisual visual = new DrawingVisual();
|
||||
protected readonly GeometryDrawing drawing = new GeometryDrawing();
|
||||
|
||||
public MapPolyline()
|
||||
{
|
||||
drawing.Brush = Fill;
|
||||
drawing.Pen = new Pen(Stroke, StrokeThickness);
|
||||
|
||||
using (DrawingContext drawingContext = visual.RenderOpen())
|
||||
|
|
@ -59,18 +63,6 @@ namespace MapControl
|
|||
Loaded += (o, e) => UpdateGeometry();
|
||||
}
|
||||
|
||||
public Geometry Data
|
||||
{
|
||||
get { return (Geometry)GetValue(DataProperty); }
|
||||
set { SetValue(DataProperty, value); }
|
||||
}
|
||||
|
||||
public Brush Fill
|
||||
{
|
||||
get { return (Brush)GetValue(FillProperty); }
|
||||
set { SetValue(FillProperty, value); }
|
||||
}
|
||||
|
||||
public Brush Stroke
|
||||
{
|
||||
get { return (Brush)GetValue(StrokeProperty); }
|
||||
|
|
@ -131,6 +123,12 @@ namespace MapControl
|
|||
set { SetValue(TransformStrokeProperty, value); }
|
||||
}
|
||||
|
||||
public LocationCollection Locations
|
||||
{
|
||||
get { return (LocationCollection)GetValue(LocationsProperty); }
|
||||
set { SetValue(LocationsProperty, value); }
|
||||
}
|
||||
|
||||
public double TransformedStrokeThickness
|
||||
{
|
||||
get { return drawing.Pen.Thickness; }
|
||||
|
|
@ -162,23 +160,26 @@ namespace MapControl
|
|||
{
|
||||
double scale = 1d;
|
||||
|
||||
if (TransformStroke && Data != null)
|
||||
if (TransformStroke)
|
||||
{
|
||||
Point center = Data.Bounds.Location + (Vector)Data.Bounds.Size / 2d;
|
||||
scale = parentMap.GetMapScale(center) * Map.MeterPerDegree;
|
||||
scale = parentMap.CenterScale * Map.MeterPerDegree;
|
||||
}
|
||||
|
||||
drawing.Pen.Thickness = scale * StrokeThickness;
|
||||
}
|
||||
|
||||
private void UpdateGeometry()
|
||||
protected virtual void UpdateGeometry()
|
||||
{
|
||||
UpdateGeometry(false);
|
||||
}
|
||||
|
||||
protected void UpdateGeometry(bool closed)
|
||||
{
|
||||
Map parentMap = MapPanel.GetParentMap(this);
|
||||
|
||||
if (parentMap != null && Data != null)
|
||||
if (parentMap != null && Locations != null && Locations.Count > 0)
|
||||
{
|
||||
drawing.Geometry = parentMap.MapTransform.Transform(Data);
|
||||
drawing.Geometry.Transform = parentMap.ViewTransform;
|
||||
drawing.Geometry = CreateGeometry(parentMap, Locations, closed);
|
||||
OnViewTransformChanged(parentMap);
|
||||
}
|
||||
else
|
||||
|
|
@ -196,5 +197,25 @@ namespace MapControl
|
|||
OnViewTransformChanged(parentMap);
|
||||
}
|
||||
}
|
||||
|
||||
private Geometry CreateGeometry(Map parentMap, LocationCollection locations, bool closed)
|
||||
{
|
||||
StreamGeometry geometry = new StreamGeometry
|
||||
{
|
||||
Transform = parentMap.ViewportTransform
|
||||
};
|
||||
|
||||
using (StreamGeometryContext sgc = geometry.Open())
|
||||
{
|
||||
sgc.BeginFigure(parentMap.MapTransform.Transform(locations.First()), closed, closed);
|
||||
|
||||
if (Locations.Count > 1)
|
||||
{
|
||||
sgc.PolyLineTo(parentMap.MapTransform.Transform(locations.Skip(1)), true, true);
|
||||
}
|
||||
}
|
||||
|
||||
return geometry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public static class MapStreamGeometry
|
||||
{
|
||||
public static MapStreamGeometryContext Open(this StreamGeometry mapGeometry, MapTransform transform)
|
||||
{
|
||||
return new MapStreamGeometryContext(mapGeometry.Open(), transform);
|
||||
}
|
||||
}
|
||||
|
||||
public class MapStreamGeometryContext : IDisposable
|
||||
{
|
||||
StreamGeometryContext context;
|
||||
MapTransform transform;
|
||||
|
||||
public MapStreamGeometryContext(StreamGeometryContext context, MapTransform transform)
|
||||
{
|
||||
this.context = context;
|
||||
this.transform = transform;
|
||||
}
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
context.Close();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
context.Close();
|
||||
}
|
||||
|
||||
public void BeginFigure(Point startPoint, bool isFilled, bool isClosed)
|
||||
{
|
||||
context.BeginFigure(transform.Transform(startPoint), isFilled, isClosed);
|
||||
}
|
||||
|
||||
public void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection, bool isStroked, bool isSmoothJoin)
|
||||
{
|
||||
double yScale = transform.RelativeScale(point);
|
||||
|
||||
if (rotationAngle == 0d)
|
||||
{
|
||||
size.Height *= yScale;
|
||||
}
|
||||
else
|
||||
{
|
||||
double sinR = Math.Sin(rotationAngle * Math.PI / 180d);
|
||||
double cosR = Math.Cos(rotationAngle * Math.PI / 180d);
|
||||
|
||||
size.Width *= Math.Sqrt(yScale * yScale * sinR * sinR + cosR * cosR);
|
||||
size.Height *= Math.Sqrt(yScale * yScale * cosR * cosR + sinR * sinR);
|
||||
}
|
||||
|
||||
context.ArcTo(transform.Transform(point), size, rotationAngle, isLargeArc, sweepDirection, isStroked, isSmoothJoin);
|
||||
}
|
||||
|
||||
public void LineTo(Point point, bool isStroked, bool isSmoothJoin)
|
||||
{
|
||||
context.LineTo(transform.Transform(point), isStroked, isSmoothJoin);
|
||||
}
|
||||
|
||||
public void QuadraticBezierTo(Point point1, Point point2, bool isStroked, bool isSmoothJoin)
|
||||
{
|
||||
context.QuadraticBezierTo(transform.Transform(point1), transform.Transform(point2), isStroked, isSmoothJoin);
|
||||
}
|
||||
|
||||
public void BezierTo(Point point1, Point point2, Point point3, bool isStroked, bool isSmoothJoin)
|
||||
{
|
||||
context.BezierTo(transform.Transform(point1), transform.Transform(point2), transform.Transform(point3), isStroked, isSmoothJoin);
|
||||
}
|
||||
|
||||
public void PolyLineTo(IList<Point> points, bool isStroked, bool isSmoothJoin)
|
||||
{
|
||||
context.PolyLineTo(TransformPoints(points), isStroked, isSmoothJoin);
|
||||
}
|
||||
|
||||
public void PolyQuadraticBezierTo(IList<Point> points, bool isStroked, bool isSmoothJoin)
|
||||
{
|
||||
context.PolyQuadraticBezierTo(TransformPoints(points), isStroked, isSmoothJoin);
|
||||
}
|
||||
|
||||
public void PolyBezierTo(IList<Point> points, bool isStroked, bool isSmoothJoin)
|
||||
{
|
||||
context.PolyBezierTo(TransformPoints(points), isStroked, isSmoothJoin);
|
||||
}
|
||||
|
||||
private IList<Point> TransformPoints(IList<Point> points)
|
||||
{
|
||||
Point[] transformedPoints = new Point[points.Count];
|
||||
|
||||
for (int i = 0; i < transformedPoints.Length; i++)
|
||||
{
|
||||
transformedPoints[i] = transform.Transform(points[i]);
|
||||
}
|
||||
|
||||
return transformedPoints;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,10 @@
|
|||
using System;
|
||||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
|
|
@ -9,7 +15,7 @@ namespace MapControl
|
|||
/// are transformed to cartesian coordinates with origin at latitude = 0 and longitude = 0.
|
||||
/// Longitude values are transformed identically to x values in the interval [-180 .. 180].
|
||||
/// </summary>
|
||||
public abstract class MapTransform : GeneralTransform
|
||||
public abstract class MapTransform
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the absolute value of the minimum and maximum latitude that can be transformed.
|
||||
|
|
@ -17,9 +23,35 @@ namespace MapControl
|
|||
public abstract double MaxLatitude { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the point scale factor of the map projection at the specified point
|
||||
/// relative to the point (0, 0).
|
||||
/// Gets the scale factor of the map projection at the specified geographic location
|
||||
/// relative to the scale at latitude zero.
|
||||
/// </summary>
|
||||
public abstract double RelativeScale(Point point);
|
||||
public abstract double RelativeScale(Location location);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a geographic location to a cartesian coordinate point.
|
||||
/// </summary>
|
||||
public abstract Point Transform(Location location);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a cartesian coordinate point to a geographic location.
|
||||
/// </summary>
|
||||
public abstract Location TransformBack(Point point);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a collection of geographic locations to a collection of cartesian coordinate points.
|
||||
/// </summary>
|
||||
public PointCollection Transform(IEnumerable<Location> locations)
|
||||
{
|
||||
return new PointCollection(locations.Select(l => Transform(l)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a collection of cartesian coordinate points to a collections of geographic location.
|
||||
/// </summary>
|
||||
public LocationCollection TransformBack(IEnumerable<Point> points)
|
||||
{
|
||||
return new LocationCollection(points.Select(p => TransformBack(p)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public class MapViewTransform : GeneralTransform
|
||||
{
|
||||
private readonly GeneralTransform inverse;
|
||||
|
||||
public MapViewTransform()
|
||||
{
|
||||
MapTransform = new MercatorTransform();
|
||||
inverse = new InverseMapViewTransform(this);
|
||||
}
|
||||
|
||||
public MapTransform MapTransform { get; set; }
|
||||
public Transform ViewTransform { get; set; }
|
||||
|
||||
public override GeneralTransform Inverse
|
||||
{
|
||||
get { return inverse; }
|
||||
}
|
||||
|
||||
public override bool TryTransform(Point point, out Point result)
|
||||
{
|
||||
result = ViewTransform.Transform(MapTransform.Transform(point));
|
||||
return true;
|
||||
}
|
||||
|
||||
public override Rect TransformBounds(Rect rect)
|
||||
{
|
||||
return ViewTransform.TransformBounds(MapTransform.TransformBounds(rect));
|
||||
}
|
||||
|
||||
protected override Freezable CreateInstanceCore()
|
||||
{
|
||||
return new MapViewTransform();
|
||||
}
|
||||
}
|
||||
|
||||
internal class InverseMapViewTransform : GeneralTransform
|
||||
{
|
||||
private readonly MapViewTransform inverse;
|
||||
|
||||
public InverseMapViewTransform(MapViewTransform inverse)
|
||||
{
|
||||
this.inverse = inverse;
|
||||
}
|
||||
|
||||
public override GeneralTransform Inverse
|
||||
{
|
||||
get { return inverse; }
|
||||
}
|
||||
|
||||
public override bool TryTransform(Point point, out Point result)
|
||||
{
|
||||
result = inverse.MapTransform.Inverse.Transform(inverse.ViewTransform.Inverse.Transform(point));
|
||||
return true;
|
||||
}
|
||||
|
||||
public override Rect TransformBounds(Rect rect)
|
||||
{
|
||||
return inverse.MapTransform.Inverse.TransformBounds(inverse.ViewTransform.Inverse.TransformBounds(rect));
|
||||
}
|
||||
|
||||
protected override Freezable CreateInstanceCore()
|
||||
{
|
||||
return new InverseMapViewTransform(inverse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,8 @@
|
|||
using System;
|
||||
// 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.Media;
|
||||
|
||||
|
|
@ -10,101 +14,50 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public class MercatorTransform : MapTransform
|
||||
{
|
||||
private GeneralTransform inverse = new InverseMercatorTransform();
|
||||
|
||||
public MercatorTransform()
|
||||
{
|
||||
Freeze();
|
||||
}
|
||||
|
||||
public override GeneralTransform Inverse
|
||||
{
|
||||
get { return inverse; }
|
||||
}
|
||||
|
||||
public override double MaxLatitude
|
||||
{
|
||||
get { return 85.0511; }
|
||||
}
|
||||
|
||||
public override double RelativeScale(Point point)
|
||||
public override double RelativeScale(Location location)
|
||||
{
|
||||
if (point.Y <= -90d)
|
||||
if (location.Latitude <= -90d)
|
||||
{
|
||||
return double.NegativeInfinity;
|
||||
}
|
||||
|
||||
if (point.Y >= 90d)
|
||||
if (location.Latitude >= 90d)
|
||||
{
|
||||
return double.PositiveInfinity;
|
||||
}
|
||||
|
||||
return 1d / Math.Cos(point.Y * Math.PI / 180d);
|
||||
return 1d / Math.Cos(location.Latitude * Math.PI / 180d);
|
||||
}
|
||||
|
||||
public override bool TryTransform(Point point, out Point result)
|
||||
public override Point Transform(Location location)
|
||||
{
|
||||
result = point;
|
||||
Point result = new Point(location.Longitude, 0d);
|
||||
|
||||
if (point.Y <= -90d)
|
||||
if (location.Latitude <= -90d)
|
||||
{
|
||||
result.Y = double.NegativeInfinity;
|
||||
}
|
||||
else if (point.Y >= 90d)
|
||||
else if (location.Latitude >= 90d)
|
||||
{
|
||||
result.Y = double.PositiveInfinity;
|
||||
}
|
||||
else
|
||||
{
|
||||
double lat = point.Y * Math.PI / 180d;
|
||||
double lat = location.Latitude * Math.PI / 180d;
|
||||
result.Y = (Math.Log(Math.Tan(lat) + 1d / Math.Cos(lat))) / Math.PI * 180d;
|
||||
}
|
||||
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
||||
public override Rect TransformBounds(Rect rect)
|
||||
public override Location TransformBack(Point point)
|
||||
{
|
||||
return new Rect(Transform(rect.TopLeft), Transform(rect.BottomRight));
|
||||
}
|
||||
|
||||
protected override Freezable CreateInstanceCore()
|
||||
{
|
||||
return new MercatorTransform();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms cartesian coordinates to latitude and longitude values in degrees
|
||||
/// according to the inverse Mercator transform.
|
||||
/// </summary>
|
||||
public class InverseMercatorTransform : GeneralTransform
|
||||
{
|
||||
public InverseMercatorTransform()
|
||||
{
|
||||
Freeze();
|
||||
}
|
||||
|
||||
public override GeneralTransform Inverse
|
||||
{
|
||||
get { return new MercatorTransform(); }
|
||||
}
|
||||
|
||||
public override bool TryTransform(Point point, out Point result)
|
||||
{
|
||||
result = point;
|
||||
result.Y = Math.Atan(Math.Sinh(point.Y * Math.PI / 180d)) / Math.PI * 180d;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override Rect TransformBounds(Rect rect)
|
||||
{
|
||||
return new Rect(Transform(rect.TopLeft), Transform(rect.BottomRight));
|
||||
}
|
||||
|
||||
protected override Freezable CreateInstanceCore()
|
||||
{
|
||||
return new InverseMercatorTransform();
|
||||
return new Location(Math.Atan(Math.Sinh(point.Y * Math.PI / 180d)) / Math.PI * 180d, point.X);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ using System.Windows;
|
|||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("MapControl")]
|
||||
[assembly: AssemblyDescription("MapControl")]
|
||||
[assembly: AssemblyTitle("Map Control")]
|
||||
[assembly: AssemblyDescription("WPF Map Control")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyProduct("MapControl")]
|
||||
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012")]
|
||||
[assembly: AssemblyProduct("WPF Map Control")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2012 Clemens Fischer")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
using System;
|
||||
// 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.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
using System;
|
||||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Windows;
|
||||
|
|
@ -22,7 +26,7 @@ namespace MapControl
|
|||
private Int32Rect tileGrid;
|
||||
private TileLayerCollection tileLayers;
|
||||
private readonly DispatcherTimer updateTimer;
|
||||
private readonly MatrixTransform viewTransform = new MatrixTransform();
|
||||
private readonly MatrixTransform viewportTransform = new MatrixTransform();
|
||||
|
||||
public TileContainer()
|
||||
{
|
||||
|
|
@ -52,17 +56,17 @@ namespace MapControl
|
|||
}
|
||||
}
|
||||
|
||||
public Transform ViewTransform
|
||||
public Transform ViewportTransform
|
||||
{
|
||||
get { return viewTransform; }
|
||||
get { return viewportTransform; }
|
||||
}
|
||||
|
||||
public double SetTransform(double mapZoomLevel, double mapRotation, Point mapOrigin, Point viewOrigin, Size viewSize)
|
||||
public double SetTransform(double mapZoomLevel, double mapRotation, Point mapOrigin, Point viewportOrigin, Size viewportSize)
|
||||
{
|
||||
zoomLevel = mapZoomLevel;
|
||||
rotation = mapRotation;
|
||||
size = viewSize;
|
||||
origin = viewOrigin;
|
||||
size = viewportSize;
|
||||
origin = viewportOrigin;
|
||||
|
||||
double scale = Math.Pow(2d, zoomLevel) * 256d / 360d;
|
||||
offset.X = origin.X - (180d + mapOrigin.X) * scale;
|
||||
|
|
@ -72,7 +76,7 @@ namespace MapControl
|
|||
transform.Scale(scale, scale);
|
||||
transform.Translate(offset.X, offset.Y);
|
||||
transform.RotateAt(rotation, origin.X, origin.Y);
|
||||
viewTransform.Matrix = transform;
|
||||
viewportTransform.Matrix = transform;
|
||||
|
||||
transform = GetVisualTransform();
|
||||
|
||||
|
|
@ -110,7 +114,7 @@ namespace MapControl
|
|||
int zoom = (int)Math.Floor(zoomLevel + 1d - zoomLevelSwitchOffset);
|
||||
int numTiles = 1 << zoom;
|
||||
double mapToTileScale = (double)numTiles / 360d;
|
||||
Matrix transform = viewTransform.Matrix;
|
||||
Matrix transform = viewportTransform.Matrix;
|
||||
transform.Invert(); // view to map coordinates
|
||||
transform.Translate(180d, -180d);
|
||||
transform.Scale(mapToTileScale, -mapToTileScale); // map coordinates to tile indices
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
using System;
|
||||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
|
@ -10,9 +14,13 @@ using System.Windows.Threading;
|
|||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads map tiles by their URIs and optionally caches their image files in a folder
|
||||
/// defined by the static TileCacheFolder property.
|
||||
/// </summary>
|
||||
public class TileImageLoader : DispatcherObject
|
||||
{
|
||||
public static string TileCacheDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MapCache");
|
||||
public static string TileCacheFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MapControl Cache");
|
||||
public static TimeSpan TileCacheExpiryAge = TimeSpan.FromDays(1);
|
||||
|
||||
private readonly Queue<Tile> pendingTiles = new Queue<Tile>();
|
||||
|
|
@ -45,7 +53,7 @@ namespace MapControl
|
|||
|
||||
lock (pendingTiles)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(TileCacheDirectory) &&
|
||||
if (!string.IsNullOrEmpty(TileCacheFolder) &&
|
||||
!string.IsNullOrEmpty(TileLayerName))
|
||||
{
|
||||
List<Tile> expiredTiles = new List<Tile>(newTiles.Count);
|
||||
|
|
@ -174,7 +182,7 @@ namespace MapControl
|
|||
|
||||
string tilePath;
|
||||
|
||||
if (!string.IsNullOrEmpty(TileCacheDirectory) &&
|
||||
if (!string.IsNullOrEmpty(TileCacheFolder) &&
|
||||
!string.IsNullOrEmpty(TileLayerName) &&
|
||||
(tilePath = TilePath(tile, decoder)) != null)
|
||||
{
|
||||
|
|
@ -213,7 +221,7 @@ namespace MapControl
|
|||
|
||||
private string TileDirectory(Tile tile)
|
||||
{
|
||||
return Path.Combine(TileCacheDirectory, TileLayerName, tile.ZoomLevel.ToString(), tile.XIndex.ToString());
|
||||
return Path.Combine(TileCacheFolder, TileLayerName, tile.ZoomLevel.ToString(), tile.XIndex.ToString());
|
||||
}
|
||||
|
||||
private string TilePath(Tile tile, BitmapDecoder decoder)
|
||||
|
|
@ -255,7 +263,7 @@ namespace MapControl
|
|||
|
||||
private static void TraceInformation(string format, params object[] args)
|
||||
{
|
||||
#if DEBUG
|
||||
#if TRACE
|
||||
System.Diagnostics.Trace.TraceInformation("[{0:00}] {1}", Thread.CurrentThread.ManagedThreadId, string.Format(format, args));
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
using System;
|
||||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
|
|
@ -7,11 +11,17 @@ using System.Windows.Media;
|
|||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Fills a rectangular area with map tiles from a TileSource. If the IsCached property is true,
|
||||
/// map tiles are cached in a folder defined by the TileImageLoader.TileCacheFolder property.
|
||||
/// </summary>
|
||||
[ContentProperty("TileSource")]
|
||||
public class TileLayer : DrawingVisual
|
||||
{
|
||||
private readonly TileImageLoader tileImageLoader = new TileImageLoader();
|
||||
private readonly List<Tile> tiles = new List<Tile>();
|
||||
private bool isCached = false;
|
||||
private string name = string.Empty;
|
||||
private string description = string.Empty;
|
||||
private Int32Rect grid;
|
||||
private int zoomLevel;
|
||||
|
|
@ -36,10 +46,24 @@ namespace MapControl
|
|||
set { tileImageLoader.MaxDownloads = value; }
|
||||
}
|
||||
|
||||
public bool IsCached
|
||||
{
|
||||
get { return isCached; }
|
||||
set
|
||||
{
|
||||
isCached = value;
|
||||
tileImageLoader.TileLayerName = isCached ? name : null;
|
||||
}
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return tileImageLoader.TileLayerName; }
|
||||
set { tileImageLoader.TileLayerName = value; }
|
||||
get { return name; }
|
||||
set
|
||||
{
|
||||
name = value;
|
||||
tileImageLoader.TileLayerName = isCached ? name : null;
|
||||
}
|
||||
}
|
||||
|
||||
public string Description
|
||||
|
|
@ -136,7 +160,7 @@ namespace MapControl
|
|||
|
||||
tiles.Sort((t1, t2) => t1.ZoomLevel - t2.ZoomLevel);
|
||||
|
||||
System.Diagnostics.Trace.TraceInformation("{0} Tiles: {1}", tiles.Count, string.Join(", ", tiles.Select(t => t.ZoomLevel.ToString())));
|
||||
//System.Diagnostics.Trace.TraceInformation("{0} Tiles: {1}", tiles.Count, string.Join(", ", tiles.Select(t => t.ZoomLevel.ToString())));
|
||||
}
|
||||
|
||||
private void RenderTiles()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
using System;
|
||||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows.Media;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
using System;
|
||||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
|
@ -6,6 +10,9 @@ using System.Windows;
|
|||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the URI of a map tile.
|
||||
/// </summary>
|
||||
[TypeConverter(typeof(TileSourceTypeConverter))]
|
||||
public class TileSource
|
||||
{
|
||||
|
|
@ -90,20 +97,20 @@ namespace MapControl
|
|||
{
|
||||
public override Uri GetUri(int x, int y, int zoomLevel)
|
||||
{
|
||||
InverseMercatorTransform t = new InverseMercatorTransform();
|
||||
MercatorTransform t = new MercatorTransform();
|
||||
double n = 1 << zoomLevel;
|
||||
double x1 = (double)x * 360d / n - 180d;
|
||||
double x2 = (double)(x + 1) * 360d / n - 180d;
|
||||
double y1 = 180d - (double)(y + 1) * 360d / n;
|
||||
double y2 = 180d - (double)y * 360d / n;
|
||||
Point p1 = t.Transform(new Point(x1, y1));
|
||||
Point p2 = t.Transform(new Point(x2, y2));
|
||||
Location p1 = t.TransformBack(new Point(x1, y1));
|
||||
Location p2 = t.TransformBack(new Point(x2, y2));
|
||||
|
||||
return new Uri(UriFormat.
|
||||
Replace("{w}", p1.X.ToString(CultureInfo.InvariantCulture)).
|
||||
Replace("{s}", p1.Y.ToString(CultureInfo.InvariantCulture)).
|
||||
Replace("{e}", p2.X.ToString(CultureInfo.InvariantCulture)).
|
||||
Replace("{n}", p2.Y.ToString(CultureInfo.InvariantCulture)));
|
||||
Replace("{w}", p1.Longitude.ToString(CultureInfo.InvariantCulture)).
|
||||
Replace("{s}", p1.Latitude.ToString(CultureInfo.InvariantCulture)).
|
||||
Replace("{e}", p2.Longitude.ToString(CultureInfo.InvariantCulture)).
|
||||
Replace("{n}", p2.Latitude.ToString(CultureInfo.InvariantCulture)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue