diff --git a/MapControl.sln b/MapControl.sln
index 838e0c8f..a4c6eb04 100644
--- a/MapControl.sln
+++ b/MapControl.sln
@@ -3,6 +3,8 @@ Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapControl", "MapControl\MapControl.csproj", "{06481252-2310-414A-B9FC-D5739FDF6BD3}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApplication", "TestApplication\TestApplication.csproj", "{A3F251EE-A1A0-4752-8C3D-C6BACBFB2AF2}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -23,6 +25,16 @@ Global
{06481252-2310-414A-B9FC-D5739FDF6BD3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{06481252-2310-414A-B9FC-D5739FDF6BD3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{06481252-2310-414A-B9FC-D5739FDF6BD3}.Release|x86.ActiveCfg = Release|Any CPU
+ {A3F251EE-A1A0-4752-8C3D-C6BACBFB2AF2}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {A3F251EE-A1A0-4752-8C3D-C6BACBFB2AF2}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+ {A3F251EE-A1A0-4752-8C3D-C6BACBFB2AF2}.Debug|Mixed Platforms.Build.0 = Debug|x86
+ {A3F251EE-A1A0-4752-8C3D-C6BACBFB2AF2}.Debug|x86.ActiveCfg = Debug|x86
+ {A3F251EE-A1A0-4752-8C3D-C6BACBFB2AF2}.Debug|x86.Build.0 = Debug|x86
+ {A3F251EE-A1A0-4752-8C3D-C6BACBFB2AF2}.Release|Any CPU.ActiveCfg = Release|x86
+ {A3F251EE-A1A0-4752-8C3D-C6BACBFB2AF2}.Release|Mixed Platforms.ActiveCfg = Release|x86
+ {A3F251EE-A1A0-4752-8C3D-C6BACBFB2AF2}.Release|Mixed Platforms.Build.0 = Release|x86
+ {A3F251EE-A1A0-4752-8C3D-C6BACBFB2AF2}.Release|x86.ActiveCfg = Release|x86
+ {A3F251EE-A1A0-4752-8C3D-C6BACBFB2AF2}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/MapControl/GlyphRunText.cs b/MapControl/GlyphRunText.cs
index 641f2d4b..0822dc43 100644
--- a/MapControl/GlyphRunText.cs
+++ b/MapControl/GlyphRunText.cs
@@ -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
{
+ ///
+ /// Contains helper methods for creating GlyphRun objects.
+ ///
public static class GlyphRunText
{
public static GlyphRun Create(string text, Typeface typeface, double emSize, Point baselineOrigin)
diff --git a/MapControl/Location.cs b/MapControl/Location.cs
new file mode 100644
index 00000000..ed2ccb56
--- /dev/null
+++ b/MapControl/Location.cs
@@ -0,0 +1,51 @@
+using System;
+using System.ComponentModel;
+using System.Globalization;
+using System.Windows;
+
+namespace MapControl
+{
+ ///
+ /// A geographic location given as latitude and longitude.
+ ///
+ [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);
+ }
+ }
+}
diff --git a/MapControl/LocationAnimation.cs b/MapControl/LocationAnimation.cs
new file mode 100644
index 00000000..9f7178e4
--- /dev/null
+++ b/MapControl/LocationAnimation.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Windows;
+using System.Windows.Media.Animation;
+
+namespace MapControl
+{
+ ///
+ /// Animates the value of a Location property between two values.
+ ///
+ 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);
+ }
+ }
+}
diff --git a/MapControl/LocationCollection.cs b/MapControl/LocationCollection.cs
new file mode 100644
index 00000000..3e5a921a
--- /dev/null
+++ b/MapControl/LocationCollection.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Globalization;
+
+namespace MapControl
+{
+ ///
+ /// A collection of geographic locations.
+ ///
+ [TypeConverter(typeof(LocationCollectionConverter))]
+ public class LocationCollection : ObservableCollection
+ {
+ public LocationCollection()
+ {
+ }
+
+ public LocationCollection(IEnumerable 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);
+ }
+ }
+}
diff --git a/MapControl/Map.cs b/MapControl/Map.cs
index bf511993..e9339e20 100644
--- a/MapControl/Map.cs
+++ b/MapControl/Map.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.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
@@ -7,6 +11,12 @@ using System.Windows.Media.Animation;
namespace MapControl
{
+ ///
+ /// 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.
+ ///
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
}
///
- /// 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].
///
- public TileLayer BaseTileLayer
+ public TileLayer MainTileLayer
{
- get { return (TileLayer)GetValue(BaseTileLayerProperty); }
- set { SetValue(BaseTileLayerProperty, value); }
+ get { return (TileLayer)GetValue(MainTileLayerProperty); }
+ set { SetValue(MainTileLayerProperty, value); }
}
///
@@ -208,20 +217,20 @@ namespace MapControl
}
///
- /// 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.
///
- public Point Center
+ public Location Center
{
- get { return (Point)GetValue(CenterProperty); }
+ get { return (Location)GetValue(CenterProperty); }
set { SetValue(CenterProperty, value); }
}
///
/// Gets or sets the target value of a Center animation.
///
- 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
}
///
- /// 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.
///
public double CenterScale
{
@@ -271,34 +280,24 @@ namespace MapControl
}
///
- /// Gets the transformation from world coordinates (latitude and longitude)
- /// to cartesian map coordinates.
+ /// Gets the transformation from geographic coordinates to cartesian map coordinates.
///
public MapTransform MapTransform
{
- get { return mapViewTransform.MapTransform; }
+ get { return mapTransform; }
}
///
- /// Gets the transformation from cartesian map coordinates to view coordinates.
+ /// Gets the transformation from cartesian map coordinates to viewport coordinates.
///
- public Transform ViewTransform
+ public Transform ViewportTransform
{
- get { return mapViewTransform.ViewTransform; }
+ get { return tileContainer.ViewportTransform; }
}
///
- /// Gets the combination of MapTransform and ViewTransform, i.e. the transformation
- /// from longitude and latitude values to view coordinates.
- ///
- public GeneralTransform MapViewTransform
- {
- get { return mapViewTransform; }
- }
-
- ///
- /// 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.
///
public Transform ScaleTransform
{
@@ -322,57 +321,73 @@ namespace MapControl
}
///
- /// Sets an intermittent origin point in world coordinates for scaling and rotation transformations.
+ /// Transforms a geographic location to a viewport coordinates point.
+ ///
+ public Point LocationToViewportPoint(Location location)
+ {
+ return ViewportTransform.Transform(MapTransform.Transform(location));
+ }
+
+ ///
+ /// Transforms a viewport coordinates point to a geographic location.
+ ///
+ public Location ViewportPointToLocation(Point point)
+ {
+ return MapTransform.TransformBack(ViewportTransform.Inverse.Transform(point));
+ }
+
+ ///
+ /// 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.
+ ///
+ public void SetTransformOrigin(Location origin)
+ {
+ transformOrigin = origin;
+ viewportOrigin = LocationToViewportPoint(origin);
+ }
+
+ ///
+ /// 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.
///
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));
}
///
- /// 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.
- ///
- 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));
- }
-
- ///
- /// Removes the intermittent transform origin point set by SetTransformOrigin.
+ /// Removes the temporary transform origin point set by SetTransformOrigin.
///
public void ResetTransformOrigin()
{
transformOrigin = null;
- viewOrigin = new Point(RenderSize.Width / 2d, RenderSize.Height / 2d);
+ viewportOrigin = new Point(RenderSize.Width / 2d, RenderSize.Height / 2d);
}
///
- /// Changes the Center property according to the specified translation in view coordinates.
+ /// Changes the Center property according to the specified translation in viewport coordinates.
///
public void TranslateMap(Vector translation)
{
if (translation.X != 0d || translation.Y != 0d)
{
ResetTransformOrigin();
- Center = MapViewTransform.Inverse.Transform(viewOrigin - translation);
+ Center = ViewportPointToLocation(viewportOrigin - translation);
}
}
///
/// 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.
///
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
///
/// Sets the value of the ZoomLevel property while retaining the specified origin point
- /// in view coordinates.
+ /// in viewport coordinates.
///
public void ZoomMap(Point origin, double zoomLevel)
{
- SetTransformViewOrigin(origin);
+ SetTransformOrigin(origin);
TargetZoomLevel = zoomLevel;
}
///
- /// 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.
///
- 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
diff --git a/MapControl/MapControl.csproj b/MapControl/MapControl.csproj
index 0e714c36..8c212bac 100644
--- a/MapControl/MapControl.csproj
+++ b/MapControl/MapControl.csproj
@@ -24,10 +24,11 @@
4
- pdbonly
+ none
true
bin\Release\
- TRACE
+
+
prompt
4
@@ -42,21 +43,20 @@
+
+
+
-
- Code
-
-
-
+
+
-
diff --git a/MapControl/MapElement.cs b/MapControl/MapElement.cs
index 342074f2..3366c188 100644
--- a/MapControl/MapElement.cs
+++ b/MapControl/MapElement.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);
}
+ ///
+ /// Base class for child elements of a MapPanel.
+ ///
public abstract class MapElement : FrameworkElement, INotifyParentMapChanged
{
protected MapElement()
diff --git a/MapControl/MapGraticule.cs b/MapControl/MapGraticule.cs
index 86ea3982..53d5e7f2 100644
--- a/MapControl/MapGraticule.cs
+++ b/MapControl/MapGraticule.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.Linq;
using System.Windows;
using System.Windows.Controls;
@@ -7,6 +11,10 @@ using System.Windows.Shapes;
namespace MapControl
{
+ ///
+ /// Draws a graticule overlay. The minimum spacing in pixels between adjacent graticule lines
+ /// is specified by the MinSpacingPixels property.
+ ///
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");
diff --git a/MapControl/MapInput.cs b/MapControl/MapInput.cs
index df9bc807..6e4af765 100644
--- a/MapControl/MapInput.cs
+++ b/MapControl/MapInput.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;
using System.Windows.Input;
diff --git a/MapControl/MapItem.cs b/MapControl/MapItem.cs
index 0b877071..e644b515 100644
--- a/MapControl/MapItem.cs
+++ b/MapControl/MapItem.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;
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")]
+ ///
+ /// Container class for an item in a MapItemsControl.
+ ///
+ [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)
diff --git a/MapControl/MapItemsControl.cs b/MapControl/MapItemsControl.cs
index 58ec9cbd..477111d0 100644
--- a/MapControl/MapItemsControl.cs
+++ b/MapControl/MapItemsControl.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.ComponentModel;
using System.Windows;
using System.Windows.Controls.Primitives;
@@ -8,6 +12,10 @@ namespace MapControl
{
public enum MapItemSelectionMode { Single, Extended }
+ ///
+ /// 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.
+ ///
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);
}
diff --git a/MapControl/MapPanel.cs b/MapControl/MapPanel.cs
index e899387e..0f19d30d 100644
--- a/MapControl/MapPanel.cs
+++ b/MapControl/MapPanel.cs
@@ -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
{
+ ///
+ /// 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.
+ ///
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)
diff --git a/MapControl/MapPathGeometry.cs b/MapControl/MapPathGeometry.cs
deleted file mode 100644
index 6694db4b..00000000
--- a/MapControl/MapPathGeometry.cs
+++ /dev/null
@@ -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;
- }
- }
-}
diff --git a/MapControl/MapPolygon.cs b/MapControl/MapPolygon.cs
new file mode 100644
index 00000000..2c086cc4
--- /dev/null
+++ b/MapControl/MapPolygon.cs
@@ -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
+{
+ ///
+ /// A closed map polygon, defined by a collection of geographic locations in the Locations property.
+ ///
+ 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);
+ }
+ }
+}
diff --git a/MapControl/MapPath.cs b/MapControl/MapPolyline.cs
similarity index 58%
rename from MapControl/MapPath.cs
rename to MapControl/MapPolyline.cs
index 6b430c8b..6e532a13 100644
--- a/MapControl/MapPath.cs
+++ b/MapControl/MapPolyline.cs
@@ -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
+ ///
+ /// An open map polygon, defined by a collection of geographic locations in the Locations property.
+ ///
+ 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;
+ }
}
}
diff --git a/MapControl/MapStreamGeometry.cs b/MapControl/MapStreamGeometry.cs
deleted file mode 100644
index 5866c84e..00000000
--- a/MapControl/MapStreamGeometry.cs
+++ /dev/null
@@ -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 points, bool isStroked, bool isSmoothJoin)
- {
- context.PolyLineTo(TransformPoints(points), isStroked, isSmoothJoin);
- }
-
- public void PolyQuadraticBezierTo(IList points, bool isStroked, bool isSmoothJoin)
- {
- context.PolyQuadraticBezierTo(TransformPoints(points), isStroked, isSmoothJoin);
- }
-
- public void PolyBezierTo(IList points, bool isStroked, bool isSmoothJoin)
- {
- context.PolyBezierTo(TransformPoints(points), isStroked, isSmoothJoin);
- }
-
- private IList TransformPoints(IList points)
- {
- Point[] transformedPoints = new Point[points.Count];
-
- for (int i = 0; i < transformedPoints.Length; i++)
- {
- transformedPoints[i] = transform.Transform(points[i]);
- }
-
- return transformedPoints;
- }
- }
-}
diff --git a/MapControl/MapTransform.cs b/MapControl/MapTransform.cs
index bb0d7680..f0797c1c 100644
--- a/MapControl/MapTransform.cs
+++ b/MapControl/MapTransform.cs
@@ -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].
///
- public abstract class MapTransform : GeneralTransform
+ public abstract class MapTransform
{
///
/// 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; }
///
- /// 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.
///
- public abstract double RelativeScale(Point point);
+ public abstract double RelativeScale(Location location);
+
+ ///
+ /// Transforms a geographic location to a cartesian coordinate point.
+ ///
+ public abstract Point Transform(Location location);
+
+ ///
+ /// Transforms a cartesian coordinate point to a geographic location.
+ ///
+ public abstract Location TransformBack(Point point);
+
+ ///
+ /// Transforms a collection of geographic locations to a collection of cartesian coordinate points.
+ ///
+ public PointCollection Transform(IEnumerable locations)
+ {
+ return new PointCollection(locations.Select(l => Transform(l)));
+ }
+
+ ///
+ /// Transforms a collection of cartesian coordinate points to a collections of geographic location.
+ ///
+ public LocationCollection TransformBack(IEnumerable points)
+ {
+ return new LocationCollection(points.Select(p => TransformBack(p)));
+ }
}
}
diff --git a/MapControl/MapViewTransform.cs b/MapControl/MapViewTransform.cs
deleted file mode 100644
index 1323fd2a..00000000
--- a/MapControl/MapViewTransform.cs
+++ /dev/null
@@ -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);
- }
- }
-}
diff --git a/MapControl/MercatorTransform.cs b/MapControl/MercatorTransform.cs
index 089358ce..2fd59020 100644
--- a/MapControl/MercatorTransform.cs
+++ b/MapControl/MercatorTransform.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;
using System.Windows.Media;
@@ -10,101 +14,50 @@ namespace MapControl
///
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();
- }
- }
-
- ///
- /// Transforms cartesian coordinates to latitude and longitude values in degrees
- /// according to the inverse Mercator transform.
- ///
- 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);
}
}
}
diff --git a/MapControl/Properties/AssemblyInfo.cs b/MapControl/Properties/AssemblyInfo.cs
index 3da8f27b..b8a2a5c4 100644
--- a/MapControl/Properties/AssemblyInfo.cs
+++ b/MapControl/Properties/AssemblyInfo.cs
@@ -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("")]
diff --git a/MapControl/Tile.cs b/MapControl/Tile.cs
index 683eb49f..d8a66eb6 100644
--- a/MapControl/Tile.cs
+++ b/MapControl/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;
using System.Windows.Media;
using System.Windows.Media.Animation;
diff --git a/MapControl/TileContainer.cs b/MapControl/TileContainer.cs
index 0dfc583e..ae9284a9 100644
--- a/MapControl/TileContainer.cs
+++ b/MapControl/TileContainer.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.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
diff --git a/MapControl/TileImageLoader.cs b/MapControl/TileImageLoader.cs
index 8f7cc61a..f9320d54 100644
--- a/MapControl/TileImageLoader.cs
+++ b/MapControl/TileImageLoader.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.Collections.Generic;
using System.IO;
using System.Linq;
@@ -10,9 +14,13 @@ using System.Windows.Threading;
namespace MapControl
{
+ ///
+ /// Loads map tiles by their URIs and optionally caches their image files in a folder
+ /// defined by the static TileCacheFolder property.
+ ///
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 pendingTiles = new Queue();
@@ -45,7 +53,7 @@ namespace MapControl
lock (pendingTiles)
{
- if (!string.IsNullOrEmpty(TileCacheDirectory) &&
+ if (!string.IsNullOrEmpty(TileCacheFolder) &&
!string.IsNullOrEmpty(TileLayerName))
{
List expiredTiles = new List(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
}
diff --git a/MapControl/TileLayer.cs b/MapControl/TileLayer.cs
index bb7f03dd..9f6685b1 100644
--- a/MapControl/TileLayer.cs
+++ b/MapControl/TileLayer.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.Collections.Generic;
using System.Linq;
using System.Windows;
@@ -7,11 +11,17 @@ using System.Windows.Media;
namespace MapControl
{
+ ///
+ /// 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.
+ ///
[ContentProperty("TileSource")]
public class TileLayer : DrawingVisual
{
private readonly TileImageLoader tileImageLoader = new TileImageLoader();
private readonly List tiles = new List();
+ 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()
diff --git a/MapControl/TileLayerCollection.cs b/MapControl/TileLayerCollection.cs
index b3ea04b1..6c374cc1 100644
--- a/MapControl/TileLayerCollection.cs
+++ b/MapControl/TileLayerCollection.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.Collections.ObjectModel;
using System.Windows.Media;
diff --git a/MapControl/TileSource.cs b/MapControl/TileSource.cs
index e420a33d..52e7c66b 100644
--- a/MapControl/TileSource.cs
+++ b/MapControl/TileSource.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.ComponentModel;
using System.Globalization;
using System.Text;
@@ -6,6 +10,9 @@ using System.Windows;
namespace MapControl
{
+ ///
+ /// Defines the URI of a map tile.
+ ///
[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)));
}
}
diff --git a/TestApplication/App.xaml b/TestApplication/App.xaml
new file mode 100644
index 00000000..be2ad0e5
--- /dev/null
+++ b/TestApplication/App.xaml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/TestApplication/App.xaml.cs b/TestApplication/App.xaml.cs
new file mode 100644
index 00000000..8da9a87e
--- /dev/null
+++ b/TestApplication/App.xaml.cs
@@ -0,0 +1,8 @@
+using System.Windows;
+
+namespace MapControlTestApp
+{
+ public partial class App : Application
+ {
+ }
+}
diff --git a/TestApplication/MainWindow.xaml b/TestApplication/MainWindow.xaml
new file mode 100644
index 00000000..5d148b27
--- /dev/null
+++ b/TestApplication/MainWindow.xaml
@@ -0,0 +1,210 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TestApplication/MainWindow.xaml.cs b/TestApplication/MainWindow.xaml.cs
new file mode 100644
index 00000000..40f8774a
--- /dev/null
+++ b/TestApplication/MainWindow.xaml.cs
@@ -0,0 +1,131 @@
+using System;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using MapControl;
+
+namespace MapControlTestApp
+{
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+
+ SampleItemCollection items = (SampleItemCollection)Resources["SampleItems"];
+ items.Add(
+ new SamplePolyline
+ {
+ Name = "WHV - Eckwarderhörne",
+ Locations = LocationCollection.Parse("53.5140,8.1451 53.5123,8.1506 53.5156,8.1623 53.5276,8.1757 53.5491,8.1852 53.5495,8.1877 53.5426,8.1993 53.5184,8.2219 53.5182,8.2386 53.5195,8.2387")
+ });
+ items.Add(
+ new SamplePolygon
+ {
+ Name = "JadeWeserPort",
+ Locations = LocationCollection.Parse("53.5978,8.1212 53.6018,8.1494 53.5859,8.1554 53.5852,8.1531 53.5841,8.1539 53.5802,8.1392 53.5826,8.1309 53.5867,8.1317")
+ });
+ items.Add(
+ new SamplePushpin
+ {
+ Name = "WHV - Eckwarderhörne",
+ Location = new Location(53.5495, 8.1877)
+ });
+ items.Add(
+ new SamplePushpin
+ {
+ Name = "JadeWeserPort",
+ Location = new Location(53.5914, 8.14)
+ });
+ items.Add(
+ new SamplePushpin
+ {
+ Name = "Kurhaus Dangast",
+ Location = new Location(53.447, 8.1114)
+ });
+ items.Add(
+ new SamplePushpin
+ {
+ Name = "Eckwarderhörne",
+ Location = new Location(53.5207, 8.2323)
+ });
+ items.Add(
+ new SamplePoint
+ {
+ Name = "Steinbake Leitdamm",
+ Location = new Location(53.51217, 8.16603)
+ });
+ items.Add(
+ new SamplePoint
+ {
+ Name = "Buhne 2",
+ Location = new Location(53.50926, 8.15815)
+ });
+ items.Add(
+ new SamplePoint
+ {
+ Name = "Buhne 4",
+ Location = new Location(53.50468, 8.15343)
+ });
+ items.Add(
+ new SamplePoint
+ {
+ Name = "Buhne 6",
+ Location = new Location(53.50092, 8.15267)
+ });
+ items.Add(
+ new SamplePoint
+ {
+ Name = "Buhne 8",
+ Location = new Location(53.49871, 8.15321)
+ });
+ items.Add(
+ new SamplePoint
+ {
+ Name = "Buhne 10",
+ Location = new Location(53.49350, 8.15563)
+ });
+ items.Add(
+ new SampleShape
+ {
+ Name = "N 53°30' E 8°12'",
+ Location = new Location(53.5, 8.2),
+ RadiusX = 200d, // meters
+ RadiusY = 300d, // meters
+ Rotation = 30d
+ });
+ }
+
+ private void MapManipulationInertiaStarting(object sender, ManipulationInertiaStartingEventArgs e)
+ {
+ e.TranslationBehavior.DesiredDeceleration = 0.001;
+ }
+
+ private void MapMouseLeave(object sender, MouseEventArgs e)
+ {
+ mouseLocation.Text = string.Empty;
+ }
+
+ private void MapMouseMove(object sender, MouseEventArgs e)
+ {
+ mouseLocation.Text = map.ViewportPointToLocation(e.GetPosition(map)).ToString();
+ }
+
+ private void SeamarksClick(object sender, RoutedEventArgs e)
+ {
+ TileLayer seamarks = (TileLayer)Resources["SeamarksTileLayer"];
+ CheckBox checkBox = (CheckBox)sender;
+
+ if ((bool)checkBox.IsChecked)
+ {
+ map.TileLayers.Add(seamarks);
+ }
+ else
+ {
+ map.TileLayers.Remove(seamarks);
+ }
+ }
+ }
+}
diff --git a/TestApplication/MapBackgroundConverter.cs b/TestApplication/MapBackgroundConverter.cs
new file mode 100644
index 00000000..26ecf976
--- /dev/null
+++ b/TestApplication/MapBackgroundConverter.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+using System.Windows.Media;
+using MapControl;
+
+namespace MapControlTestApp
+{
+ class MapBackgroundConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ MapBackground mapBackground = (MapBackground)value;
+
+ if (parameter as string == "Foreground")
+ {
+ return mapBackground == MapBackground.Light ? Brushes.Black : Brushes.White;
+ }
+
+ return mapBackground == MapBackground.Light ? Brushes.White : Brushes.Black;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/TestApplication/Properties/AssemblyInfo.cs b/TestApplication/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..da1180b2
--- /dev/null
+++ b/TestApplication/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+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("TestApplication")]
+[assembly: AssemblyDescription("WPF Map Control Test Application")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Clemens Fischer")]
+[assembly: AssemblyProduct("WPF Map Control")]
+[assembly: AssemblyCopyright("Copyright © 2012 Clemens Fischer")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+//In order to begin building localizable applications, set
+//CultureYouAreCodingWith in your .csproj file
+//inside a . For example, if you are using US english
+//in your source files, set the to en-US. Then uncomment
+//the NeutralResourceLanguage attribute below. Update the "en-US" in
+//the line below to match the UICulture setting in the project file.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
+
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/TestApplication/SampleItems.cs b/TestApplication/SampleItems.cs
new file mode 100644
index 00000000..acf41317
--- /dev/null
+++ b/TestApplication/SampleItems.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Windows;
+using System.Windows.Controls;
+using MapControl;
+
+namespace MapControlTestApp
+{
+ class SampleItem
+ {
+ public string Name { get; set; }
+ }
+
+ class SamplePoint : SampleItem
+ {
+ public Location Location { get; set; }
+ }
+
+ class SamplePushpin : SamplePoint
+ {
+ }
+
+ class SampleShape : SamplePoint
+ {
+ public double RadiusX { get; set; }
+ public double RadiusY { get; set; }
+ public double Rotation { get; set; }
+ }
+
+ class SamplePolyline : SampleItem
+ {
+ public LocationCollection Locations { get; set; }
+ }
+
+ class SamplePolygon : SampleItem
+ {
+ public LocationCollection Locations { get; set; }
+ }
+
+ class SampleItemCollection : ObservableCollection
+ {
+ }
+
+ class SampleItemStyleSelector : StyleSelector
+ {
+ public override Style SelectStyle(object item, DependencyObject container)
+ {
+ if (item is SamplePolyline)
+ {
+ return Application.Current.Windows[0].Resources["SamplePolylineItemStyle"] as Style;
+ }
+
+ if (item is SamplePolygon)
+ {
+ return Application.Current.Windows[0].Resources["SamplePolygonItemStyle"] as Style;
+ }
+
+ if (item is SampleShape)
+ {
+ return Application.Current.Windows[0].Resources["SampleShapeItemStyle"] as Style;
+ }
+
+ if (item is SamplePushpin)
+ {
+ return Application.Current.Windows[0].Resources["SamplePushpinItemStyle"] as Style;
+ }
+
+ return Application.Current.Windows[0].Resources["SamplePointItemStyle"] as Style;
+ }
+ }
+}
diff --git a/TestApplication/TestApplication.csproj b/TestApplication/TestApplication.csproj
new file mode 100644
index 00000000..07180684
--- /dev/null
+++ b/TestApplication/TestApplication.csproj
@@ -0,0 +1,87 @@
+
+
+
+ Debug
+ x86
+ 8.0.30703
+ 2.0
+ {CCBCDAE5-E68F-43A8-930A-0749E476D29D}
+ WinExe
+ Properties
+ MapControlTestApp
+ MapControlTestApp
+ v4.0
+ Client
+ 512
+ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 4
+
+
+ x86
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ x86
+ none
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+ 4.0
+
+
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+ App.xaml
+ Code
+
+
+ MainWindow.xaml
+ Code
+
+
+
+
+ Code
+
+
+
+
+
+ {06481252-2310-414A-B9FC-D5739FDF6BD3}
+ MapControl
+
+
+
+
+
\ No newline at end of file