mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2025-12-06 07:12:04 +01:00
Version 4.17.0: Added support for WMTS
This commit is contained in:
parent
7e5c5c0671
commit
b5fe760c83
|
|
@ -351,7 +351,7 @@ namespace MapControl
|
|||
var rect = MapProjection.BoundingBoxToRect(boundingBox);
|
||||
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)
|
||||
* MapProjection.TrueScale * 360d / MapProjection.TileSize;
|
||||
* MapProjection.TrueScale * 360d / 256d;
|
||||
|
||||
TargetZoomLevel = Math.Log(scale, 2d);
|
||||
TargetCenter = MapProjection.PointToLocation(center);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System;
|
|||
using System.Globalization;
|
||||
#if WINDOWS_UWP
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml.Media;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
|
@ -18,8 +19,6 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public abstract class MapProjection
|
||||
{
|
||||
public const int TileSize = 256;
|
||||
|
||||
public const double Wgs84EquatorialRadius = 6378137d;
|
||||
public const double Wgs84Flattening = 1d / 298.257223563;
|
||||
public static readonly double Wgs84Eccentricity = Math.Sqrt((2d - Wgs84Flattening) * Wgs84Flattening);
|
||||
|
|
@ -79,6 +78,11 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public Matrix InverseViewportTransform { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rotation angle of the ViewportTransform matrix.
|
||||
/// </summary>
|
||||
public double ViewportRotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scaling factor from cartesian map coordinates to viewport coordinates
|
||||
/// at the projection's point of true scale.
|
||||
|
|
@ -178,19 +182,76 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets ProjectionCenter, ViewportScale, ViewportTransform and InverseViewportTransform.
|
||||
/// Sets ProjectionCenter, ViewportScale, ViewportRotation, ViewportTransform and InverseViewportTransform.
|
||||
/// </summary>
|
||||
public void SetViewportTransform(Location projectionCenter, Location mapCenter, Point viewportCenter, double zoomLevel, double heading)
|
||||
public void SetViewportTransform(Location projectionCenter, Location mapCenter, Point viewportCenter, double zoomLevel, double rotation)
|
||||
{
|
||||
ProjectionCenter = projectionCenter;
|
||||
ViewportScale = Math.Pow(2d, zoomLevel) * TileSize / (360d * TrueScale);
|
||||
ViewportScale = Math.Pow(2d, zoomLevel) * 256d / (360d * TrueScale);
|
||||
ViewportRotation = rotation;
|
||||
|
||||
var center = LocationToPoint(mapCenter);
|
||||
var matrix = MatrixFactory.Create(center, ViewportScale, -ViewportScale, heading, viewportCenter);
|
||||
var matrix = CreateViewportTransform(center, viewportCenter);
|
||||
|
||||
ViewportTransform = matrix;
|
||||
matrix.Invert();
|
||||
InverseViewportTransform = matrix;
|
||||
}
|
||||
|
||||
internal Matrix CreateViewportTransform(Point mapCenter, Point viewportCenter)
|
||||
{
|
||||
var matrix = new Matrix(ViewportScale, 0d, 0d, -ViewportScale, -ViewportScale * mapCenter.X, ViewportScale * mapCenter.Y);
|
||||
|
||||
matrix.Rotate(ViewportRotation);
|
||||
matrix.Translate(viewportCenter.X, viewportCenter.Y);
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
internal Matrix CreateTileLayerTransform(double tileGridScale, Point tileGridTopLeft, Point tileGridOrigin)
|
||||
{
|
||||
var scale = ViewportScale / tileGridScale;
|
||||
var matrix = new Matrix(scale, 0d, 0d, scale, 0d, 0d);
|
||||
|
||||
matrix.Rotate(ViewportRotation);
|
||||
|
||||
// tile grid origin in map cordinates
|
||||
//
|
||||
var mapOrigin = new Point(
|
||||
tileGridTopLeft.X + tileGridOrigin.X / tileGridScale,
|
||||
tileGridTopLeft.Y - tileGridOrigin.Y / tileGridScale);
|
||||
|
||||
// tile grid origin in viewport cordinates
|
||||
//
|
||||
var viewOrigin = ViewportTransform.Transform(mapOrigin);
|
||||
|
||||
matrix.Translate(viewOrigin.X, viewOrigin.Y);
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
internal Rect GetTileBounds(double tileGridScale, Point tileGridTopLeft, Size viewportSize)
|
||||
{
|
||||
var scale = tileGridScale / ViewportScale;
|
||||
var matrix = new Matrix(scale, 0d, 0d, scale, 0d, 0d);
|
||||
|
||||
matrix.Rotate(-ViewportRotation);
|
||||
|
||||
// viewport origin in map coordinates
|
||||
//
|
||||
var origin = InverseViewportTransform.Transform(new Point());
|
||||
|
||||
// translate origin to tile grid origin in pixels
|
||||
//
|
||||
matrix.Translate(
|
||||
tileGridScale * (origin.X - tileGridTopLeft.X),
|
||||
tileGridScale * (tileGridTopLeft.Y - origin.Y));
|
||||
|
||||
// transforms viewport bounds to tile pixel bounds
|
||||
//
|
||||
var transform = new MatrixTransform { Matrix = matrix };
|
||||
|
||||
return transform.TransformBounds(new Rect(0d, 0d, viewportSize.Width, viewportSize.Height));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,14 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public class MapTileLayer : MapTileLayerBase
|
||||
{
|
||||
private const double WebMercatorMapSize = 2 * Math.PI * MapProjection.Wgs84EquatorialRadius;
|
||||
public const int TileSize = 256;
|
||||
public const double MapSize = 2 * Math.PI * MapProjection.Wgs84EquatorialRadius;
|
||||
public static readonly Point TileGridTopLeft = new Point(-MapSize / 2, MapSize / 2);
|
||||
|
||||
public static double TileGridScale(int zoomLevel)
|
||||
{
|
||||
return (TileSize << zoomLevel) / MapSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A default MapTileLayer using OpenStreetMap data.
|
||||
|
|
@ -96,9 +103,9 @@ namespace MapControl
|
|||
{
|
||||
foreach (var tile in Tiles)
|
||||
{
|
||||
var tileSize = MapProjection.TileSize << (TileGrid.ZoomLevel - tile.ZoomLevel);
|
||||
var x = tileSize * tile.X - MapProjection.TileSize * TileGrid.XMin;
|
||||
var y = tileSize * tile.Y - MapProjection.TileSize * TileGrid.YMin;
|
||||
var tileSize = TileSize << (TileGrid.ZoomLevel - tile.ZoomLevel);
|
||||
var x = tileSize * tile.X - TileSize * TileGrid.XMin;
|
||||
var y = tileSize * tile.Y - TileSize * TileGrid.YMin;
|
||||
|
||||
tile.Image.Width = tileSize;
|
||||
tile.Image.Height = tileSize;
|
||||
|
|
@ -136,53 +143,29 @@ namespace MapControl
|
|||
|
||||
protected override void SetRenderTransform()
|
||||
{
|
||||
var tileGridSize = (double)(1 << TileGrid.ZoomLevel);
|
||||
|
||||
// top/left tile grid corner in map coordinates
|
||||
// tile grid origin in pixels
|
||||
//
|
||||
var tileGridOrigin = new Point(
|
||||
WebMercatorMapSize * (TileGrid.XMin / tileGridSize - 0.5),
|
||||
WebMercatorMapSize * (0.5 - TileGrid.YMin / tileGridSize));
|
||||
var tileGridOrigin = new Point(TileSize * TileGrid.XMin, TileSize * TileGrid.YMin);
|
||||
|
||||
// top/left tile grid corner in viewport coordinates
|
||||
//
|
||||
var viewOrigin = ParentMap.MapProjection.ViewportTransform.Transform(tileGridOrigin);
|
||||
|
||||
// tile pixels per viewport unit, 0.5 .. 2
|
||||
//
|
||||
var tileScale = Math.Pow(2d, ParentMap.ZoomLevel - TileGrid.ZoomLevel);
|
||||
|
||||
((MatrixTransform)RenderTransform).Matrix = MatrixFactory.Create(tileScale, ParentMap.Heading, viewOrigin);
|
||||
((MatrixTransform)RenderTransform).Matrix = ParentMap.MapProjection.CreateTileLayerTransform(
|
||||
TileGridScale(TileGrid.ZoomLevel), TileGridTopLeft, tileGridOrigin);
|
||||
}
|
||||
|
||||
private bool SetTileGrid()
|
||||
{
|
||||
var tileGridZoomLevel = (int)Math.Round(ParentMap.ZoomLevel);
|
||||
var tileGridSize = (double)(1 << tileGridZoomLevel);
|
||||
var tileGridZoomLevel = (int)Math.Floor(ParentMap.ZoomLevel + 0.001); // avoid rounding issues
|
||||
|
||||
// top/left viewport corner in map coordinates
|
||||
// bounds in tile pixels from viewport size
|
||||
//
|
||||
var tileOrigin = ParentMap.MapProjection.InverseViewportTransform.Transform(new Point());
|
||||
var tileBounds = ParentMap.MapProjection.GetTileBounds(
|
||||
TileGridScale(tileGridZoomLevel), TileGridTopLeft, ParentMap.RenderSize);
|
||||
|
||||
// top/left viewport corner in tile grid coordinates
|
||||
// tile column and row index bounds
|
||||
//
|
||||
var tileGridOrigin = new Point(
|
||||
tileGridSize * (0.5 + tileOrigin.X / WebMercatorMapSize),
|
||||
tileGridSize * (0.5 - tileOrigin.Y / WebMercatorMapSize));
|
||||
|
||||
// transforms viewport bounds to tile grid bounds
|
||||
//
|
||||
var transform = new MatrixTransform
|
||||
{
|
||||
Matrix = MatrixFactory.Create(1d / MapProjection.TileSize, -ParentMap.Heading, tileGridOrigin)
|
||||
};
|
||||
|
||||
var bounds = transform.TransformBounds(new Rect(0d, 0d, ParentMap.RenderSize.Width, ParentMap.RenderSize.Height));
|
||||
|
||||
var xMin = (int)Math.Floor(bounds.X);
|
||||
var yMin = (int)Math.Floor(bounds.Y);
|
||||
var xMax = (int)Math.Floor(bounds.X + bounds.Width);
|
||||
var yMax = (int)Math.Floor(bounds.Y + bounds.Height);
|
||||
var xMin = (int)Math.Floor(tileBounds.X / TileSize);
|
||||
var yMin = (int)Math.Floor(tileBounds.Y / TileSize);
|
||||
var xMax = (int)Math.Floor((tileBounds.X + tileBounds.Width) / TileSize);
|
||||
var yMax = (int)Math.Floor((tileBounds.Y + tileBounds.Height) / TileSize);
|
||||
|
||||
if (TileGrid != null &&
|
||||
TileGrid.ZoomLevel == tileGridZoomLevel &&
|
||||
|
|
|
|||
|
|
@ -1,30 +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 System.Windows;
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public static class MatrixFactory
|
||||
{
|
||||
public static Matrix Create(Point translation1, double scaleX, double scaleY, double rotation, Point translation2)
|
||||
{
|
||||
var matrix = new Matrix(scaleX, 0d, 0d, scaleY, -scaleX * translation1.X, -scaleY * translation1.Y);
|
||||
matrix.Rotate(rotation);
|
||||
matrix.Translate(translation2.X, translation2.Y);
|
||||
return matrix;
|
||||
}
|
||||
|
||||
public static Matrix Create(double scale, double rotation, Point translation)
|
||||
{
|
||||
var matrix = new Matrix(scale, 0d, 0d, scale, 0d, 0d);
|
||||
matrix.Rotate(rotation);
|
||||
matrix.Translate(translation.X, translation.Y);
|
||||
return matrix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -164,9 +164,7 @@ namespace MapControl
|
|||
.Replace("{W}", west.ToString(CultureInfo.InvariantCulture))
|
||||
.Replace("{S}", south.ToString(CultureInfo.InvariantCulture))
|
||||
.Replace("{E}", east.ToString(CultureInfo.InvariantCulture))
|
||||
.Replace("{N}", north.ToString(CultureInfo.InvariantCulture))
|
||||
.Replace("{X}", MapProjection.TileSize.ToString())
|
||||
.Replace("{Y}", MapProjection.TileSize.ToString());
|
||||
.Replace("{N}", north.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
private string GetLatLonBoundingBoxUri(int x, int y, int zoomLevel)
|
||||
|
|
@ -181,9 +179,7 @@ namespace MapControl
|
|||
.Replace("{w}", west.ToString(CultureInfo.InvariantCulture))
|
||||
.Replace("{s}", south.ToString(CultureInfo.InvariantCulture))
|
||||
.Replace("{e}", east.ToString(CultureInfo.InvariantCulture))
|
||||
.Replace("{n}", north.ToString(CultureInfo.InvariantCulture))
|
||||
.Replace("{X}", MapProjection.TileSize.ToString())
|
||||
.Replace("{Y}", MapProjection.TileSize.ToString());
|
||||
.Replace("{n}", north.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,18 +97,19 @@ namespace MapControl
|
|||
{
|
||||
foreach (var layer in Children.Cast<WmtsTileMatrixLayer>())
|
||||
{
|
||||
layer.SetRenderTransform(ParentMap.MapProjection, ParentMap.Heading);
|
||||
layer.SetRenderTransform(ParentMap.MapProjection);
|
||||
}
|
||||
}
|
||||
|
||||
private bool UpdateChildLayers(WmtsTileMatrixSet tileMatrixSet)
|
||||
{
|
||||
bool layersChanged = false;
|
||||
var layersChanged = false;
|
||||
var maxScale = 1.001 * ParentMap.MapProjection.ViewportScale; // avoid rounding issues
|
||||
|
||||
// show all TileMatrix layers with Scale <= ViewportScale, or at least the first layer
|
||||
// show all TileMatrix layers with Scale <= maxScale, or at least the first layer
|
||||
//
|
||||
var currentMatrixes = tileMatrixSet.TileMatrixes
|
||||
.Where((matrix, i) => i == 0 || matrix.Scale <= ParentMap.MapProjection.ViewportScale)
|
||||
.Where((matrix, i) => i == 0 || matrix.Scale <= maxScale)
|
||||
.ToList();
|
||||
|
||||
if (this != ParentMap.MapLayer) // do not load background tiles
|
||||
|
|
@ -136,7 +137,7 @@ namespace MapControl
|
|||
layersChanged = true;
|
||||
}
|
||||
|
||||
if (layer.SetBounds(ParentMap.MapProjection, ParentMap.Heading, ParentMap.RenderSize))
|
||||
if (layer.SetBounds(ParentMap.MapProjection, ParentMap.RenderSize))
|
||||
{
|
||||
layersChanged = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,29 +36,24 @@ namespace MapControl
|
|||
|
||||
public IReadOnlyCollection<Tile> Tiles { get; private set; } = new List<Tile>();
|
||||
|
||||
public bool SetBounds(MapProjection projection, double heading, Size mapSize)
|
||||
public void SetRenderTransform(MapProjection projection)
|
||||
{
|
||||
// top/left viewport corner in map coordinates (meters)
|
||||
// tile grid origin in pixels
|
||||
//
|
||||
var tileOrigin = projection.InverseViewportTransform.Transform(new Point());
|
||||
var tileGridOrigin = new Point(TileMatrix.TileWidth * XMin, TileMatrix.TileHeight * YMin);
|
||||
|
||||
// top/left viewport corner in tile matrix coordinates (tile column and row indexes)
|
||||
//
|
||||
var tileMatrixOrigin = new Point(
|
||||
TileMatrix.Scale * (tileOrigin.X - TileMatrix.TopLeft.X),
|
||||
TileMatrix.Scale * (TileMatrix.TopLeft.Y - tileOrigin.Y));
|
||||
((MatrixTransform)RenderTransform).Matrix =
|
||||
projection.CreateTileLayerTransform(TileMatrix.Scale, TileMatrix.TopLeft, tileGridOrigin);
|
||||
}
|
||||
|
||||
// relative layer scale
|
||||
//
|
||||
var scale = TileMatrix.Scale / projection.ViewportScale;
|
||||
|
||||
var transform = new MatrixTransform
|
||||
public bool SetBounds(MapProjection projection, Size viewportSize)
|
||||
{
|
||||
Matrix = MatrixFactory.Create(scale, -heading, tileMatrixOrigin)
|
||||
};
|
||||
|
||||
var bounds = transform.TransformBounds(new Rect(0d, 0d, mapSize.Width, mapSize.Height));
|
||||
// bounds in tile pixels from viewport size
|
||||
//
|
||||
var bounds = projection.GetTileBounds(TileMatrix.Scale, TileMatrix.TopLeft, viewportSize);
|
||||
|
||||
// tile column and row index bounds
|
||||
//
|
||||
var xMin = (int)Math.Floor(bounds.X / TileMatrix.TileWidth);
|
||||
var yMin = (int)Math.Floor(bounds.Y / TileMatrix.TileHeight);
|
||||
var xMax = (int)Math.Floor((bounds.X + bounds.Width) / TileMatrix.TileWidth);
|
||||
|
|
@ -82,25 +77,6 @@ namespace MapControl
|
|||
return true;
|
||||
}
|
||||
|
||||
public void SetRenderTransform(MapProjection projection, double heading)
|
||||
{
|
||||
// XMin/YMin corner in map coordinates (meters)
|
||||
//
|
||||
var mapOrigin = new Point(
|
||||
TileMatrix.TopLeft.X + XMin * TileMatrix.TileWidth / TileMatrix.Scale,
|
||||
TileMatrix.TopLeft.Y - YMin * TileMatrix.TileHeight / TileMatrix.Scale);
|
||||
|
||||
// XMin/YMin corner in viewport coordinates (pixels)
|
||||
//
|
||||
var viewOrigin = projection.ViewportTransform.Transform(mapOrigin);
|
||||
|
||||
// relative layer scale
|
||||
//
|
||||
var scale = projection.ViewportScale / TileMatrix.Scale;
|
||||
|
||||
((MatrixTransform)RenderTransform).Matrix = MatrixFactory.Create(scale, heading, viewOrigin);
|
||||
}
|
||||
|
||||
public void UpdateTiles()
|
||||
{
|
||||
var newTiles = new List<Tile>();
|
||||
|
|
|
|||
Loading…
Reference in a new issue