Version 5.0: Reworked MapBase and MapPath

This commit is contained in:
ClemensF 2020-03-28 21:53:38 +01:00
parent 06fd31c17b
commit 49e15ce424
41 changed files with 466 additions and 1068 deletions

View file

@ -7,16 +7,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MapControl", "MapControl",
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SampleApps", "SampleApps", "{8F2103C2-78AF-4810-8FB9-67572F50C8FC}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SampleApps", "SampleApps", "{8F2103C2-78AF-4810-8FB9-67572F50C8FC}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfApplication", "SampleApps\WpfApplication\WpfApplication.csproj", "{F92DA93D-75DB-4308-A5F9-6B4C3908A675}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniversalApp", "SampleApps\UniversalApp\UniversalApp.csproj", "{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniversalApp", "SampleApps\UniversalApp\UniversalApp.csproj", "{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapControl.UWP", "MapControl\UWP\MapControl.UWP.csproj", "{9545F73C-9C35-4CF6-BAAE-19A0BAEBD344}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapControl.UWP", "MapControl\UWP\MapControl.UWP.csproj", "{9545F73C-9C35-4CF6-BAAE-19A0BAEBD344}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfCoreApp", "SampleApps\WpfCoreApp\WpfCoreApp.csproj", "{E9D15B9A-75F5-40A9-A582-9BFBB074C127}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapControl.WPF", "MapControl\WPF\MapControl.WPF.csproj", "{62F1726B-3144-49F4-8BCC-94160A3B2186}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapControl.WPF", "MapControl\WPF\MapControl.WPF.csproj", "{62F1726B-3144-49F4-8BCC-94160A3B2186}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfApplication", "SampleApps\WpfApplication\WpfApplication.csproj", "{CA60DFDB-0BE1-40F6-B575-CDC797A40E2E}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -29,22 +27,6 @@ Global
Release|x86 = Release|x86 Release|x86 = Release|x86
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F92DA93D-75DB-4308-A5F9-6B4C3908A675}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F92DA93D-75DB-4308-A5F9-6B4C3908A675}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F92DA93D-75DB-4308-A5F9-6B4C3908A675}.Debug|ARM.ActiveCfg = Debug|Any CPU
{F92DA93D-75DB-4308-A5F9-6B4C3908A675}.Debug|ARM.Build.0 = Debug|Any CPU
{F92DA93D-75DB-4308-A5F9-6B4C3908A675}.Debug|x64.ActiveCfg = Debug|Any CPU
{F92DA93D-75DB-4308-A5F9-6B4C3908A675}.Debug|x64.Build.0 = Debug|Any CPU
{F92DA93D-75DB-4308-A5F9-6B4C3908A675}.Debug|x86.ActiveCfg = Debug|Any CPU
{F92DA93D-75DB-4308-A5F9-6B4C3908A675}.Debug|x86.Build.0 = Debug|Any CPU
{F92DA93D-75DB-4308-A5F9-6B4C3908A675}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F92DA93D-75DB-4308-A5F9-6B4C3908A675}.Release|Any CPU.Build.0 = Release|Any CPU
{F92DA93D-75DB-4308-A5F9-6B4C3908A675}.Release|ARM.ActiveCfg = Release|Any CPU
{F92DA93D-75DB-4308-A5F9-6B4C3908A675}.Release|ARM.Build.0 = Release|Any CPU
{F92DA93D-75DB-4308-A5F9-6B4C3908A675}.Release|x64.ActiveCfg = Release|Any CPU
{F92DA93D-75DB-4308-A5F9-6B4C3908A675}.Release|x64.Build.0 = Release|Any CPU
{F92DA93D-75DB-4308-A5F9-6B4C3908A675}.Release|x86.ActiveCfg = Release|Any CPU
{F92DA93D-75DB-4308-A5F9-6B4C3908A675}.Release|x86.Build.0 = Release|Any CPU
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|Any CPU.ActiveCfg = Debug|x64 {AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|Any CPU.ActiveCfg = Debug|x64
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|Any CPU.Build.0 = Debug|x64 {AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|Any CPU.Build.0 = Debug|x64
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|Any CPU.Deploy.0 = Debug|x64 {AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|Any CPU.Deploy.0 = Debug|x64
@ -85,22 +67,6 @@ Global
{9545F73C-9C35-4CF6-BAAE-19A0BAEBD344}.Release|x64.Build.0 = Release|Any CPU {9545F73C-9C35-4CF6-BAAE-19A0BAEBD344}.Release|x64.Build.0 = Release|Any CPU
{9545F73C-9C35-4CF6-BAAE-19A0BAEBD344}.Release|x86.ActiveCfg = Release|Any CPU {9545F73C-9C35-4CF6-BAAE-19A0BAEBD344}.Release|x86.ActiveCfg = Release|Any CPU
{9545F73C-9C35-4CF6-BAAE-19A0BAEBD344}.Release|x86.Build.0 = Release|Any CPU {9545F73C-9C35-4CF6-BAAE-19A0BAEBD344}.Release|x86.Build.0 = Release|Any CPU
{E9D15B9A-75F5-40A9-A582-9BFBB074C127}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E9D15B9A-75F5-40A9-A582-9BFBB074C127}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E9D15B9A-75F5-40A9-A582-9BFBB074C127}.Debug|ARM.ActiveCfg = Debug|Any CPU
{E9D15B9A-75F5-40A9-A582-9BFBB074C127}.Debug|ARM.Build.0 = Debug|Any CPU
{E9D15B9A-75F5-40A9-A582-9BFBB074C127}.Debug|x64.ActiveCfg = Debug|Any CPU
{E9D15B9A-75F5-40A9-A582-9BFBB074C127}.Debug|x64.Build.0 = Debug|Any CPU
{E9D15B9A-75F5-40A9-A582-9BFBB074C127}.Debug|x86.ActiveCfg = Debug|Any CPU
{E9D15B9A-75F5-40A9-A582-9BFBB074C127}.Debug|x86.Build.0 = Debug|Any CPU
{E9D15B9A-75F5-40A9-A582-9BFBB074C127}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E9D15B9A-75F5-40A9-A582-9BFBB074C127}.Release|Any CPU.Build.0 = Release|Any CPU
{E9D15B9A-75F5-40A9-A582-9BFBB074C127}.Release|ARM.ActiveCfg = Release|Any CPU
{E9D15B9A-75F5-40A9-A582-9BFBB074C127}.Release|ARM.Build.0 = Release|Any CPU
{E9D15B9A-75F5-40A9-A582-9BFBB074C127}.Release|x64.ActiveCfg = Release|Any CPU
{E9D15B9A-75F5-40A9-A582-9BFBB074C127}.Release|x64.Build.0 = Release|Any CPU
{E9D15B9A-75F5-40A9-A582-9BFBB074C127}.Release|x86.ActiveCfg = Release|Any CPU
{E9D15B9A-75F5-40A9-A582-9BFBB074C127}.Release|x86.Build.0 = Release|Any CPU
{62F1726B-3144-49F4-8BCC-94160A3B2186}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {62F1726B-3144-49F4-8BCC-94160A3B2186}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{62F1726B-3144-49F4-8BCC-94160A3B2186}.Debug|Any CPU.Build.0 = Debug|Any CPU {62F1726B-3144-49F4-8BCC-94160A3B2186}.Debug|Any CPU.Build.0 = Debug|Any CPU
{62F1726B-3144-49F4-8BCC-94160A3B2186}.Debug|ARM.ActiveCfg = Debug|Any CPU {62F1726B-3144-49F4-8BCC-94160A3B2186}.Debug|ARM.ActiveCfg = Debug|Any CPU
@ -117,16 +83,31 @@ Global
{62F1726B-3144-49F4-8BCC-94160A3B2186}.Release|x64.Build.0 = Release|Any CPU {62F1726B-3144-49F4-8BCC-94160A3B2186}.Release|x64.Build.0 = Release|Any CPU
{62F1726B-3144-49F4-8BCC-94160A3B2186}.Release|x86.ActiveCfg = Release|Any CPU {62F1726B-3144-49F4-8BCC-94160A3B2186}.Release|x86.ActiveCfg = Release|Any CPU
{62F1726B-3144-49F4-8BCC-94160A3B2186}.Release|x86.Build.0 = Release|Any CPU {62F1726B-3144-49F4-8BCC-94160A3B2186}.Release|x86.Build.0 = Release|Any CPU
{CA60DFDB-0BE1-40F6-B575-CDC797A40E2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CA60DFDB-0BE1-40F6-B575-CDC797A40E2E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CA60DFDB-0BE1-40F6-B575-CDC797A40E2E}.Debug|ARM.ActiveCfg = Debug|Any CPU
{CA60DFDB-0BE1-40F6-B575-CDC797A40E2E}.Debug|ARM.Build.0 = Debug|Any CPU
{CA60DFDB-0BE1-40F6-B575-CDC797A40E2E}.Debug|x64.ActiveCfg = Debug|Any CPU
{CA60DFDB-0BE1-40F6-B575-CDC797A40E2E}.Debug|x64.Build.0 = Debug|Any CPU
{CA60DFDB-0BE1-40F6-B575-CDC797A40E2E}.Debug|x86.ActiveCfg = Debug|Any CPU
{CA60DFDB-0BE1-40F6-B575-CDC797A40E2E}.Debug|x86.Build.0 = Debug|Any CPU
{CA60DFDB-0BE1-40F6-B575-CDC797A40E2E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CA60DFDB-0BE1-40F6-B575-CDC797A40E2E}.Release|Any CPU.Build.0 = Release|Any CPU
{CA60DFDB-0BE1-40F6-B575-CDC797A40E2E}.Release|ARM.ActiveCfg = Release|Any CPU
{CA60DFDB-0BE1-40F6-B575-CDC797A40E2E}.Release|ARM.Build.0 = Release|Any CPU
{CA60DFDB-0BE1-40F6-B575-CDC797A40E2E}.Release|x64.ActiveCfg = Release|Any CPU
{CA60DFDB-0BE1-40F6-B575-CDC797A40E2E}.Release|x64.Build.0 = Release|Any CPU
{CA60DFDB-0BE1-40F6-B575-CDC797A40E2E}.Release|x86.ActiveCfg = Release|Any CPU
{CA60DFDB-0BE1-40F6-B575-CDC797A40E2E}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{F92DA93D-75DB-4308-A5F9-6B4C3908A675} = {8F2103C2-78AF-4810-8FB9-67572F50C8FC}
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1} = {8F2103C2-78AF-4810-8FB9-67572F50C8FC} {AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1} = {8F2103C2-78AF-4810-8FB9-67572F50C8FC}
{9545F73C-9C35-4CF6-BAAE-19A0BAEBD344} = {52AECE49-F314-4F76-98F2-FA800F07824B} {9545F73C-9C35-4CF6-BAAE-19A0BAEBD344} = {52AECE49-F314-4F76-98F2-FA800F07824B}
{E9D15B9A-75F5-40A9-A582-9BFBB074C127} = {8F2103C2-78AF-4810-8FB9-67572F50C8FC}
{62F1726B-3144-49F4-8BCC-94160A3B2186} = {52AECE49-F314-4F76-98F2-FA800F07824B} {62F1726B-3144-49F4-8BCC-94160A3B2186} = {52AECE49-F314-4F76-98F2-FA800F07824B}
{CA60DFDB-0BE1-40F6-B575-CDC797A40E2E} = {8F2103C2-78AF-4810-8FB9-67572F50C8FC}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {458346DD-B23F-4FDC-8F9D-A10F1882A4DB} SolutionGuid = {458346DD-B23F-4FDC-8F9D-A10F1882A4DB}

View file

@ -69,12 +69,12 @@ namespace MapControl
private DoubleAnimation zoomLevelAnimation; private DoubleAnimation zoomLevelAnimation;
private DoubleAnimation headingAnimation; private DoubleAnimation headingAnimation;
private Location transformCenter; private Location transformCenter;
private Point viewportCenter; private Point viewCenter;
private double centerLongitude; private double centerLongitude;
private bool internalPropertyChange; private bool internalPropertyChange;
/// <summary> /// <summary>
/// Raised when the current viewport has changed. /// Raised when the current map viewport has changed.
/// </summary> /// </summary>
public event EventHandler<ViewportChangedEventArgs> ViewportChanged; public event EventHandler<ViewportChangedEventArgs> ViewportChanged;
@ -224,59 +224,56 @@ namespace MapControl
} }
/// <summary> /// <summary>
/// Gets the transformation from cartesian map coordinates to viewport coordinates. /// Gets the scaling factor from cartesian map coordinates to view coordinates,
/// i.e. pixels per meter, as a read-only dependency property.
/// </summary>
public double ViewScale
{
get { return (double)GetValue(ViewScaleProperty); }
private set { SetViewScale(value); }
}
/// <summary>
/// Gets the ViewTransform instance that is used to transform between cartesian map coordinates
/// and view coordinates.
/// </summary> /// </summary>
public ViewTransform ViewTransform { get; } = new ViewTransform(); public ViewTransform ViewTransform { get; } = new ViewTransform();
/// <summary> /// <summary>
/// Gets the transformation from cartesian map coordinates to viewport coordinates as MatrixTransform. /// Gets a MatrixTransform that can be used to transform from cartesian map coordinates
/// to view coordinates.
/// </summary> /// </summary>
public MatrixTransform ViewportTransform { get; } = new MatrixTransform(); public MatrixTransform MapToViewTransform { get; } = new MatrixTransform();
/// <summary> /// <summary>
/// Gets the scaling transformation from meters to viewport coordinates at the Center location. /// Gets the horizontal and vertical scaling factors from cartesian map coordinates to view
/// coordinates at the specified location, i.e. pixels per meter.
/// </summary> /// </summary>
public ScaleTransform ScaleTransform { get; } = new ScaleTransform(); public Vector GetScale(Location location)
/// <summary>
/// Gets the transformation that rotates by the value of the Heading property.
/// </summary>
public RotateTransform RotateTransform { get; } = new RotateTransform();
/// <summary>
/// Gets the combination of ScaleTransform and RotateTransform
/// </summary>
public TransformGroup ScaleRotateTransform
{ {
get return ViewTransform.Scale * MapProjection.GetRelativeScale(location);
{
var transform = new TransformGroup();
transform.Children.Add(ScaleTransform);
transform.Children.Add(RotateTransform);
return transform;
}
} }
/// <summary> /// <summary>
/// Transforms a Location in geographic coordinates to a Point in viewport coordinates. /// Transforms a Location in geographic coordinates to a Point in view coordinates.
/// </summary> /// </summary>
public Point LocationToViewportPoint(Location location) public Point LocationToView(Location location)
{ {
return ViewTransform.MapToView(MapProjection.LocationToMap(location)); return ViewTransform.MapToView(MapProjection.LocationToMap(location));
} }
/// <summary> /// <summary>
/// Transforms a Point in viewport coordinates to a Location in geographic coordinates. /// Transforms a Point in view coordinates to a Location in geographic coordinates.
/// </summary> /// </summary>
public Location ViewportPointToLocation(Point point) public Location ViewToLocation(Point point)
{ {
return MapProjection.MapToLocation(ViewTransform.ViewToMap(point)); return MapProjection.MapToLocation(ViewTransform.ViewToMap(point));
} }
/// <summary> /// <summary>
/// Transforms a Rect in viewport coordinates to a BoundingBox in geographic coordinates. /// Transforms a Rect in view coordinates to a BoundingBox in geographic coordinates.
/// </summary> /// </summary>
public BoundingBox ViewportRectToBoundingBox(Rect rect) public BoundingBox ViewRectToBoundingBox(Rect rect)
{ {
var p1 = ViewTransform.ViewToMap(new Point(rect.X, rect.Y)); var p1 = ViewTransform.ViewToMap(new Point(rect.X, rect.Y));
var p2 = ViewTransform.ViewToMap(new Point(rect.X, rect.Y + rect.Height)); var p2 = ViewTransform.ViewToMap(new Point(rect.X, rect.Y + rect.Height));
@ -292,13 +289,13 @@ namespace MapControl
} }
/// <summary> /// <summary>
/// Sets a temporary center point in viewport coordinates for scaling and rotation transformations. /// Sets a temporary center point in view coordinates for scaling and rotation transformations.
/// This center point is automatically reset when the Center property is set by application code. /// This center point is automatically reset when the Center property is set by application code.
/// </summary> /// </summary>
public void SetTransformCenter(Point center) public void SetTransformCenter(Point center)
{ {
transformCenter = ViewportPointToLocation(center); transformCenter = ViewToLocation(center);
viewportCenter = center; viewCenter = center;
} }
/// <summary> /// <summary>
@ -307,11 +304,11 @@ namespace MapControl
public void ResetTransformCenter() public void ResetTransformCenter()
{ {
transformCenter = null; transformCenter = null;
viewportCenter = new Point(RenderSize.Width / 2d, RenderSize.Height / 2d); viewCenter = new Point(RenderSize.Width / 2d, RenderSize.Height / 2d);
} }
/// <summary> /// <summary>
/// Changes the Center property according to the specified translation in viewport coordinates. /// Changes the Center property according to the specified translation in view coordinates.
/// </summary> /// </summary>
public void TranslateMap(Vector translation) public void TranslateMap(Vector translation)
{ {
@ -323,21 +320,21 @@ namespace MapControl
if (translation.X != 0d || translation.Y != 0d) if (translation.X != 0d || translation.Y != 0d)
{ {
Center = ViewportPointToLocation(viewportCenter - translation); Center = ViewToLocation(viewCenter - translation);
} }
} }
/// <summary> /// <summary>
/// Changes the Center, Heading and ZoomLevel properties according to the specified /// Changes the Center, Heading and ZoomLevel properties according to the specified
/// viewport coordinate translation, rotation and scale delta values. Rotation and scaling /// view coordinate translation, rotation and scale delta values. Rotation and scaling
/// is performed relative to the specified center point in viewport coordinates. /// is performed relative to the specified center point in view coordinates.
/// </summary> /// </summary>
public void TransformMap(Point center, Vector translation, double rotation, double scale) public void TransformMap(Point center, Vector translation, double rotation, double scale)
{ {
if (rotation != 0d || scale != 1d) if (rotation != 0d || scale != 1d)
{ {
transformCenter = ViewportPointToLocation(center); transformCenter = ViewToLocation(center);
viewportCenter = center + translation; viewCenter = center + translation;
if (rotation != 0d) if (rotation != 0d)
{ {
@ -363,7 +360,7 @@ namespace MapControl
/// <summary> /// <summary>
/// Sets the value of the TargetZoomLevel property while retaining the specified center point /// Sets the value of the TargetZoomLevel property while retaining the specified center point
/// in viewport coordinates. /// in view coordinates.
/// </summary> /// </summary>
public void ZoomMap(Point center, double zoomLevel) public void ZoomMap(Point center, double zoomLevel)
{ {
@ -378,16 +375,15 @@ namespace MapControl
/// <summary> /// <summary>
/// Sets the TargetZoomLevel and TargetCenter properties so that the specified bounding box /// Sets the TargetZoomLevel and TargetCenter properties so that the specified bounding box
/// fits into the current viewport. The TargetHeading property is set to zero. /// fits into the current view. The TargetHeading property is set to zero.
/// </summary> /// </summary>
public void ZoomToBounds(BoundingBox boundingBox) public void ZoomToBounds(BoundingBox boundingBox)
{ {
var rect = MapProjection.BoundingBoxToRect(boundingBox); var rect = MapProjection.BoundingBoxToRect(boundingBox);
var center = new Point(rect.X + rect.Width / 2d, rect.Y + rect.Height / 2d); var center = new Point(rect.X + rect.Width / 2d, rect.Y + rect.Height / 2d);
var scale = Math.Min(RenderSize.Width / rect.Width, RenderSize.Height / rect.Height) var scale = Math.Min(RenderSize.Width / rect.Width, RenderSize.Height / rect.Height);
* MapProjection.Wgs84MetersPerDegree * 360d / 256d;
TargetZoomLevel = Math.Log(scale, 2d); TargetZoomLevel = ViewTransform.ScaleToZoomLevel(scale);
TargetCenter = MapProjection.MapToLocation(center); TargetCenter = MapProjection.MapToLocation(center);
TargetHeading = 0d; TargetHeading = 0d;
} }
@ -703,16 +699,19 @@ namespace MapControl
private void UpdateTransform(bool resetTransformCenter = false, bool projectionChanged = false) private void UpdateTransform(bool resetTransformCenter = false, bool projectionChanged = false)
{ {
var projection = MapProjection; var projection = MapProjection;
var viewportScale = 256d * Math.Pow(2d, ZoomLevel) / (360d * MapProjection.Wgs84MetersPerDegree);
var center = transformCenter ?? Center;
projection.Center = ProjectionCenter ?? Center; projection.Center = ProjectionCenter ?? Center;
ViewTransform.SetTransform(projection.LocationToMap(center), viewportCenter, viewportScale, Heading); var center = transformCenter ?? Center;
var mapCenter = projection.LocationToMap(center);
var viewScale = ViewTransform.ZoomLevelToScale(ZoomLevel);
ViewTransform.SetTransform(mapCenter, viewCenter, viewScale, Heading);
if (transformCenter != null) if (transformCenter != null)
{ {
center = ViewportPointToLocation(new Point(RenderSize.Width / 2d, RenderSize.Height / 2d)); center = ViewToLocation(new Point(RenderSize.Width / 2d, RenderSize.Height / 2d));
center.Longitude = Location.NormalizeLongitude(center.Longitude); center.Longitude = Location.NormalizeLongitude(center.Longitude);
if (center.Latitude < -projection.MaxLatitude || center.Latitude > projection.MaxLatitude) if (center.Latitude < -projection.MaxLatitude || center.Latitude > projection.MaxLatitude)
@ -733,18 +732,14 @@ namespace MapControl
ResetTransformCenter(); ResetTransformCenter();
projection.Center = ProjectionCenter ?? center; projection.Center = ProjectionCenter ?? center;
mapCenter = projection.LocationToMap(center);
ViewTransform.SetTransform(projection.LocationToMap(center), viewportCenter, viewportScale, Heading); ViewTransform.SetTransform(mapCenter, viewCenter, viewScale, Heading);
} }
} }
ViewportTransform.Matrix = ViewTransform.MapToViewMatrix; ViewScale = ViewTransform.Scale;
MapToViewTransform.Matrix = ViewTransform.MapToViewMatrix;
var scale = projection.GetRelativeScale(center);
ScaleTransform.ScaleX = scale.X * ViewTransform.Scale;
ScaleTransform.ScaleY = scale.Y * ViewTransform.Scale;
RotateTransform.Angle = ViewTransform.Rotation;
OnViewportChanged(new ViewportChangedEventArgs(projectionChanged, Center.Longitude - centerLongitude)); OnViewportChanged(new ViewportChangedEventArgs(projectionChanged, Center.Longitude - centerLongitude));

View file

@ -23,11 +23,14 @@ using System.Windows.Threading;
namespace MapControl namespace MapControl
{ {
/// <summary> /// <summary>
/// Map image layer. Fills the entire viewport with a map image, e.g. provided by a Web Map Service. /// Map image layer. Fills the viewport with a single map image, e.g. provided by a Web Map Service.
/// The image must be provided by the abstract GetImageAsync method. /// The image must be provided by the abstract GetImageAsync method.
/// </summary> /// </summary>
public abstract class MapImageLayer : MapPanel, IMapLayer public abstract class MapImageLayer : MapPanel, IMapLayer
{ {
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(
nameof(Description), typeof(string), typeof(MapImageLayer), new PropertyMetadata(null));
public static readonly DependencyProperty MinLatitudeProperty = DependencyProperty.Register( public static readonly DependencyProperty MinLatitudeProperty = DependencyProperty.Register(
nameof(MinLatitude), typeof(double), typeof(MapImageLayer), new PropertyMetadata(double.NaN)); nameof(MinLatitude), typeof(double), typeof(MapImageLayer), new PropertyMetadata(double.NaN));
@ -53,9 +56,6 @@ namespace MapControl
public static readonly DependencyProperty UpdateWhileViewportChangingProperty = DependencyProperty.Register( public static readonly DependencyProperty UpdateWhileViewportChangingProperty = DependencyProperty.Register(
nameof(UpdateWhileViewportChanging), typeof(bool), typeof(MapImageLayer), new PropertyMetadata(false)); nameof(UpdateWhileViewportChanging), typeof(bool), typeof(MapImageLayer), new PropertyMetadata(false));
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(
nameof(Description), typeof(string), typeof(MapImageLayer), new PropertyMetadata(null));
public static readonly DependencyProperty MapBackgroundProperty = DependencyProperty.Register( public static readonly DependencyProperty MapBackgroundProperty = DependencyProperty.Register(
nameof(MapBackground), typeof(Brush), typeof(MapImageLayer), new PropertyMetadata(null)); nameof(MapBackground), typeof(Brush), typeof(MapImageLayer), new PropertyMetadata(null));
@ -74,6 +74,16 @@ namespace MapControl
updateTimer.Tick += async (s, e) => await UpdateImageAsync(); updateTimer.Tick += async (s, e) => await UpdateImageAsync();
} }
/// <summary>
/// Description of the MapImageLayer.
/// Used to display copyright information on top of the map.
/// </summary>
public string Description
{
get { return (string)GetValue(DescriptionProperty); }
set { SetValue(DescriptionProperty, value); }
}
/// <summary> /// <summary>
/// Optional minimum latitude value. Default is NaN. /// Optional minimum latitude value. Default is NaN.
/// </summary> /// </summary>
@ -120,9 +130,9 @@ namespace MapControl
} }
/// <summary> /// <summary>
/// Relative size of the map image in relation to the current viewport size. /// Relative size of the map image in relation to the current view size.
/// Setting a value greater than one will let MapImageLayer request images that /// Setting a value greater than one will let MapImageLayer request images that
/// are larger than the viewport, in order to support smooth panning. /// are larger than the view, in order to support smooth panning.
/// </summary> /// </summary>
public double RelativeImageSize public double RelativeImageSize
{ {
@ -148,16 +158,6 @@ namespace MapControl
set { SetValue(UpdateWhileViewportChangingProperty, value); } set { SetValue(UpdateWhileViewportChangingProperty, value); }
} }
/// <summary>
/// Description of the MapImageLayer.
/// Used to display copyright information on top of the map.
/// </summary>
public string Description
{
get { return (string)GetValue(DescriptionProperty); }
set { SetValue(DescriptionProperty, value); }
}
/// <summary> /// <summary>
/// Optional foreground brush. /// Optional foreground brush.
/// Sets MapBase.Foreground if not null and the MapImageLayer is the base map layer. /// Sets MapBase.Foreground if not null and the MapImageLayer is the base map layer.
@ -258,7 +258,7 @@ namespace MapControl
var y = (ParentMap.RenderSize.Height - height) / 2d; var y = (ParentMap.RenderSize.Height - height) / 2d;
var rect = new Rect(x, y, width, height); var rect = new Rect(x, y, width, height);
BoundingBox = ParentMap.ViewportRectToBoundingBox(rect); BoundingBox = ParentMap.ViewRectToBoundingBox(rect);
if (BoundingBox != null) if (BoundingBox != null)
{ {

View file

@ -74,7 +74,7 @@ namespace MapControl
{ {
SelectItems(item => SelectItems(item =>
{ {
var pos = MapPanel.GetViewportPosition(ContainerFromItem(item)); var pos = MapPanel.GetViewPosition(ContainerFromItem(item));
return pos.HasValue && predicate(pos.Value); return pos.HasValue && predicate(pos.Value);
}); });
} }
@ -116,10 +116,10 @@ namespace MapControl
} }
else if (shiftKey && SelectedItem != null) else if (shiftKey && SelectedItem != null)
{ {
// Extended with Shift -> select items in viewport rectangle // Extended with Shift -> select items in view rectangle
var p1 = MapPanel.GetViewportPosition(ContainerFromItem(SelectedItem)); var p1 = MapPanel.GetViewPosition(ContainerFromItem(SelectedItem));
var p2 = MapPanel.GetViewportPosition(mapItem); var p2 = MapPanel.GetViewPosition(mapItem);
if (p1.HasValue && p2.HasValue) if (p1.HasValue && p2.HasValue)
{ {

View file

@ -58,9 +58,9 @@ namespace MapControl
element.SetValue(BoundingBoxProperty, value); element.SetValue(BoundingBoxProperty, value);
} }
public static Point? GetViewportPosition(FrameworkElement element) public static Point? GetViewPosition(FrameworkElement element)
{ {
return (Point?)element.GetValue(ViewportPositionProperty); return (Point?)element.GetValue(ViewPositionProperty);
} }
public MapBase ParentMap public MapBase ParentMap
@ -118,9 +118,9 @@ namespace MapControl
if (location != null) if (location != null)
{ {
var viewportPosition = ArrangeElement(element, location); var viewPosition = ArrangeElement(element, location);
SetViewportPosition(element, viewportPosition); SetViewPosition(element, viewPosition);
} }
else else
{ {
@ -135,7 +135,7 @@ namespace MapControl
ArrangeElement(element, finalSize); ArrangeElement(element, finalSize);
} }
SetViewportPosition(element, null); SetViewPosition(element, null);
} }
} }
} }
@ -145,13 +145,13 @@ namespace MapControl
private Point ArrangeElement(FrameworkElement element, Location location) private Point ArrangeElement(FrameworkElement element, Location location)
{ {
var pos = parentMap.LocationToViewportPoint(location); var pos = parentMap.LocationToView(location);
if (parentMap.MapProjection.IsNormalCylindrical && if (parentMap.MapProjection.IsNormalCylindrical &&
(pos.X < 0d || pos.X > parentMap.RenderSize.Width || (pos.X < 0d || pos.X > parentMap.RenderSize.Width ||
pos.Y < 0d || pos.Y > parentMap.RenderSize.Height)) pos.Y < 0d || pos.Y > parentMap.RenderSize.Height))
{ {
pos = parentMap.LocationToViewportPoint(new Location( pos = parentMap.LocationToView(new Location(
location.Latitude, location.Latitude,
Location.NearestLongitude(location.Longitude, parentMap.Center.Longitude))); Location.NearestLongitude(location.Longitude, parentMap.Center.Longitude)));
} }
@ -210,7 +210,7 @@ namespace MapControl
var location = projection.MapToLocation(center); var location = projection.MapToLocation(center);
location.Longitude = Location.NearestLongitude(location.Longitude, parentMap.Center.Longitude); location.Longitude = Location.NearestLongitude(location.Longitude, parentMap.Center.Longitude);
pos = parentMap.LocationToViewportPoint(location); pos = parentMap.LocationToView(location);
} }
rect.Width *= parentMap.ViewTransform.Scale; rect.Width *= parentMap.ViewTransform.Scale;
@ -229,14 +229,15 @@ namespace MapControl
element.Arrange(rect); element.Arrange(rect);
var rotateTransform = element.RenderTransform as RotateTransform; var rotateTransform = element.RenderTransform as RotateTransform;
var rotation = parentMap.ViewTransform.Rotation;
if (rotateTransform != null) if (rotateTransform != null)
{ {
rotateTransform.Angle = parentMap.Heading; rotateTransform.Angle = rotation;
} }
else if (parentMap.Heading != 0d) else if (rotation != 0d)
{ {
rotateTransform = new RotateTransform { Angle = parentMap.Heading }; rotateTransform = new RotateTransform { Angle = rotation };
element.RenderTransform = rotateTransform; element.RenderTransform = rotateTransform;
element.RenderTransformOrigin = new Point(0.5, 0.5); element.RenderTransformOrigin = new Point(0.5, 0.5);
} }

View file

@ -0,0 +1,142 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2020 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
#if WINDOWS_UWP
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
#else
using System.Windows;
using System.Windows.Media;
#endif
namespace MapControl
{
/// <summary>
/// A path element with a Data property that holds a Geometry in cartesian map coordinates
/// or view coordinates. Cartesian coordinates can optionally be relative to an origin Location.
/// </summary>
public partial class MapPath : IMapElement
{
public static readonly DependencyProperty LocationProperty = DependencyProperty.Register(
nameof(Location), typeof(Location), typeof(MapPath),
new PropertyMetadata(null, (o, e) => ((MapPath)o).LocationOrViewportChanged()));
private MapBase parentMap;
private MatrixTransform dataTransform;
private double longitudeOffset;
public MapPath()
{
MapPanel.InitMapElement(this);
}
/// <summary>
/// Gets or sets a Location that is used as
/// - either the origin point of a geometry specified in cartesian map units (meters)
/// - or as an optional value to constrain the view position of MapPaths with multiple
/// Locations (like MapPolyline or MapPolygon) to the visible map viewport, as done
/// for elements where the MapPanel.Location property is set.
/// </summary>
public Location Location
{
get { return (Location)GetValue(LocationProperty); }
set { SetValue(LocationProperty, value); }
}
public MapBase ParentMap
{
get { return parentMap; }
set
{
if (parentMap != null)
{
parentMap.ViewportChanged -= OnViewportChanged;
}
parentMap = value;
if (parentMap != null)
{
parentMap.ViewportChanged += OnViewportChanged;
}
LocationOrViewportChanged();
}
}
private void OnViewportChanged(object sender, ViewportChangedEventArgs e)
{
LocationOrViewportChanged();
}
private void LocationOrViewportChanged()
{
longitudeOffset = 0d;
if (parentMap != null && parentMap.MapProjection.IsNormalCylindrical && Location != null)
{
var viewPos = LocationToView(Location);
if (viewPos.X < 0d || viewPos.X > parentMap.RenderSize.Width ||
viewPos.Y < 0d || viewPos.Y > parentMap.RenderSize.Height)
{
longitudeOffset = Location.NearestLongitude(Location.Longitude, parentMap.Center.Longitude) - Location.Longitude;
}
}
UpdateData();
}
protected virtual void UpdateData()
{
if (parentMap != null && Data != null)
{
if (dataTransform == null)
{
Data.Transform = dataTransform = new MatrixTransform();
}
if (Location != null)
{
var viewPos = LocationToView(Location);
var scale = parentMap.GetScale(Location);
var matrix = new Matrix(scale.X, 0, 0, scale.Y, 0, 0);
matrix.Rotate(parentMap.Heading);
matrix.Translate(viewPos.X, viewPos.Y);
dataTransform.Matrix = matrix;
}
else
{
dataTransform.Matrix = parentMap.ViewTransform.MapToViewMatrix;
}
}
}
protected Point LocationToMap(Location location)
{
if (longitudeOffset != 0d)
{
location = new Location(location.Latitude, location.Longitude + longitudeOffset);
}
var point = parentMap.MapProjection.LocationToMap(location);
if (point.Y == double.PositiveInfinity)
{
point.Y = 1e9;
}
else if (point.X == double.NegativeInfinity)
{
point.Y = -1e9;
}
return point;
}
protected Point LocationToView(Location location)
{
return parentMap.ViewTransform.MapToView(LocationToMap(location));
}
}
}

View file

@ -17,7 +17,7 @@ namespace MapControl
/// <summary> /// <summary>
/// A polygon defined by a collection of Locations. /// A polygon defined by a collection of Locations.
/// </summary> /// </summary>
public class MapPolygon : MapShape public class MapPolygon : MapPath
{ {
public static readonly DependencyProperty LocationsProperty = DependencyProperty.Register( public static readonly DependencyProperty LocationsProperty = DependencyProperty.Register(
nameof(Locations), typeof(IEnumerable<Location>), typeof(MapPolygon), nameof(Locations), typeof(IEnumerable<Location>), typeof(MapPolygon),
@ -35,6 +35,11 @@ namespace MapControl
set { SetValue(LocationsProperty, value); } set { SetValue(LocationsProperty, value); }
} }
public MapPolygon()
{
Data = new PathGeometry();
}
protected override void UpdateData() protected override void UpdateData()
{ {
var figures = ((PathGeometry)Data).Figures; var figures = ((PathGeometry)Data).Figures;

View file

@ -17,7 +17,7 @@ namespace MapControl
/// <summary> /// <summary>
/// A polyline defined by a collection of Locations. /// A polyline defined by a collection of Locations.
/// </summary> /// </summary>
public class MapPolyline : MapShape public class MapPolyline : MapPath
{ {
public static readonly DependencyProperty LocationsProperty = DependencyProperty.Register( public static readonly DependencyProperty LocationsProperty = DependencyProperty.Register(
nameof(Locations), typeof(IEnumerable<Location>), typeof(MapPolyline), nameof(Locations), typeof(IEnumerable<Location>), typeof(MapPolyline),
@ -35,6 +35,11 @@ namespace MapControl
set { SetValue(LocationsProperty, value); } set { SetValue(LocationsProperty, value); }
} }
public MapPolyline()
{
Data = new PathGeometry();
}
protected override void UpdateData() protected override void UpdateData()
{ {
var figures = ((PathGeometry)Data).Figures; var figures = ((PathGeometry)Data).Figures;

View file

@ -59,9 +59,10 @@ namespace MapControl
{ {
var size = new Size(); var size = new Size();
if (ParentMap != null && ParentMap.ScaleTransform.ScaleX > 0d) if (ParentMap != null)
{ {
var length = MinWidth / ParentMap.ScaleTransform.ScaleX; var scale = ParentMap.GetScale(ParentMap.Center).X;
var length = MinWidth / scale;
var magnitude = Math.Pow(10d, Math.Floor(Math.Log10(length))); var magnitude = Math.Pow(10d, Math.Floor(Math.Log10(length)));
if (length / magnitude < 2d) if (length / magnitude < 2d)
@ -77,7 +78,7 @@ namespace MapControl
length = 10d * magnitude; length = 10d * magnitude;
} }
size.Width = length * ParentMap.ScaleTransform.ScaleX + StrokeThickness + Padding.Left + Padding.Right; size.Width = length * scale + StrokeThickness + Padding.Left + Padding.Right;
size.Height = 1.25 * FontSize + StrokeThickness + Padding.Top + Padding.Bottom; size.Height = 1.25 * FontSize + StrokeThickness + Padding.Top + Padding.Bottom;
var x1 = Padding.Left + StrokeThickness / 2d; var x1 = Padding.Left + StrokeThickness / 2d;

View file

@ -1,125 +0,0 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2020 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
#if WINDOWS_UWP
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
#else
using System.Windows;
using System.Windows.Media;
#endif
namespace MapControl
{
/// <summary>
/// Base class for MapPolyline and MapPolygon.
/// </summary>
public abstract partial class MapShape : IMapElement
{
public static readonly DependencyProperty LocationProperty = DependencyProperty.Register(
nameof(Location), typeof(Location), typeof(MapShape),
new PropertyMetadata(null, (o, e) => ((MapShape)o).LocationPropertyChanged()));
/// <summary>
/// Gets or sets an optional Location to constrain the viewport position to the visible
/// map viewport, as done for elements where the MapPanel.Location property is set.
/// </summary>
public Location Location
{
get { return (Location)GetValue(LocationProperty); }
set { SetValue(LocationProperty, value); }
}
private void LocationPropertyChanged()
{
if (parentMap != null)
{
UpdateData();
}
}
private MapBase parentMap;
public MapBase ParentMap
{
get { return parentMap; }
set
{
if (parentMap != null)
{
parentMap.ViewportChanged -= OnViewportChanged;
}
parentMap = value;
if (parentMap != null)
{
parentMap.ViewportChanged += OnViewportChanged;
}
UpdateData();
}
}
private void OnViewportChanged(object sender, ViewportChangedEventArgs e)
{
UpdateData();
}
protected abstract void UpdateData();
protected MapShape()
: this(new PathGeometry())
{
}
protected MapShape(Geometry data)
{
Data = data;
MapPanel.InitMapElement(this);
}
protected Point LocationToMap(Location location)
{
var point = parentMap.MapProjection.LocationToMap(location);
if (point.Y == double.PositiveInfinity)
{
point.Y = 1e9;
}
else if (point.X == double.NegativeInfinity)
{
point.Y = -1e9;
}
return point;
}
protected Point LocationToViewportPoint(Location location)
{
return parentMap.ViewTransform.MapToView(LocationToMap(location));
}
protected double GetLongitudeOffset()
{
var longitudeOffset = 0d;
if (parentMap.MapProjection.IsNormalCylindrical && Location != null)
{
var viewportPosition = LocationToViewportPoint(Location);
if (viewportPosition.X < 0d || viewportPosition.X > parentMap.RenderSize.Width ||
viewportPosition.Y < 0d || viewportPosition.Y > parentMap.RenderSize.Height)
{
var nearestLongitude = Location.NearestLongitude(Location.Longitude, parentMap.Center.Longitude);
longitudeOffset = nearestLongitude - Location.Longitude;
}
}
return longitudeOffset;
}
}
}

View file

@ -17,7 +17,7 @@ using System.Windows.Media;
namespace MapControl namespace MapControl
{ {
/// <summary> /// <summary>
/// Fills the map viewport with map tiles from a TileSource. /// Fills the viewport with map tiles from a TileSource.
/// </summary> /// </summary>
public class MapTileLayer : MapTileLayerBase public class MapTileLayer : MapTileLayerBase
{ {
@ -26,11 +26,6 @@ namespace MapControl
public static readonly Point TileMatrixTopLeft = new Point( public static readonly Point TileMatrixTopLeft = new Point(
-180d * MapProjection.Wgs84MetersPerDegree, 180d * MapProjection.Wgs84MetersPerDegree); -180d * MapProjection.Wgs84MetersPerDegree, 180d * MapProjection.Wgs84MetersPerDegree);
public static double TileMatrixScale(int zoomLevel)
{
return (TileSize << zoomLevel) / (360d * MapProjection.Wgs84MetersPerDegree);
}
/// <summary> /// <summary>
/// A default MapTileLayer using OpenStreetMap data. /// A default MapTileLayer using OpenStreetMap data.
/// </summary> /// </summary>
@ -117,18 +112,22 @@ namespace MapControl
// //
var tileMatrixOrigin = new Point(TileSize * TileMatrix.XMin, TileSize * TileMatrix.YMin); var tileMatrixOrigin = new Point(TileSize * TileMatrix.XMin, TileSize * TileMatrix.YMin);
var tileMatrixScale = ViewTransform.ZoomLevelToScale(TileMatrix.ZoomLevel);
((MatrixTransform)RenderTransform).Matrix = ParentMap.ViewTransform.GetTileLayerTransform( ((MatrixTransform)RenderTransform).Matrix = ParentMap.ViewTransform.GetTileLayerTransform(
TileMatrixScale(TileMatrix.ZoomLevel), TileMatrixTopLeft, tileMatrixOrigin); tileMatrixScale, TileMatrixTopLeft, tileMatrixOrigin);
} }
private bool SetTileMatrix() private bool SetTileMatrix()
{ {
var tileMatrixZoomLevel = (int)Math.Floor(ParentMap.ZoomLevel + 0.001); // avoid rounding issues var tileMatrixZoomLevel = (int)Math.Floor(ParentMap.ZoomLevel + 0.001); // avoid rounding issues
// bounds in tile pixels from viewport size var tileMatrixScale = ViewTransform.ZoomLevelToScale(tileMatrixZoomLevel);
// bounds in tile pixels from view size
// //
var tileBounds = ParentMap.ViewTransform.GetTileMatrixBounds( var tileBounds = ParentMap.ViewTransform.GetTileMatrixBounds(
TileMatrixScale(tileMatrixZoomLevel), TileMatrixTopLeft, ParentMap.RenderSize); tileMatrixScale, TileMatrixTopLeft, ParentMap.RenderSize);
// tile column and row index bounds // tile column and row index bounds
// //

View file

@ -2,6 +2,7 @@
// © 2020 Clemens Fischer // © 2020 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System;
#if WINDOWS_UWP #if WINDOWS_UWP
using Windows.Foundation; using Windows.Foundation;
using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media;
@ -13,22 +14,14 @@ using System.Windows.Media;
namespace MapControl namespace MapControl
{ {
/// <summary> /// <summary>
/// Defines the transformation between cartesian map coordinates and viewport coordinates. /// Defines the transformation between cartesian map coordinates in meters
/// and view coordinates in pixels.
/// </summary> /// </summary>
public class ViewTransform public class ViewTransform
{ {
/// <summary> /// <summary>
/// Gets the transform matrix from cartesian map coordinates to viewport coordinates. /// Gets the scaling factor from cartesian map coordinates to view coordinates,
/// </summary> /// i.e. pixels per meter.
public Matrix MapToViewMatrix { get; private set; }
/// <summary>
/// Gets the transform matrix from viewport coordinates to cartesian map coordinates.
/// </summary>
public Matrix ViewToMapMatrix { get; private set; }
/// <summary>
/// Gets the scaling factor from cartesian map coordinates to viewport coordinates.
/// </summary> /// </summary>
public double Scale { get; private set; } public double Scale { get; private set; }
@ -38,7 +31,17 @@ namespace MapControl
public double Rotation { get; private set; } public double Rotation { get; private set; }
/// <summary> /// <summary>
/// Transforms a Point from cartesian map coordinates to viewport coordinates. /// Gets the transform matrix from cartesian map coordinates to view coordinates.
/// </summary>
public Matrix MapToViewMatrix { get; private set; }
/// <summary>
/// Gets the transform matrix from view coordinates to cartesian map coordinates.
/// </summary>
public Matrix ViewToMapMatrix { get; private set; }
/// <summary>
/// Transforms a Point from cartesian map coordinates to view coordinates.
/// </summary> /// </summary>
public Point MapToView(Point point) public Point MapToView(Point point)
{ {
@ -46,14 +49,14 @@ namespace MapControl
} }
/// <summary> /// <summary>
/// Transforms a Point from viewport coordinates to cartesian map coordinates. /// Transforms a Point from view coordinates to cartesian map coordinates.
/// </summary> /// </summary>
public Point ViewToMap(Point point) public Point ViewToMap(Point point)
{ {
return ViewToMapMatrix.Transform(point); return ViewToMapMatrix.Transform(point);
} }
public void SetTransform(Point mapCenter, Point viewportCenter, double scale, double rotation) public void SetTransform(Point mapCenter, Point viewCenter, double scale, double rotation)
{ {
Scale = scale; Scale = scale;
Rotation = rotation; Rotation = rotation;
@ -61,7 +64,7 @@ namespace MapControl
var transform = new Matrix(Scale, 0d, 0d, -Scale, -Scale * mapCenter.X, Scale * mapCenter.Y); var transform = new Matrix(Scale, 0d, 0d, -Scale, -Scale * mapCenter.X, Scale * mapCenter.Y);
transform.Rotate(Rotation); transform.Rotate(Rotation);
transform.Translate(viewportCenter.X, viewportCenter.Y); transform.Translate(viewCenter.X, viewCenter.Y);
MapToViewMatrix = transform; MapToViewMatrix = transform;
@ -83,7 +86,7 @@ namespace MapControl
tileMatrixTopLeft.X + tileMatrixOrigin.X / tileMatrixScale, tileMatrixTopLeft.X + tileMatrixOrigin.X / tileMatrixScale,
tileMatrixTopLeft.Y - tileMatrixOrigin.Y / tileMatrixScale); tileMatrixTopLeft.Y - tileMatrixOrigin.Y / tileMatrixScale);
// tile matrix origin in viewport coordinates // tile matrix origin in view coordinates
// //
var viewOrigin = MapToView(mapOrigin); var viewOrigin = MapToView(mapOrigin);
@ -92,14 +95,14 @@ namespace MapControl
return transform; return transform;
} }
public Rect GetTileMatrixBounds(double tileMatrixScale, Point tileMatrixTopLeft, Size viewportSize) public Rect GetTileMatrixBounds(double tileMatrixScale, Point tileMatrixTopLeft, Size viewSize)
{ {
var transformScale = tileMatrixScale / Scale; var transformScale = tileMatrixScale / Scale;
var transform = new Matrix(transformScale, 0d, 0d, transformScale, 0d, 0d); var transform = new Matrix(transformScale, 0d, 0d, transformScale, 0d, 0d);
transform.Rotate(-Rotation); transform.Rotate(-Rotation);
// viewport origin in map coordinates // view origin in map coordinates
// //
var origin = ViewToMap(new Point()); var origin = ViewToMap(new Point());
@ -109,10 +112,20 @@ namespace MapControl
tileMatrixScale * (origin.X - tileMatrixTopLeft.X), tileMatrixScale * (origin.X - tileMatrixTopLeft.X),
tileMatrixScale * (tileMatrixTopLeft.Y - origin.Y)); tileMatrixScale * (tileMatrixTopLeft.Y - origin.Y));
// transform viewport bounds to tile pixel bounds // transform view bounds to tile pixel bounds
// //
return new MatrixTransform { Matrix = transform } return new MatrixTransform { Matrix = transform }
.TransformBounds(new Rect(0d, 0d, viewportSize.Width, viewportSize.Height)); .TransformBounds(new Rect(0d, 0d, viewSize.Width, viewSize.Height));
}
public static double ZoomLevelToScale(double zoomLevel)
{
return 256d * Math.Pow(2d, zoomLevel) / (360d * MapProjection.Wgs84MetersPerDegree);
}
public static double ScaleToZoomLevel(double scale)
{
return Math.Log(scale * 360d * MapProjection.Wgs84MetersPerDegree / 256d, 2d);
} }
} }
} }

View file

@ -16,7 +16,7 @@ namespace MapControl
/// <summary> /// <summary>
/// Indicates if the map projection has changed, i.e. if a MapTileLayer or MapImageLayer should /// Indicates if the map projection has changed, i.e. if a MapTileLayer or MapImageLayer should
/// be updated immediately, or MapShape Data in cartesian map coordinates should be recalculated. /// be updated immediately, or MapPath Data in cartesian map coordinates should be recalculated.
/// </summary> /// </summary>
public bool ProjectionChanged { get; } public bool ProjectionChanged { get; }

View file

@ -47,11 +47,11 @@ namespace MapControl
viewTransform.GetTileLayerTransform(TileMatrix.Scale, TileMatrix.TopLeft, tileMatrixOrigin); viewTransform.GetTileLayerTransform(TileMatrix.Scale, TileMatrix.TopLeft, tileMatrixOrigin);
} }
public bool SetBounds(ViewTransform viewTransform, Size viewportSize) public bool SetBounds(ViewTransform viewTransform, Size viewSize)
{ {
// bounds in tile pixels from viewport size // bounds in tile pixels from view size
// //
var bounds = viewTransform.GetTileMatrixBounds(TileMatrix.Scale, TileMatrix.TopLeft, viewportSize); var bounds = viewTransform.GetTileMatrixBounds(TileMatrix.Scale, TileMatrix.TopLeft, viewSize);
// tile column and row index bounds // tile column and row index bounds
// //

View file

@ -15,11 +15,11 @@ namespace MapControl
/// </summary> /// </summary>
public class WorldMercatorProjection : MapProjection public class WorldMercatorProjection : MapProjection
{ {
private static readonly double maxLatitude = YToLatitude(180d);
public static double ConvergenceTolerance = 1e-6; public static double ConvergenceTolerance = 1e-6;
public static int MaxIterations = 10; public static int MaxIterations = 10;
private static readonly double maxLatitude = YToLatitude(180d);
public WorldMercatorProjection() public WorldMercatorProjection()
{ {
CrsId = "EPSG:3395"; CrsId = "EPSG:3395";

View file

@ -39,6 +39,9 @@ namespace MapControl
nameof(TargetHeading), typeof(double), typeof(MapBase), nameof(TargetHeading), typeof(double), typeof(MapBase),
new PropertyMetadata(0d, (o, e) => ((MapBase)o).TargetHeadingPropertyChanged((double)e.NewValue))); new PropertyMetadata(0d, (o, e) => ((MapBase)o).TargetHeadingPropertyChanged((double)e.NewValue)));
public static readonly DependencyProperty ViewScaleProperty = DependencyProperty.Register(
nameof(ViewScale), typeof(double), typeof(MapBase), new PropertyMetadata(0d));
internal static readonly DependencyProperty CenterPointProperty = DependencyProperty.Register( internal static readonly DependencyProperty CenterPointProperty = DependencyProperty.Register(
"CenterPoint", typeof(Windows.Foundation.Point), typeof(MapBase), "CenterPoint", typeof(Windows.Foundation.Point), typeof(MapBase),
new PropertyMetadata(new Windows.Foundation.Point(), (o, e) => ((MapBase)o).CenterPointPropertyChanged((Windows.Foundation.Point)e.NewValue))); new PropertyMetadata(new Windows.Foundation.Point(), (o, e) => ((MapBase)o).CenterPointPropertyChanged((Windows.Foundation.Point)e.NewValue)));
@ -62,6 +65,11 @@ namespace MapControl
}; };
} }
private void SetViewScale(double scale)
{
SetValue(ViewScaleProperty, scale);
}
private void CenterPointPropertyChanged(Windows.Foundation.Point center) private void CenterPointPropertyChanged(Windows.Foundation.Point center)
{ {
CenterPointPropertyChanged(new Location(center.Y, center.X)); CenterPointPropertyChanged(new Location(center.Y, center.X));

View file

@ -107,6 +107,9 @@
<Compile Include="..\Shared\MapPanel.cs"> <Compile Include="..\Shared\MapPanel.cs">
<Link>MapPanel.cs</Link> <Link>MapPanel.cs</Link>
</Compile> </Compile>
<Compile Include="..\Shared\MapPath.cs">
<Link>MapPath.cs</Link>
</Compile>
<Compile Include="..\Shared\MapPolygon.cs"> <Compile Include="..\Shared\MapPolygon.cs">
<Link>MapPolygon.cs</Link> <Link>MapPolygon.cs</Link>
</Compile> </Compile>
@ -119,9 +122,6 @@
<Compile Include="..\Shared\MapScale.cs"> <Compile Include="..\Shared\MapScale.cs">
<Link>MapScale.cs</Link> <Link>MapScale.cs</Link>
</Compile> </Compile>
<Compile Include="..\Shared\MapShape.cs">
<Link>MapShape.cs</Link>
</Compile>
<Compile Include="..\Shared\MapTileLayer.cs"> <Compile Include="..\Shared\MapTileLayer.cs">
<Link>MapTileLayer.cs</Link> <Link>MapTileLayer.cs</Link>
</Compile> </Compile>
@ -188,7 +188,7 @@
<Compile Include="MapItemsControl.UWP.cs" /> <Compile Include="MapItemsControl.UWP.cs" />
<Compile Include="MapOverlay.UWP.cs" /> <Compile Include="MapOverlay.UWP.cs" />
<Compile Include="MapPanel.UWP.cs" /> <Compile Include="MapPanel.UWP.cs" />
<Compile Include="MapShape.UWP.cs" /> <Compile Include="MapPath.UWP.cs" />
<Compile Include="Matrix.UWP.cs" /> <Compile Include="Matrix.UWP.cs" />
<Compile Include="Point.UWP.cs" /> <Compile Include="Point.UWP.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />

View file

@ -4,11 +4,9 @@
using System; using System;
using Windows.Foundation; using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes; using Windows.UI.Xaml.Shapes;
using Windows.UI.Xaml.Data;
namespace MapControl namespace MapControl
{ {
@ -40,7 +38,7 @@ namespace MapControl
Children.Add(path); Children.Add(path);
} }
var bounds = map.ViewportRectToBoundingBox(new Rect(0d, 0d, map.RenderSize.Width, map.RenderSize.Height)); var bounds = map.ViewRectToBoundingBox(new Rect(0d, 0d, map.RenderSize.Width, map.RenderSize.Height));
var lineDistance = GetLineDistance(); var lineDistance = GetLineDistance();
var labelStart = new Location( var labelStart = new Location(
@ -66,14 +64,14 @@ namespace MapControl
{ {
var figure = new PathFigure var figure = new PathFigure
{ {
StartPoint = map.LocationToViewportPoint(new Location(lat, lineStart.Longitude)), StartPoint = map.LocationToView(new Location(lat, lineStart.Longitude)),
IsClosed = false, IsClosed = false,
IsFilled = false IsFilled = false
}; };
figure.Segments.Add(new LineSegment figure.Segments.Add(new LineSegment
{ {
Point = map.LocationToViewportPoint(new Location(lat, lineEnd.Longitude)) Point = map.LocationToView(new Location(lat, lineEnd.Longitude))
}); });
geometry.Figures.Add(figure); geometry.Figures.Add(figure);
@ -83,14 +81,14 @@ namespace MapControl
{ {
var figure = new PathFigure var figure = new PathFigure
{ {
StartPoint = map.LocationToViewportPoint(new Location(lineStart.Latitude, lon)), StartPoint = map.LocationToView(new Location(lineStart.Latitude, lon)),
IsClosed = false, IsClosed = false,
IsFilled = false IsFilled = false
}; };
figure.Segments.Add(new LineSegment figure.Segments.Add(new LineSegment
{ {
Point = map.LocationToViewportPoint(new Location(lineEnd.Latitude, lon)) Point = map.LocationToView(new Location(lineEnd.Latitude, lon))
}); });
geometry.Figures.Add(figure); geometry.Figures.Add(figure);
@ -111,22 +109,18 @@ namespace MapControl
} }
else else
{ {
var renderTransform = new TransformGroup(); label = new TextBlock { RenderTransform = new MatrixTransform() };
renderTransform.Children.Add(new TranslateTransform());
renderTransform.Children.Add(map.RotateTransform);
renderTransform.Children.Add(new TranslateTransform());
label = new TextBlock { RenderTransform = renderTransform };
if (FontFamily != null)
{
label.SetBinding(TextBlock.FontFamilyProperty, GetBinding(FontFamilyProperty, nameof(FontFamily)));
}
label.SetBinding(TextBlock.FontSizeProperty, GetBinding(FontSizeProperty, nameof(FontSize))); label.SetBinding(TextBlock.FontSizeProperty, GetBinding(FontSizeProperty, nameof(FontSize)));
label.SetBinding(TextBlock.FontStyleProperty, GetBinding(FontStyleProperty, nameof(FontStyle))); label.SetBinding(TextBlock.FontStyleProperty, GetBinding(FontStyleProperty, nameof(FontStyle)));
label.SetBinding(TextBlock.FontStretchProperty, GetBinding(FontStretchProperty, nameof(FontStretch))); label.SetBinding(TextBlock.FontStretchProperty, GetBinding(FontStretchProperty, nameof(FontStretch)));
label.SetBinding(TextBlock.FontWeightProperty, GetBinding(FontWeightProperty, nameof(FontWeight))); label.SetBinding(TextBlock.FontWeightProperty, GetBinding(FontWeightProperty, nameof(FontWeight)));
label.SetBinding(TextBlock.ForegroundProperty, GetBinding(ForegroundProperty, nameof(Foreground))); label.SetBinding(TextBlock.ForegroundProperty, GetBinding(ForegroundProperty, nameof(Foreground)));
if (FontFamily != null)
{
label.SetBinding(TextBlock.FontFamilyProperty, GetBinding(FontFamilyProperty, nameof(FontFamily)));
}
Children.Add(label); Children.Add(label);
} }
@ -135,10 +129,6 @@ namespace MapControl
label.Text = GetLabelText(lat, labelFormat, "NS") + "\n" + GetLabelText(Location.NormalizeLongitude(lon), labelFormat, "EW"); label.Text = GetLabelText(lat, labelFormat, "NS") + "\n" + GetLabelText(Location.NormalizeLongitude(lon), labelFormat, "EW");
label.Tag = new Location(lat, lon); label.Tag = new Location(lat, lon);
label.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); label.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
var translateTransform = (TranslateTransform)((TransformGroup)label.RenderTransform).Children[0];
translateTransform.X = StrokeThickness / 2d + 2d;
translateTransform.Y = -label.DesiredSize.Height / 2d;
} }
while (Children.Count > childIndex) while (Children.Count > childIndex)
@ -153,10 +143,14 @@ namespace MapControl
{ {
var label = (TextBlock)Children[i]; var label = (TextBlock)Children[i];
var location = (Location)label.Tag; var location = (Location)label.Tag;
var viewportTransform = (TranslateTransform)((TransformGroup)label.RenderTransform).Children[2]; var viewPosition = map.LocationToView(location);
var viewportPosition = map.LocationToViewportPoint(location); var matrix = new Matrix(1, 0, 0, 1, 0, 0);
viewportTransform.X = viewportPosition.X;
viewportTransform.Y = viewportPosition.Y; matrix.Translate(StrokeThickness / 2d + 2d, -label.DesiredSize.Height / 2d);
matrix.Rotate(map.ViewTransform.Rotation);
matrix.Translate(viewPosition.X, viewPosition.Y);
((MatrixTransform)label.RenderTransform).Matrix = matrix;
} }
} }
else if (path != null) else if (path != null)

View file

@ -20,8 +20,8 @@ namespace MapControl
public static readonly DependencyProperty ParentMapProperty = DependencyProperty.RegisterAttached( public static readonly DependencyProperty ParentMapProperty = DependencyProperty.RegisterAttached(
"ParentMap", typeof(MapBase), typeof(MapPanel), new PropertyMetadata(null, ParentMapPropertyChanged)); "ParentMap", typeof(MapBase), typeof(MapPanel), new PropertyMetadata(null, ParentMapPropertyChanged));
private static readonly DependencyProperty ViewportPositionProperty = DependencyProperty.RegisterAttached( private static readonly DependencyProperty ViewPositionProperty = DependencyProperty.RegisterAttached(
"ViewportPosition", typeof(Point?), typeof(MapPanel), new PropertyMetadata(null)); "ViewPosition", typeof(Point?), typeof(MapPanel), new PropertyMetadata(null));
public static void InitMapElement(FrameworkElement element) public static void InitMapElement(FrameworkElement element)
{ {
@ -61,9 +61,9 @@ namespace MapControl
?? FindParentMap(parent)); ?? FindParentMap(parent));
} }
private static void SetViewportPosition(FrameworkElement element, Point? viewportPosition) private static void SetViewPosition(FrameworkElement element, Point? viewPosition)
{ {
element.SetValue(ViewportPositionProperty, viewportPosition); element.SetValue(ViewPositionProperty, viewPosition);
} }
} }
} }

View file

@ -12,7 +12,7 @@ using Windows.UI.Xaml.Shapes;
namespace MapControl namespace MapControl
{ {
public abstract partial class MapShape : Path public partial class MapPath : Path
{ {
protected void DataCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) protected void DataCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{ {
@ -40,13 +40,7 @@ namespace MapControl
{ {
if (locations != null && locations.Count() >= 2) if (locations != null && locations.Count() >= 2)
{ {
var offset = GetLongitudeOffset(); var points = locations.Select(loc => LocationToView(loc)).ToList();
if (offset != 0d)
{
locations = locations.Select(loc => new Location(loc.Latitude, loc.Longitude + offset));
}
var points = locations.Select(loc => LocationToViewportPoint(loc)).ToList();
if (closed) if (closed)
{ {
points.Add(points[0]); points.Add(points[0]);

View file

@ -50,6 +50,16 @@ namespace MapControl
return new Vector(v1.X - v2.X, v1.Y - v2.Y); return new Vector(v1.X - v2.X, v1.Y - v2.Y);
} }
public static Vector operator *(double f, Vector v)
{
return new Vector(f * v.X, f * v.Y);
}
public static Vector operator *(Vector v, double f)
{
return new Vector(f * v.X, f * v.Y);
}
public static bool operator ==(Vector v1, Vector v2) public static bool operator ==(Vector v1, Vector v2)
{ {
return v1.X == v2.X && v1.Y == v2.Y; return v1.X == v2.X && v1.Y == v2.Y;

View file

@ -43,6 +43,11 @@ namespace MapControl
0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
(o, e) => ((MapBase)o).TargetHeadingPropertyChanged((double)e.NewValue))); (o, e) => ((MapBase)o).TargetHeadingPropertyChanged((double)e.NewValue)));
private static readonly DependencyPropertyKey ViewScalePropertyKey = DependencyProperty.RegisterReadOnly(
nameof(ViewScale), typeof(double), typeof(MapBase), new PropertyMetadata(0d));
public static readonly DependencyProperty ViewScaleProperty = ViewScalePropertyKey.DependencyProperty;
private static readonly DependencyProperty CenterPointProperty = DependencyProperty.Register( private static readonly DependencyProperty CenterPointProperty = DependencyProperty.Register(
"CenterPoint", typeof(Point), typeof(MapBase), "CenterPoint", typeof(Point), typeof(MapBase),
new PropertyMetadata(new Point(), (o, e) => ((MapBase)o).CenterPointPropertyChanged((Point)e.NewValue))); new PropertyMetadata(new Point(), (o, e) => ((MapBase)o).CenterPointPropertyChanged((Point)e.NewValue)));
@ -61,6 +66,11 @@ namespace MapControl
UpdateTransform(); UpdateTransform();
} }
private void SetViewScale(double scale)
{
SetValue(ViewScalePropertyKey, scale);
}
private void CenterPointPropertyChanged(Point center) private void CenterPointPropertyChanged(Point center)
{ {
CenterPointPropertyChanged(new Location(center.Y, center.X)); CenterPointPropertyChanged(new Location(center.Y, center.X));

View file

@ -56,7 +56,7 @@ namespace MapControl
private void DrawCylindricalGraticule(DrawingContext drawingContext, double lineDistance, string labelFormat) private void DrawCylindricalGraticule(DrawingContext drawingContext, double lineDistance, string labelFormat)
{ {
var boundingBox = ParentMap.ViewportRectToBoundingBox(new Rect(ParentMap.RenderSize)); var boundingBox = ParentMap.ViewRectToBoundingBox(new Rect(ParentMap.RenderSize));
var latLabelStart = Math.Ceiling(boundingBox.South / lineDistance) * lineDistance; var latLabelStart = Math.Ceiling(boundingBox.South / lineDistance) * lineDistance;
var lonLabelStart = Math.Ceiling(boundingBox.West / lineDistance) * lineDistance; var lonLabelStart = Math.Ceiling(boundingBox.West / lineDistance) * lineDistance;
var latLabels = new List<Label>((int)((boundingBox.North - latLabelStart) / lineDistance) + 1); var latLabels = new List<Label>((int)((boundingBox.North - latLabelStart) / lineDistance) + 1);
@ -72,8 +72,8 @@ namespace MapControl
CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, FontSize, Foreground, pixelsPerDip))); CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, FontSize, Foreground, pixelsPerDip)));
drawingContext.DrawLine(pen, drawingContext.DrawLine(pen,
ParentMap.LocationToViewportPoint(new Location(lat, boundingBox.West)), ParentMap.LocationToView(new Location(lat, boundingBox.West)),
ParentMap.LocationToViewportPoint(new Location(lat, boundingBox.East))); ParentMap.LocationToView(new Location(lat, boundingBox.East)));
} }
for (var lon = lonLabelStart; lon <= boundingBox.East; lon += lineDistance) for (var lon = lonLabelStart; lon <= boundingBox.East; lon += lineDistance)
@ -83,17 +83,17 @@ namespace MapControl
CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, FontSize, Foreground, pixelsPerDip))); CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, FontSize, Foreground, pixelsPerDip)));
drawingContext.DrawLine(pen, drawingContext.DrawLine(pen,
ParentMap.LocationToViewportPoint(new Location(boundingBox.South, lon)), ParentMap.LocationToView(new Location(boundingBox.South, lon)),
ParentMap.LocationToViewportPoint(new Location(boundingBox.North, lon))); ParentMap.LocationToView(new Location(boundingBox.North, lon)));
} }
foreach (var latLabel in latLabels) foreach (var latLabel in latLabels)
{ {
foreach (var lonLabel in lonLabels) foreach (var lonLabel in lonLabels)
{ {
var position = ParentMap.LocationToViewportPoint(new Location(latLabel.Position, lonLabel.Position)); var position = ParentMap.LocationToView(new Location(latLabel.Position, lonLabel.Position));
drawingContext.PushTransform(new RotateTransform(ParentMap.Heading, position.X, position.Y)); drawingContext.PushTransform(new RotateTransform(ParentMap.ViewTransform.Rotation, position.X, position.Y));
drawingContext.DrawText(latLabel.Text, drawingContext.DrawText(latLabel.Text,
new Point(position.X + StrokeThickness / 2d + 2d, position.Y - StrokeThickness / 2d - latLabel.Text.Height)); new Point(position.X + StrokeThickness / 2d + 2d, position.Y - StrokeThickness / 2d - latLabel.Text.Height));
drawingContext.DrawText(lonLabel.Text, drawingContext.DrawText(lonLabel.Text,

View file

@ -16,7 +16,7 @@ namespace MapControl
/// for the Polygons property if collection changes of the property itself and its /// for the Polygons property if collection changes of the property itself and its
/// elements are both supposed to trigger a UI update. /// elements are both supposed to trigger a UI update.
/// </summary> /// </summary>
public class MapMultiPolygon : MapShape public class MapMultiPolygon : MapPath
{ {
public static readonly DependencyProperty PolygonsProperty = DependencyProperty.Register( public static readonly DependencyProperty PolygonsProperty = DependencyProperty.Register(
nameof(Polygons), typeof(IEnumerable<IEnumerable<Location>>), typeof(MapMultiPolygon), nameof(Polygons), typeof(IEnumerable<IEnumerable<Location>>), typeof(MapMultiPolygon),
@ -31,6 +31,11 @@ namespace MapControl
set { SetValue(PolygonsProperty, value); } set { SetValue(PolygonsProperty, value); }
} }
public MapMultiPolygon()
{
Data = new PathGeometry();
}
protected override void UpdateData() protected override void UpdateData()
{ {
var figures = ((PathGeometry)Data).Figures; var figures = ((PathGeometry)Data).Figures;

View file

@ -20,11 +20,11 @@ namespace MapControl
"ParentMap", typeof(MapBase), typeof(MapPanel), "ParentMap", typeof(MapBase), typeof(MapPanel),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, ParentMapPropertyChanged)); new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, ParentMapPropertyChanged));
private static readonly DependencyPropertyKey ViewportPositionPropertyKey = DependencyProperty.RegisterAttachedReadOnly( private static readonly DependencyPropertyKey ViewPositionPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
"ViewportPosition", typeof(Point?), typeof(MapPanel), new PropertyMetadata()); "ViewPosition", typeof(Point?), typeof(MapPanel), new PropertyMetadata());
public static readonly DependencyProperty ParentMapProperty = ParentMapPropertyKey.DependencyProperty; public static readonly DependencyProperty ParentMapProperty = ParentMapPropertyKey.DependencyProperty;
public static readonly DependencyProperty ViewportPositionProperty = ViewportPositionPropertyKey.DependencyProperty; public static readonly DependencyProperty ViewPositionProperty = ViewPositionPropertyKey.DependencyProperty;
public static MapBase GetParentMap(FrameworkElement element) public static MapBase GetParentMap(FrameworkElement element)
{ {
@ -39,9 +39,9 @@ namespace MapControl
} }
} }
private static void SetViewportPosition(FrameworkElement element, Point? viewportPosition) private static void SetViewPosition(FrameworkElement element, Point? viewPosition)
{ {
element.SetValue(ViewportPositionPropertyKey, viewportPosition); element.SetValue(ViewPositionPropertyKey, viewPosition);
} }
} }
} }

View file

@ -12,9 +12,15 @@ using System.Windows.Shapes;
namespace MapControl namespace MapControl
{ {
public abstract partial class MapShape : Shape, IWeakEventListener public partial class MapPath : Shape, IWeakEventListener
{ {
public Geometry Data { get; } public static readonly DependencyProperty DataProperty = Path.DataProperty.AddOwner(typeof(MapPath));
public Geometry Data
{
get { return (Geometry)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
protected override Geometry DefiningGeometry protected override Geometry DefiningGeometry
{ {
@ -48,13 +54,7 @@ namespace MapControl
{ {
if (locations != null && locations.Count() >= 2) if (locations != null && locations.Count() >= 2)
{ {
var offset = GetLongitudeOffset(); var points = locations.Select(loc => LocationToView(loc));
if (offset != 0d)
{
locations = locations.Select(loc => new Location(loc.Latitude, loc.Longitude + offset));
}
var points = locations.Select(loc => LocationToViewportPoint(loc));
var figure = new PathFigure var figure = new PathFigure
{ {
StartPoint = points.First(), StartPoint = points.First(),

View file

@ -29,7 +29,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MapProjections", "MapProjec
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapProjections.WPF", "MapProjections\WPF\MapProjections.WPF.csproj", "{426C21C0-5F14-491F-BCD1-6D2993510420}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapProjections.WPF", "MapProjections\WPF\MapProjections.WPF.csproj", "{426C21C0-5F14-491F-BCD1-6D2993510420}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfApplication", "SampleApps\WpfApplication\WpfApplication.csproj", "{F92DA93D-75DB-4308-A5F9-6B4C3908A675}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfApplication", "SampleApps\WpfApplication\WpfApplication.csproj", "{F92DA93D-75DB-4308-A5F9-6B4C3908A675}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapProjections.UWP", "MapProjections\UWP\MapProjections.UWP.csproj", "{9EE69591-5EDC-45E3-893E-2F9A4B82D538}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapProjections.UWP", "MapProjections\UWP\MapProjections.UWP.csproj", "{9EE69591-5EDC-45E3-893E-2F9A4B82D538}"
EndProject EndProject
@ -45,8 +45,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLiteCache.UWP", "SQLiteCa
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLiteCache.WPF", "SQLiteCache\WPF\SQLiteCache.WPF.csproj", "{0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLiteCache.WPF", "SQLiteCache\WPF\SQLiteCache.WPF.csproj", "{0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfCoreApp", "SampleApps\WpfCoreApp\WpfCoreApp.csproj", "{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -347,26 +345,6 @@ Global
{0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Release|x64.Build.0 = Release|Any CPU {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Release|x64.Build.0 = Release|Any CPU
{0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Release|x86.ActiveCfg = Release|Any CPU {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Release|x86.ActiveCfg = Release|Any CPU
{0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Release|x86.Build.0 = Release|Any CPU {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349}.Release|x86.Build.0 = Release|Any CPU
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}.Debug|ARM.ActiveCfg = Debug|Any CPU
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}.Debug|ARM.Build.0 = Debug|Any CPU
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}.Debug|ARM64.Build.0 = Debug|Any CPU
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}.Debug|x64.ActiveCfg = Debug|Any CPU
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}.Debug|x64.Build.0 = Debug|Any CPU
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}.Debug|x86.ActiveCfg = Debug|Any CPU
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}.Debug|x86.Build.0 = Debug|Any CPU
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}.Release|Any CPU.Build.0 = Release|Any CPU
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}.Release|ARM.ActiveCfg = Release|Any CPU
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}.Release|ARM.Build.0 = Release|Any CPU
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}.Release|ARM64.ActiveCfg = Release|Any CPU
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}.Release|ARM64.Build.0 = Release|Any CPU
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}.Release|x64.ActiveCfg = Release|Any CPU
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}.Release|x64.Build.0 = Release|Any CPU
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}.Release|x86.ActiveCfg = Release|Any CPU
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -386,7 +364,6 @@ Global
{BE08B7BC-8C89-4837-BCE7-EDDDABEAB372} = {2FDC8B91-FB95-4C57-8183-63587FBFE180} {BE08B7BC-8C89-4837-BCE7-EDDDABEAB372} = {2FDC8B91-FB95-4C57-8183-63587FBFE180}
{56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1} = {96FD1258-1597-48A2-8D64-1ADAE13E886A} {56DFA7CF-F31D-45CE-9C36-DA8DBB8413B1} = {96FD1258-1597-48A2-8D64-1ADAE13E886A}
{0109C2F0-BA2C-420F-B2CA-DB5B29B1A349} = {96FD1258-1597-48A2-8D64-1ADAE13E886A} {0109C2F0-BA2C-420F-B2CA-DB5B29B1A349} = {96FD1258-1597-48A2-8D64-1ADAE13E886A}
{43380AAB-EE6E-40BF-90EB-FFD86F885CAF} = {8F2103C2-78AF-4810-8FB9-67572F50C8FC}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {458346DD-B23F-4FDC-8F9D-A10F1882A4DB} SolutionGuid = {458346DD-B23F-4FDC-8F9D-A10F1882A4DB}

View file

@ -66,7 +66,7 @@ namespace ProjectionDemo
var map = (MapBase)sender; var map = (MapBase)sender;
var pos = e.GetPosition(map); var pos = e.GetPosition(map);
viewModel.PushpinLocation = map.ViewportPointToLocation(pos); viewModel.PushpinLocation = map.ViewToLocation(pos);
} }
} }

View file

@ -130,6 +130,15 @@
<map:MapItemsControl ItemsSource="{Binding Pushpins}" <map:MapItemsControl ItemsSource="{Binding Pushpins}"
ItemContainerStyle="{StaticResource PushpinItemStyle}"/> ItemContainerStyle="{StaticResource PushpinItemStyle}"/>
<map:MapPath Stroke="Blue" StrokeThickness="3" Fill="#1F007F00">
<map:MapPath.Location>
<map:Location Latitude="53.5" Longitude="8.2"/>
</map:MapPath.Location>
<map:MapPath.Data>
<EllipseGeometry RadiusX="1852" RadiusY="1852"/>
</map:MapPath.Data>
</map:MapPath>
<map:Pushpin Background="Yellow" Foreground="Blue" Content="N 53° 30' E 8° 12'"> <map:Pushpin Background="Yellow" Foreground="Blue" Content="N 53° 30' E 8° 12'">
<map:MapPanel.Location> <map:MapPanel.Location>
<map:Location Latitude="53.5" Longitude="8.2"/> <map:Location Latitude="53.5" Longitude="8.2"/>

View file

@ -1,12 +1,11 @@
<Window <Window x:Class="WpfApplication.MainWindow"
x:Class="WpfApplication.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:map="clr-namespace:MapControl;assembly=MapControl.WPF"
xmlns:map="clr-namespace:MapControl;assembly=MapControl.WPF" xmlns:vm="clr-namespace:ViewModel"
xmlns:vm="clr-namespace:ViewModel" xmlns:local="clr-namespace:WpfApplication"
xmlns:local="clr-namespace:WpfApplication" Title="XAML MapControl - WPF Test Application" Height="600" Width="900"
Title="XAML MapControl - WPF Test Application" Height="600" Width="800" Stylus.IsPressAndHoldEnabled="False">
Stylus.IsPressAndHoldEnabled="False">
<Window.Resources> <Window.Resources>
<local:LocationToVisibilityConverter x:Key="LocationToVisibilityConverter"/> <local:LocationToVisibilityConverter x:Key="LocationToVisibilityConverter"/>
@ -88,7 +87,7 @@
<Setter.Value> <Setter.Value>
<MultiBinding Converter="{StaticResource LocationToVisibilityConverter}"> <MultiBinding Converter="{StaticResource LocationToVisibilityConverter}">
<Binding Path="(map:MapPanel.ParentMap)" RelativeSource="{RelativeSource Self}"/> <Binding Path="(map:MapPanel.ParentMap)" RelativeSource="{RelativeSource Self}"/>
<Binding Path="(map:MapPanel.ViewportPosition)" RelativeSource="{RelativeSource Self}"/> <Binding Path="(map:MapPanel.ViewPosition)" RelativeSource="{RelativeSource Self}"/>
</MultiBinding> </MultiBinding>
</Setter.Value> </Setter.Value>
</Setter> </Setter>
@ -159,17 +158,17 @@
<map:Pushpin map:MapPanel.Location="35,33" Content="Cyprus"/> <map:Pushpin map:MapPanel.Location="35,33" Content="Cyprus"/>
<map:Pushpin map:MapPanel.Location="28.25,-16.5" Content="Tenerife"/> <map:Pushpin map:MapPanel.Location="28.25,-16.5" Content="Tenerife"/>
<Path map:MapPanel.Location="53.5,8.2" Stroke="Blue" StrokeThickness="3" Fill="#1F007F00"> <map:MapPath Location="53.5,8.2" Stroke="Blue" StrokeThickness="3" Fill="#1F007F00">
<Path.Data> <map:MapPath.Data>
<EllipseGeometry RadiusX="1852" RadiusY="1852" Transform="{Binding ScaleRotateTransform, ElementName=map}"/> <EllipseGeometry RadiusX="1852" RadiusY="1852"/>
</Path.Data> </map:MapPath.Data>
</Path> </map:MapPath>
<map:Pushpin map:MapPanel.Location="53.5,8.2" Background="Yellow" Foreground="Blue" Content="N 53°30' E 8°12'"> <map:Pushpin map:MapPanel.Location="53.5,8.2" Background="Yellow" Foreground="Blue" Content="N 53°30' E 8°12'">
<map:Pushpin.Visibility> <map:Pushpin.Visibility>
<MultiBinding Converter="{StaticResource LocationToVisibilityConverter}"> <MultiBinding Converter="{StaticResource LocationToVisibilityConverter}">
<Binding Path="(map:MapPanel.ParentMap)" RelativeSource="{RelativeSource Self}"/> <Binding Path="(map:MapPanel.ParentMap)" RelativeSource="{RelativeSource Self}"/>
<Binding Path="(map:MapPanel.ViewportPosition)" RelativeSource="{RelativeSource Self}"/> <Binding Path="(map:MapPanel.ViewPosition)" RelativeSource="{RelativeSource Self}"/>
</MultiBinding> </MultiBinding>
</map:Pushpin.Visibility> </map:Pushpin.Visibility>
</map:Pushpin> </map:Pushpin>

View file

@ -12,7 +12,7 @@ namespace WpfApplication
public MainWindow() public MainWindow()
{ {
ImageLoader.HttpClient.DefaultRequestHeaders.Add("User-Agent", "XAML Map Control Test Application"); ImageLoader.HttpClient.DefaultRequestHeaders.Add("User-Agent", "XAML Map Control Test Application");
//TileImageLoader.Cache = new MapControl.Caching.ImageFileCache(TileImageLoader.DefaultCacheFolder); TileImageLoader.Cache = new MapControl.Caching.ImageFileCache(TileImageLoader.DefaultCacheFolder);
//TileImageLoader.Cache = new MapControl.Caching.FileDbCache(TileImageLoader.DefaultCacheFolder); //TileImageLoader.Cache = new MapControl.Caching.FileDbCache(TileImageLoader.DefaultCacheFolder);
//TileImageLoader.Cache = new MapControl.Caching.SQLiteCache(TileImageLoader.DefaultCacheFolder); //TileImageLoader.Cache = new MapControl.Caching.SQLiteCache(TileImageLoader.DefaultCacheFolder);
//TileImageLoader.Cache = null; //TileImageLoader.Cache = null;
@ -26,7 +26,7 @@ namespace WpfApplication
{ {
//map.ZoomMap(e.GetPosition(map), Math.Floor(map.ZoomLevel + 1.5)); //map.ZoomMap(e.GetPosition(map), Math.Floor(map.ZoomLevel + 1.5));
//map.ZoomToBounds(new BoundingBox(53, 7, 54, 9)); //map.ZoomToBounds(new BoundingBox(53, 7, 54, 9));
map.TargetCenter = map.ViewportPointToLocation(e.GetPosition(map)); map.TargetCenter = map.ViewToLocation(e.GetPosition(map));
} }
} }
@ -40,7 +40,7 @@ namespace WpfApplication
private void MapMouseMove(object sender, MouseEventArgs e) private void MapMouseMove(object sender, MouseEventArgs e)
{ {
var location = map.ViewportPointToLocation(e.GetPosition(map)); var location = map.ViewToLocation(e.GetPosition(map));
var latitude = (int)Math.Round(location.Latitude * 60000d); var latitude = (int)Math.Round(location.Latitude * 60000d);
var longitude = (int)Math.Round(Location.NormalizeLongitude(location.Longitude) * 60000d); var longitude = (int)Math.Round(Location.NormalizeLongitude(location.Longitude) * 60000d);
var latHemisphere = 'N'; var latHemisphere = 'N';

View file

@ -1,14 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("WPF Sample Application")]
[assembly: AssemblyDescription("XAML Map Control WPF Sample Application")]
[assembly: AssemblyProduct("XAML Map Control")]
[assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("5.0.0")]
[assembly: AssemblyFileVersion("5.0.0")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]

View file

@ -1,110 +1,41 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <PropertyGroup>
<PropertyGroup> <OutputType>WinExe</OutputType>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFrameworks>netcoreapp3.1;net48</TargetFrameworks>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <UseWPF>true</UseWPF>
<ProjectGuid>{F92DA93D-75DB-4308-A5F9-6B4C3908A675}</ProjectGuid> <RootNamespace>WpfApplication</RootNamespace>
<OutputType>WinExe</OutputType> <Product>XAML Map Control</Product>
<AppDesignerFolder>Properties</AppDesignerFolder> <Version>5.0.0</Version>
<RootNamespace>WpfApplication</RootNamespace> <Description>XAML Map Control WPF Sample Application</Description>
<AssemblyName>WpfApplication</AssemblyName> <Authors>Clemens Fischer</Authors>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion> <Copyright>Copyright © 2020 Clemens Fischer</Copyright>
<FileAlignment>512</FileAlignment> </PropertyGroup>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <DefineConstants>DEBUG</DefineConstants>
<TargetFrameworkProfile /> </PropertyGroup>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
</PropertyGroup> <DefineConstants></DefineConstants>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> </PropertyGroup>
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols> <ItemGroup>
<DebugType>full</DebugType> <Compile Include="..\Shared\MapLayers.cs" Link="MapLayers.cs" />
<Optimize>false</Optimize> <Compile Include="..\Shared\MapViewModel.cs" Link="MapViewModel.cs" />
<OutputPath>bin\Debug\</OutputPath> </ItemGroup>
<DefineConstants>DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ItemGroup>
<WarningLevel>4</WarningLevel> <ProjectReference Include="..\..\MapControl\WPF\MapControl.WPF.csproj" />
</PropertyGroup> </ItemGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget> <ItemGroup>
<DebugType>pdbonly</DebugType> <Resource Include="..\Shared\10_535_330.jpg" Link="10_535_330.jpg" />
<Optimize>true</Optimize> </ItemGroup>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants> <ItemGroup Condition="'$(TargetFramework)' == 'net48'">
</DefineConstants> <Reference Include="System.Net.Http" />
<ErrorReport>prompt</ErrorReport> <Reference Include="System.Runtime.Caching" />
<WarningLevel>4</WarningLevel> </ItemGroup>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Runtime.Caching" />
<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>
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="..\Shared\MapLayers.cs">
<Link>MapLayers.cs</Link>
</Compile>
<Compile Include="..\Shared\MapViewModel.cs">
<Link>MapViewModel.cs</Link>
</Compile>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="LocationToVisibilityConverter.cs" />
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="OutlinedText.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<AppDesigner Include="Properties\" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Resource Include="..\Shared\10_535_330.jpg">
<Link>10_535_330.jpg</Link>
</Resource>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\MapControl\WPF\MapControl.WPF.csproj">
<Project>{62f1726b-3144-49f4-8bcc-94160a3b2186}</Project>
<Name>MapControl.WPF</Name>
</ProjectReference>
<ProjectReference Include="..\..\SQLiteCache\WPF\SQLiteCache.WPF.csproj">
<Project>{0109c2f0-ba2c-420f-b2ca-db5b29b1a349}</Project>
<Name>SQLiteCache.WPF</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> </Project>

View file

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup /> <PropertyGroup>
<ActiveDebugFramework>netcoreapp3.1</ActiveDebugFramework>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<ApplicationDefinition Update="App.xaml"> <ApplicationDefinition Update="App.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>

View file

@ -1,7 +0,0 @@
<Application x:Class="WpfCoreApp.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

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

View file

@ -1,36 +0,0 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using MapControl;
namespace WpfCoreApp
{
public class LocationToVisibilityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var visibility = Visibility.Hidden;
if (values.Length == 2 && values[0] is MapBase && values[1] is Point?)
{
var parentMap = (MapBase)values[0];
var position = (Point?)values[1];
if (position.HasValue &&
position.Value.X >= 0d && position.Value.X <= parentMap.ActualWidth &&
position.Value.Y >= 0d && position.Value.Y <= parentMap.ActualHeight)
{
visibility = Visibility.Visible;
}
}
return visibility;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
}

View file

@ -1,221 +0,0 @@
<Window x:Class="WpfCoreApp.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.WPF"
xmlns:vm="clr-namespace:ViewModel"
xmlns:local="clr-namespace:WpfCoreApp"
Title="XAML MapControl - WPF .NET Core 3.0 Test Application" Height="600" Width="800">
<Window.Resources>
<local:LocationToVisibilityConverter x:Key="LocationToVisibilityConverter"/>
<DataTemplate x:Key="PolylineItemTemplate">
<map:MapPolyline Locations="{Binding Locations}" Stroke="Red" StrokeThickness="3"/>
</DataTemplate>
<Style x:Key="PolylineItemStyle" TargetType="map:MapItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="map:MapItem">
<map:MapPolyline Locations="{Binding Locations}" Stroke="Red" StrokeThickness="3"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="PointItemStyle" TargetType="map:MapItem">
<!--<EventSetter Event="TouchDown" Handler="MapItemTouchDown"/>-->
<Setter Property="map:MapPanel.Location" Value="{Binding Location}"/>
<Setter Property="Foreground" Value="Black"/>
<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="0.7" Duration="0:0:0.1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected"/>
<VisualState x:Name="Selected">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="selectedPath" Storyboard.TargetProperty="Opacity" To="0.7" Duration="0:0:0.1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Path x:Name="selectedPath" Fill="White" Opacity="0">
<Path.Data>
<EllipseGeometry RadiusX="15" RadiusY="15"/>
</Path.Data>
</Path>
<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="15" Canvas.Top="-8">
<Rectangle x:Name="labelBackground" Fill="White" Opacity="0"/>
<local:OutlinedText Margin="1" OutlineThickness="1.5" Text="{Binding Name}"/>
</Grid>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Panel.ZIndex" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="PushpinItemStyle" TargetType="map:MapItem">
<EventSetter Event="TouchDown" Handler="MapItemTouchDown"/>
<Setter Property="map:MapPanel.Location" Value="{Binding Location}"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter="{StaticResource LocationToVisibilityConverter}">
<Binding Path="(map:MapPanel.ParentMap)" RelativeSource="{RelativeSource Self}"/>
<Binding Path="(map:MapPanel.ViewportPosition)" RelativeSource="{RelativeSource Self}"/>
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="map:MapItem">
<map:Pushpin Content="{Binding Name}" Foreground="{TemplateBinding Foreground}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Panel.ZIndex" Value="1"/>
<Setter Property="Foreground" Value="OrangeRed"/>
</Trigger>
</Style.Triggers>
</Style>
<map:WebMercatorProjection x:Key="WebMercatorProjection"/>
<map:WorldMercatorProjection x:Key="WorldMercatorProjection"/>
<map:EquirectangularProjection x:Key="EquirectangularProjection"/>
<map:OrthographicProjection x:Key="OrthographicProjection"/>
<map:GnomonicProjection x:Key="GnomonicProjection"/>
<map:StereographicProjection x:Key="StereographicProjection"/>
</Window.Resources>
<Window.DataContext>
<vm:MapViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<map:Map x:Name="map" ZoomLevel="11" MaxZoomLevel="21"
Center="{Binding MapCenter}"
MapLayer="{Binding MapLayers.CurrentMapLayer}"
MapProjection="{Binding SelectedValue, ElementName=projectionComboBox,
FallbackValue={StaticResource WebMercatorProjection},
TargetNullValue={StaticResource WebMercatorProjection}}"
MouseLeftButtonDown="MapMouseLeftButtonDown"
MouseRightButtonDown="MapMouseRightButtonDown"
MouseMove="MapMouseMove" MouseLeave="MapMouseLeave"
ManipulationInertiaStarting="MapManipulationInertiaStarting">
<Image x:Name="mapImage" Source="10_535_330.jpg" Opacity="0.5" Stretch="Fill"
map:MapPanel.BoundingBox="53.54031,8.08594,53.74871,8.43750"/>
<map:MapGraticule x:Name="mapGraticule" Opacity="0.6"/>
<map:MapScale HorizontalAlignment="Left" VerticalAlignment="Bottom"/>
<!-- use ItemTemplate or ItemContainerStyle alternatively -->
<map:MapItemsControl ItemsSource="{Binding Polylines}"
ItemTemplate="{StaticResource PolylineItemTemplate}"/>
<!--<map:MapItemsControl ItemsSource="{Binding Polylines}"
ItemContainerStyle="{StaticResource PolylineItemStyle}"/>-->
<map:MapItemsControl ItemsSource="{Binding Points}"
ItemContainerStyle="{StaticResource PointItemStyle}"
SelectionMode="Extended"/>
<map:MapItemsControl ItemsSource="{Binding Pushpins}"
ItemContainerStyle="{StaticResource PushpinItemStyle}"/>
<map:Pushpin map:MapPanel.Location="65,-18" Content="Iceland"/>
<map:Pushpin map:MapPanel.Location="71,25" Content="Norway"/>
<map:Pushpin map:MapPanel.Location="35,33" Content="Cyprus"/>
<map:Pushpin map:MapPanel.Location="28.25,-16.5" Content="Tenerife"/>
<Path map:MapPanel.Location="53.5,8.2" Stroke="Blue" StrokeThickness="3" Fill="#1F007F00">
<Path.Data>
<EllipseGeometry RadiusX="1852" RadiusY="1852" Transform="{Binding ScaleRotateTransform, ElementName=map}"/>
</Path.Data>
</Path>
<map:Pushpin map:MapPanel.Location="53.5,8.2" Background="Yellow" Foreground="Blue" Content="N 53°30' E 8°12'">
<map:Pushpin.Visibility>
<MultiBinding Converter="{StaticResource LocationToVisibilityConverter}">
<Binding Path="(map:MapPanel.ParentMap)" RelativeSource="{RelativeSource Self}"/>
<Binding Path="(map:MapPanel.ViewportPosition)" RelativeSource="{RelativeSource Self}"/>
</MultiBinding>
</map:Pushpin.Visibility>
</map:Pushpin>
</map:Map>
<Border HorizontalAlignment="Right" VerticalAlignment="Bottom" Background="#7FFFFFFF">
<TextBlock Margin="2" FontSize="10" map:HyperlinkText.InlinesSource="{Binding MapLayer.Description, ElementName=map}"/>
</Border>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="mouseLocation" Margin="5" VerticalAlignment="Bottom" FontFamily="Segoe UI Mono"/>
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
<StackPanel Margin="5">
<TextBlock Text="Zoom Level" Margin="0,0,0,2" HorizontalAlignment="Center" Foreground="Gray" FontSize="10"/>
<Slider ToolTip="Zoom Level" Width="100" VerticalAlignment="Center" SmallChange="0.01"
Minimum="{Binding MinZoomLevel, ElementName=map}"
Maximum="{Binding MaxZoomLevel, ElementName=map}"
Value="{Binding TargetZoomLevel, ElementName=map}"/>
</StackPanel>
<StackPanel Margin="5">
<TextBlock Text="Heading" Margin="0,0,0,2" HorizontalAlignment="Center" Foreground="Gray" FontSize="10"/>
<Slider ToolTip="Heading" Width="100" VerticalAlignment="Center" SmallChange="5" LargeChange="45"
Minimum="0" Maximum="360" Value="{Binding TargetHeading, ElementName=map}"/>
</StackPanel>
<StackPanel Margin="5">
<TextBlock Text="Image Opacity" Margin="0,0,0,2" HorizontalAlignment="Center" Foreground="Gray" FontSize="10"/>
<Slider ToolTip="Image Opacity" Width="100" VerticalAlignment="Center"
Minimum="0" Maximum="1" Value="{Binding Opacity, ElementName=mapImage}"/>
</StackPanel>
<CheckBox ToolTip="Seamarks Overlay" Margin="7" VerticalAlignment="Bottom" Content="Seamarks"
Checked="SeamarksChecked" Unchecked="SeamarksUnchecked"/>
<ComboBox ToolTip="Map Layer" Width="200" Margin="5" VerticalAlignment="Bottom"
ItemsSource="{Binding MapLayers.MapLayerNames}"
SelectedItem="{Binding MapLayers.CurrentMapLayerName}"/>
<ComboBox x:Name="projectionComboBox" ToolTip="Map Projection" Width="120" Margin="5" VerticalAlignment="Bottom"
SelectedValuePath="Tag" SelectedIndex="0">
<ComboBoxItem Content="Web Mercator" Tag="{StaticResource WebMercatorProjection}"/>
<ComboBoxItem Content="World Mercator" Tag="{StaticResource WorldMercatorProjection}"/>
<ComboBoxItem Content="Equirectangular" Tag="{StaticResource EquirectangularProjection}"/>
<ComboBoxItem Content="Orthographic" Tag="{StaticResource OrthographicProjection}"/>
<ComboBoxItem Content="Gnomonic" Tag="{StaticResource GnomonicProjection}"/>
<ComboBoxItem Content="Stereographic" Tag="{StaticResource StereographicProjection}"/>
</ComboBox>
</StackPanel>
</Grid>
</Grid>
</Window>

View file

@ -1,91 +0,0 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Input;
using MapControl;
using ViewModel;
namespace WpfCoreApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
ImageLoader.HttpClient.DefaultRequestHeaders.Add("User-Agent", "XAML Map Control Test Application");
//TileImageLoader.Cache = new MapControl.Caching.ImageFileCache(TileImageLoader.DefaultCacheFolder);
InitializeComponent();
}
private void MapMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 2)
{
//map.ZoomMap(e.GetPosition(map), Math.Floor(map.ZoomLevel + 1.5));
//map.ZoomToBounds(new BoundingBox(53, 7, 54, 9));
map.TargetCenter = map.ViewportPointToLocation(e.GetPosition(map));
}
}
private void MapMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 2)
{
//map.ZoomMap(e.GetPosition(map), Math.Ceiling(map.ZoomLevel - 1.5));
}
}
private void MapMouseMove(object sender, MouseEventArgs e)
{
var location = map.ViewportPointToLocation(e.GetPosition(map));
var latitude = (int)Math.Round(location.Latitude * 60000d);
var longitude = (int)Math.Round(Location.NormalizeLongitude(location.Longitude) * 60000d);
var latHemisphere = 'N';
var lonHemisphere = 'E';
if (latitude < 0)
{
latitude = -latitude;
latHemisphere = 'S';
}
if (longitude < 0)
{
longitude = -longitude;
lonHemisphere = 'W';
}
mouseLocation.Text = string.Format(CultureInfo.InvariantCulture,
"{0} {1:00} {2:00.000}\n{3} {4:000} {5:00.000}",
latHemisphere, latitude / 60000, (latitude % 60000) / 1000d,
lonHemisphere, longitude / 60000, (longitude % 60000) / 1000d);
}
private void MapMouseLeave(object sender, MouseEventArgs e)
{
mouseLocation.Text = string.Empty;
}
private void MapManipulationInertiaStarting(object sender, ManipulationInertiaStartingEventArgs e)
{
e.TranslationBehavior.DesiredDeceleration = 0.001;
}
private void MapItemTouchDown(object sender, TouchEventArgs e)
{
var mapItem = (MapItem)sender;
mapItem.IsSelected = !mapItem.IsSelected;
e.Handled = true;
}
private void SeamarksChecked(object sender, RoutedEventArgs e)
{
map.Children.Insert(map.Children.IndexOf(mapGraticule), ((MapViewModel)DataContext).MapLayers.SeamarksLayer);
}
private void SeamarksUnchecked(object sender, RoutedEventArgs e)
{
map.Children.Remove(((MapViewModel)DataContext).MapLayers.SeamarksLayer);
}
}
}

View file

@ -1,145 +0,0 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace WpfCoreApp
{
public class OutlinedText : FrameworkElement
{
private GlyphRun glyphRun;
private Geometry outline;
public static readonly DependencyProperty TextProperty = TextBlock.TextProperty.AddOwner(
typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).glyphRun = null) { AffectsMeasure = true });
public static readonly DependencyProperty FontSizeProperty = TextBlock.FontSizeProperty.AddOwner(
typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).glyphRun = null) { AffectsMeasure = true });
public static readonly DependencyProperty FontFamilyProperty = TextBlock.FontFamilyProperty.AddOwner(
typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).glyphRun = null) { AffectsMeasure = true });
public static readonly DependencyProperty FontStyleProperty = TextBlock.FontStyleProperty.AddOwner(
typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).glyphRun = null) { AffectsMeasure = true });
public static readonly DependencyProperty FontWeightProperty = TextBlock.FontWeightProperty.AddOwner(
typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).glyphRun = null) { AffectsMeasure = true });
public static readonly DependencyProperty FontStretchProperty = TextBlock.FontStretchProperty.AddOwner(
typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).glyphRun = null) { AffectsMeasure = true });
public static readonly DependencyProperty ForegroundProperty = TextBlock.ForegroundProperty.AddOwner(
typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).glyphRun = null) { AffectsMeasure = true });
public static readonly DependencyProperty BackgroundProperty = TextBlock.BackgroundProperty.AddOwner(
typeof(OutlinedText), new FrameworkPropertyMetadata(Brushes.White, (o, e) => ((OutlinedText)o).glyphRun = null) { AffectsMeasure = true });
public static readonly DependencyProperty OutlineThicknessProperty = DependencyProperty.Register(
"OutlineThickness", typeof(double), typeof(OutlinedText),
new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.AffectsMeasure, (o, e) => ((OutlinedText)o).glyphRun = null));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public double FontSize
{
get { return (double)GetValue(FontSizeProperty); }
set { SetValue(FontSizeProperty, value); }
}
public FontFamily FontFamily
{
get { return (FontFamily)GetValue(FontFamilyProperty); }
set { SetValue(FontFamilyProperty, value); }
}
public FontStyle FontStyle
{
get { return (FontStyle)GetValue(FontStyleProperty); }
set { SetValue(FontStyleProperty, value); }
}
public FontWeight FontWeight
{
get { return (FontWeight)GetValue(FontWeightProperty); }
set { SetValue(FontWeightProperty, value); }
}
public FontStretch FontStretch
{
get { return (FontStretch)GetValue(FontStretchProperty); }
set { SetValue(FontStretchProperty, value); }
}
public Brush Foreground
{
get { return (Brush)GetValue(ForegroundProperty); }
set { SetValue(ForegroundProperty, value); }
}
public Brush Background
{
get { return (Brush)GetValue(BackgroundProperty); }
set { SetValue(BackgroundProperty, value); }
}
public double OutlineThickness
{
get { return (double)GetValue(OutlineThicknessProperty); }
set { SetValue(OutlineThicknessProperty, value); }
}
protected override Size MeasureOverride(Size availableSize)
{
return CheckGlyphRun() ? outline.Bounds.Size : new Size();
}
protected override void OnRender(DrawingContext drawingContext)
{
if (CheckGlyphRun())
{
var location = outline.Bounds.Location;
drawingContext.PushTransform(new TranslateTransform(-location.X, -location.Y));
drawingContext.DrawGeometry(Background, null, outline);
drawingContext.DrawGlyphRun(Foreground, glyphRun);
}
}
private bool CheckGlyphRun()
{
if (glyphRun == null)
{
if (string.IsNullOrEmpty(Text))
{
return false;
}
var typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
GlyphTypeface glyphTypeface;
if (!typeface.TryGetGlyphTypeface(out glyphTypeface))
{
return false;
}
var glyphIndices = new ushort[Text.Length];
var advanceWidths = new double[Text.Length];
for (int i = 0; i < Text.Length; i++)
{
var glyphIndex = glyphTypeface.CharacterToGlyphMap[Text[i]];
glyphIndices[i] = glyphIndex;
advanceWidths[i] = glyphTypeface.AdvanceWidths[glyphIndex] * FontSize;
}
glyphRun = new GlyphRun(glyphTypeface, 0, false, FontSize, 1f, glyphIndices, new Point(), advanceWidths, null, null, null, null, null, null);
outline = glyphRun.BuildGeometry().GetWidenedPathGeometry(new Pen(null, OutlineThickness * 2d));
}
return true;
}
}
}

View file

@ -1,36 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWPF>true</UseWPF>
<RootNamespace>WpfCoreApp</RootNamespace>
<Product>XAML Map Control</Product>
<Version>5.0.0</Version>
<Description>XAML Map Control WPF Sample Application</Description>
<Authors>Clemens Fischer</Authors>
<Copyright>Copyright © 2020 Clemens Fischer</Copyright>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>DEBUG</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants></DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Shared\MapLayers.cs" Link="MapLayers.cs" />
<Compile Include="..\Shared\MapViewModel.cs" Link="MapViewModel.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\MapControl\WPF\MapControl.WPF.csproj" />
</ItemGroup>
<ItemGroup>
<Resource Include="..\Shared\10_535_330.jpg" Link="10_535_330.jpg" />
</ItemGroup>
</Project>