Added Location

This commit is contained in:
ClemensF 2012-05-04 12:52:20 +02:00
parent caab7208a3
commit a98f59e1a9
35 changed files with 1235 additions and 717 deletions

View file

@ -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

View file

@ -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
View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View file

@ -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

View file

@ -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" />

View file

@ -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()

View file

@ -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");

View file

@ -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;

View file

@ -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)

View file

@ -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);
}

View file

@ -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)

View file

@ -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
View 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);
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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)));
}
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -7,11 +7,11 @@ using System.Windows;
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Map Control")]
[assembly: AssemblyDescription("MapControl")]
[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("")]

View file

@ -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;

View file

@ -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

View file

@ -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
}

View file

@ -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()

View file

@ -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;

View file

@ -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)));
}
}

7
TestApplication/App.xaml Normal file
View file

@ -0,0 +1,7 @@
<Application x:Class="MapControlTestApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>

View file

@ -0,0 +1,8 @@
using System.Windows;
namespace MapControlTestApp
{
public partial class App : Application
{
}
}

View file

@ -0,0 +1,210 @@
<Window x:Class="MapControlTestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:map="clr-namespace:MapControl;assembly=MapControl"
xmlns:local="clr-namespace:MapControlTestApp"
Title="MainWindow" Height="600" Width="800">
<Window.Resources>
<map:TileLayer x:Key="SeamarksTileLayer"
Name="Seamarks" Description="© {y} OpenSeaMap Contributors, CC-BY-SA"
TileSource="http://tiles.openseamap.org/seamark/{z}/{x}/{y}.png"
IsCached="True" MinZoomLevel="10" MaxZoomLevel="18"/>
<local:SampleItemCollection x:Key="SampleItems"/>
<local:SampleItemStyleSelector x:Key="SampleItemStyleSelector"/>
<Style x:Key="SamplePushpinItemStyle" TargetType="map:MapItem">
<Setter Property="map:MapPanel.Location" Value="{Binding Location}"/>
<Setter Property="Content" Value="{Binding Name}"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="OrangeRed"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="SamplePointItemStyle" TargetType="map:MapItem">
<Setter Property="map:MapPanel.Location" Value="{Binding Location}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="map:MapItem">
<Canvas>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="Disabled"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="labelBackground" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected"/>
<VisualState x:Name="Selected"/>
</VisualStateGroup>
<VisualStateGroup x:Name="CurrentStates">
<VisualState x:Name="NotCurrent"/>
<VisualState x:Name="Current">
<Storyboard>
<ColorAnimation Storyboard.TargetName="path" Storyboard.TargetProperty="Stroke.Color" To="Magenta" Duration="0:0:0.1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Path Name="path" StrokeThickness="2" Fill="Transparent">
<Path.Stroke>
<SolidColorBrush Color="Gray"/>
</Path.Stroke>
<Path.Data>
<EllipseGeometry RadiusX="8" RadiusY="8"/>
</Path.Data>
</Path>
<Grid Canvas.Left="10" Canvas.Bottom="3">
<Rectangle Name="labelBackground" Fill="White" Opacity="0.5"/>
<TextBlock Margin="2,0,2,1" Text="{Binding Name}"/>
</Grid>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="SampleShapeItemStyle" TargetType="map:MapItem">
<Setter Property="map:MapPanel.Location" Value="{Binding Location}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="map:MapItem">
<Canvas>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="Disabled"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="labelBackground" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected"/>
<VisualState x:Name="Selected"/>
</VisualStateGroup>
<VisualStateGroup x:Name="CurrentStates">
<VisualState x:Name="NotCurrent"/>
<VisualState x:Name="Current">
<Storyboard>
<ColorAnimation Storyboard.TargetName="path" Storyboard.TargetProperty="Stroke.Color" To="Magenta" Duration="0:0:0.1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Path Name="path" StrokeThickness="2" Fill="#50FFA500">
<Path.RenderTransform>
<RotateTransform Angle="{Binding Rotation}"/>
</Path.RenderTransform>
<Path.Stroke>
<SolidColorBrush Color="Gray"/>
</Path.Stroke>
<Path.Data>
<EllipseGeometry RadiusX="{Binding RadiusX}" RadiusY="{Binding RadiusY}"
Transform="{Binding ScaleRotateTransform, ElementName=map}"/>
</Path.Data>
</Path>
<Line Stroke="Gray" StrokeThickness="4" StrokeStartLineCap="Round" StrokeEndLineCap="Round"/>
<Grid Canvas.Left="3" Canvas.Bottom="3">
<Rectangle Name="labelBackground" Fill="White" Opacity="0.5"/>
<TextBlock Margin="2,0,2,1" Text="{Binding Name}"/>
</Grid>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="SamplePolylineItemStyle" TargetType="map:MapItem">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="map:MapItem">
<map:MapPolyline Stroke="Blue" StrokeThickness="2" Locations="{Binding Locations}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="SamplePolygonItemStyle" TargetType="map:MapItem">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="map:MapItem">
<map:MapPolygon Stroke="Green" StrokeThickness="2" Locations="{Binding Locations}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<map:Map Name="map" IsManipulationEnabled="True" Margin="2" FontSize="10"
LightForeground="Black" LightBackground="White" DarkForeground="White" DarkBackground="#FF3F3F3F"
Center="53.5,8.2" ZoomLevel="11"
MainTileLayer="{Binding ElementName=tileSourceComboBox, Path=SelectedItem}"
ManipulationInertiaStarting="MapManipulationInertiaStarting"
MouseMove="MapMouseMove" MouseLeave="MapMouseLeave">
<map:MapGraticule Opacity="0.6"/>
<map:MapItemsControl ItemsSource="{StaticResource SampleItems}"
ItemContainerStyleSelector="{StaticResource SampleItemStyleSelector}"
IsSynchronizedWithCurrentItem="True"/>
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="2,0,0,0" FontSize="10"
Text="{Binding ElementName=map, Path=MainTileLayer.Description}"/>
</map:Map>
<Grid Grid.Row="1" Margin="2">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Name="mouseLocation" Margin="4" VerticalAlignment="Center"/>
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
<Slider Name="zoomSlider" ToolTip="Zoom Level" Margin="4,0,4,0" Width="100" Minimum="1" Maximum="20" SmallChange="0.01"
Value="{Binding ElementName=map, Path=TargetZoomLevel}" />
<Slider Name="headingSlider" ToolTip="Heading" Margin="4,0,4,0" Width="100" Minimum="0" Maximum="360" SmallChange="10" LargeChange="45"
Value="{Binding ElementName=map, Path=TargetHeading}"/>
<CheckBox ToolTip="Map Overlay" Margin="4,0,4,0" VerticalAlignment="Center" Content="Seamarks" Click="SeamarksClick"/>
<ComboBox Name="tileSourceComboBox" ToolTip="Main Tile Layer" Margin="4,0,4,0" DisplayMemberPath="Name" SelectedIndex="0">
<ComboBox.Items>
<map:TileLayer Name="OpenStreetMap" Description="© {y} OpenStreetMap Contributors, CC-BY-SA"
TileSource="http://{c}.tile.openstreetmap.org/{z}/{x}/{y}.png"
IsCached="True"/>
<map:TileLayer Name="OpenCycleMap" Description="OpenCycleMap - © {y} Andy Allen &amp; OpenStreetMap Contributors, CC-BY-SA"
TileSource="http://{c}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png"
IsCached="True"/>
<map:TileLayer Name="OCM Transport" Description="OpenCycleMap Transport - © {y} Andy Allen &amp; OpenStreetMap Contributors, CC-BY-SA"
TileSource="http://{c}.tile2.opencyclemap.org/transport/{z}/{x}/{y}.png"
IsCached="True"/>
<map:TileLayer Name="OCM Landscape" Description="OpenCycleMap Landscape - © {y} Andy Allen &amp; OpenStreetMap Contributors, CC-BY-SA"
TileSource="http://{c}.tile3.opencyclemap.org/landscape/{z}/{x}/{y}.png"
IsCached="True"/>
<map:TileLayer Name="MapQuest OSM" Description="MapQuest OSM - © {y} MapQuest &amp; OpenStreetMap Contributors"
TileSource="http://otile{n}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png"
IsCached="True" MaxZoomLevel="20"/>
<!--<map:TileLayer Name="Google Maps" Description="Google Maps - © {y} Google"
TileSource="http://mt{i}.google.com/vt/x={x}&amp;y={y}&amp;z={z}"
IsCached="True" MaxZoomLevel="20"/>
<map:TileLayer Name="Google Images" Description="Google Maps - © {y} Google"
TileSource="http://khm{i}.google.com/kh/v=108&amp;x={x}&amp;y={y}&amp;z={z}"
IsCached="True" MaxZoomLevel="20" HasDarkBackground="True"/>
<map:TileLayer Name="Bing Maps" Description="Bing Maps - © {y} Microsoft Corporation"
TileSource="http://ecn.t{i}.tiles.virtualearth.net/tiles/r{q}.png?g=0&amp;stl=h"
IsCached="True" MaxZoomLevel="20"/>
<map:TileLayer Name="Bing Images" Description="Bing Maps - © {y} Microsoft Corporation"
TileSource="http://ecn.t{i}.tiles.virtualearth.net/tiles/a{q}.jpeg?g=0"
IsCached="True" MaxZoomLevel="20" HasDarkBackground="True"/>
<map:TileLayer Name="Bing Hybrid" Description="Bing Maps - © {y} Microsoft Corporation"
TileSource="http://ecn.t{i}.tiles.virtualearth.net/tiles/h{q}.jpeg?g=0&amp;stl=h"
IsCached="True" MaxZoomLevel="20" HasDarkBackground="True"/>-->
</ComboBox.Items>
</ComboBox>
</StackPanel>
</Grid>
</Grid>
</Window>

View file

@ -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);
}
}
}
}

View file

@ -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();
}
}
}

View file

@ -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
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> 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")]

View file

@ -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<SampleItem>
{
}
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;
}
}
}

View file

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{CCBCDAE5-E68F-43A8-930A-0749E476D29D}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MapControlTestApp</RootNamespace>
<AssemblyName>MapControlTestApp</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="SampleItems.cs" />
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<AppDesigner Include="Properties\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MapControl\MapControl.csproj">
<Project>{06481252-2310-414A-B9FC-D5739FDF6BD3}</Project>
<Name>MapControl</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>