Added MapBase.MapBounds and MapBase.GeoBounds

This commit is contained in:
ClemensFischer 2025-12-13 18:28:07 +01:00
parent 4db96b9e83
commit 263deb5fd6
10 changed files with 95 additions and 100 deletions

View file

@ -14,11 +14,10 @@ namespace MapControl
Scale = scale;
Rotation = ((rotation % 360d) + 360d) % 360d;
MapToViewMatrix
= Matrix.CreateTranslation(-mapCenter.X, -mapCenter.Y)
* Matrix.CreateScale(scale, -scale)
* Matrix.CreateRotation(Matrix.ToRadians(Rotation))
* Matrix.CreateTranslation(viewCenter.X, viewCenter.Y);
MapToViewMatrix = Matrix.CreateTranslation(-mapCenter.X, -mapCenter.Y)
* Matrix.CreateScale(scale, -scale)
* Matrix.CreateRotation(Matrix.ToRadians(Rotation))
* Matrix.CreateTranslation(viewCenter.X, viewCenter.Y);
ViewToMapMatrix = MapToViewMatrix.Invert();
}
@ -28,10 +27,8 @@ namespace MapControl
/// </summary>
public Matrix GetMapTransform(Point relativeScale)
{
var scale = GetMapScale(relativeScale);
return Matrix.CreateScale(scale.X, scale.Y)
* Matrix.CreateRotation(Matrix.ToRadians(Rotation));
return Matrix.CreateScale(Scale * relativeScale.X, Scale * relativeScale.Y)
* Matrix.CreateRotation(Matrix.ToRadians(Rotation));
}
/// <summary>
@ -47,13 +44,13 @@ namespace MapControl
// Tile matrix origin in view coordinates.
//
var viewOrigin = MapToView(mapOrigin);
var viewOrigin = MapToViewMatrix.Transform(mapOrigin);
var transformScale = Scale / tileMatrixScale;
return Matrix.CreateScale(transformScale, transformScale)
* Matrix.CreateRotation(Matrix.ToRadians(Rotation))
* Matrix.CreateTranslation(viewOrigin.X, viewOrigin.Y);
* Matrix.CreateRotation(Matrix.ToRadians(Rotation))
* Matrix.CreateTranslation(viewOrigin.X, viewOrigin.Y);
}
/// <summary>
@ -63,13 +60,12 @@ namespace MapControl
{
// View origin in map coordinates.
//
var origin = ViewToMap(new Point());
var origin = ViewToMapMatrix.Transform(new Point());
var transformScale = tileMatrixScale / Scale;
var transform
= Matrix.CreateScale(transformScale, transformScale)
* Matrix.CreateRotation(Matrix.ToRadians(-Rotation));
var transform = Matrix.CreateScale(transformScale, transformScale)
* Matrix.CreateRotation(Matrix.ToRadians(-Rotation));
// Translate origin to tile matrix origin in pixels.
//
@ -88,8 +84,8 @@ namespace MapControl
double translation2X, double translation2Y)
{
return Matrix.CreateTranslation(translation1X, translation1Y)
* Matrix.CreateRotation(Matrix.ToRadians(rotation))
* Matrix.CreateTranslation(translation2X, translation2Y);
* Matrix.CreateRotation(Matrix.ToRadians(rotation))
* Matrix.CreateTranslation(translation2X, translation2Y);
}
}
}

View file

@ -49,6 +49,8 @@ namespace MapControl
private Location transformCenter;
private Point viewCenter;
private Rect? mapBounds;
private BoundingBox geoBounds;
private double centerLongitude;
private double maxLatitude = 85.05112878; // default WebMercatorProjection
private bool internalPropertyChange;
@ -170,6 +172,27 @@ namespace MapControl
set => SetValue(TargetHeadingProperty, value);
}
/// <summary>
/// Gets the map viewport bounds in projected map coordinates.
/// </summary>
public Rect MapBounds
{
get
{
if (!mapBounds.HasValue)
{
mapBounds = ViewRectToMap(0d, 0d, ActualWidth, ActualHeight);
}
return mapBounds.Value;
}
}
/// <summary>
/// Gets the map viewport bounds in geographic coordinates.
/// </summary>
public BoundingBox GeoBounds => geoBounds ??= MapProjection.MapToBoundingBox(MapBounds);
/// <summary>
/// Gets the ViewTransform instance that is used to transform between projected
/// map coordinates and view coordinates.
@ -180,18 +203,22 @@ namespace MapControl
/// Gets the map scale as horizontal and vertical scaling factors from meters to
/// view coordinates at the specified geographic coordinates.
/// </summary>
public Point GetScale(double latitude, double longitude)
public Point GetMapScale(double latitude, double longitude)
{
return ViewTransform.GetMapScale(MapProjection.GetRelativeScale(latitude, longitude));
var relativeScale = MapProjection.GetRelativeScale(latitude, longitude);
return new Point(ViewTransform.Scale * relativeScale.X, ViewTransform.Scale * relativeScale.Y);
}
/// <summary>
/// Gets the map scale as horizontal and vertical scaling factors from meters to
/// view coordinates at the specified location.
/// </summary>
public Point GetScale(Location location)
public Point GetMapScale(Location location)
{
return ViewTransform.GetMapScale(MapProjection.GetRelativeScale(location));
var relativeScale = MapProjection.GetRelativeScale(location);
return new Point(ViewTransform.Scale * relativeScale.X, ViewTransform.Scale * relativeScale.Y);
}
/// <summary>
@ -212,7 +239,7 @@ namespace MapControl
if (point.HasValue)
{
point = ViewTransform.MapToView(point.Value);
point = ViewTransform.MapToViewMatrix.Transform(point.Value);
}
return point;
@ -231,25 +258,35 @@ namespace MapControl
/// </summary>
public Location ViewToLocation(Point point)
{
return MapProjection.MapToLocation(ViewTransform.ViewToMap(point));
return MapProjection.MapToLocation(ViewTransform.ViewToMapMatrix.Transform(point));
}
/// <summary>
/// Gets a BoundingBox in geographic coordinates that covers a Rect in view coordinates.
/// Gets a Rect in projected map coordinates that covers a rectangle in view coordinates.
/// </summary>
public BoundingBox ViewRectToBoundingBox(Rect rect)
public Rect ViewRectToMap(double x, double y, double width, double height)
{
var p1 = ViewTransform.ViewToMap(new Point(rect.X, rect.Y));
var p2 = ViewTransform.ViewToMap(new Point(rect.X, rect.Y + rect.Height));
var p3 = ViewTransform.ViewToMap(new Point(rect.X + rect.Width, rect.Y));
var p4 = ViewTransform.ViewToMap(new Point(rect.X + rect.Width, rect.Y + rect.Height));
var viewToMap = ParentMap.ViewTransform.ViewToMapMatrix;
var p1 = viewToMap.Transform(new Point(x, y));
var p2 = viewToMap.Transform(new Point(x, y + height));
var p3 = viewToMap.Transform(new Point(x + width, y));
var p4 = viewToMap.Transform(new Point(x + width, y + height));
var x1 = Math.Min(p1.X, Math.Min(p2.X, Math.Min(p3.X, p4.X)));
var y1 = Math.Min(p1.Y, Math.Min(p2.Y, Math.Min(p3.Y, p4.Y)));
var x2 = Math.Max(p1.X, Math.Max(p2.X, Math.Max(p3.X, p4.X)));
var y2 = Math.Max(p1.Y, Math.Max(p2.Y, Math.Max(p3.Y, p4.Y)));
return MapProjection.MapToBoundingBox(new Rect(x1, y1, x2 - x1, y2 - y1));
return new Rect(x1, y1, x2 - x1, y2 - y1);
}
/// <summary>
/// Gets a BoundingBox in geographic coordinates that covers a rectangle in view coordinates.
/// </summary>
public BoundingBox ViewRectToBoundingBox(double x, double y, double width, double height)
{
return MapProjection.MapToBoundingBox(ViewRectToMap(x, y, width, height));
}
/// <summary>
@ -354,9 +391,9 @@ namespace MapControl
/// Sets the TargetZoomLevel and TargetCenter properties so that the specified BoundingBox
/// fits into the current view. The TargetHeading property is set to zero.
/// </summary>
public void ZoomToBounds(BoundingBox boundingBox)
public void ZoomToBounds(BoundingBox bounds)
{
var mapRect = MapProjection.BoundingBoxToMap(boundingBox);
var mapRect = MapProjection.BoundingBoxToMap(bounds);
if (mapRect.HasValue)
{
@ -486,16 +523,16 @@ namespace MapControl
if (center != null)
{
var centerLatitude = center.Latitude;
var centerLongitude = Location.NormalizeLongitude(center.Longitude);
var centerLat = center.Latitude;
var centerLon = Location.NormalizeLongitude(center.Longitude);
if (centerLatitude < -maxLatitude || centerLatitude > maxLatitude)
if (centerLat < -maxLatitude || centerLat > maxLatitude)
{
centerLatitude = Math.Min(Math.Max(centerLatitude, -maxLatitude), maxLatitude);
centerLat = Math.Min(Math.Max(centerLat, -maxLatitude), maxLatitude);
resetTransformCenter = true;
}
center = new Location(centerLatitude, centerLongitude);
center = new Location(centerLat, centerLon);
SetValueInternal(CenterProperty, center);
@ -539,6 +576,9 @@ namespace MapControl
{
base.OnViewportChanged(e);
mapBounds = null;
geoBounds = null;
ViewportChanged?.Invoke(this, e);
}
}

View file

@ -100,7 +100,7 @@ namespace MapControl
private double PixelPerLongitudeDegree(double latitude, double longitude)
{
var scale = ParentMap.GetScale(latitude, longitude);
var scale = ParentMap.GetMapScale(latitude, longitude);
return Math.Max(1d, // a reasonable lower limit
scale.X * Math.Cos(latitude * Math.PI / 180d) * MapProjection.Wgs84MeterPerDegree);
@ -171,14 +171,14 @@ namespace MapControl
private void DrawCylindricalGraticule(PathFigureCollection figures, List<Label> labels)
{
var boundingBox = ParentMap.ViewRectToBoundingBox(new Rect(0d, 0d, ParentMap.ActualWidth, ParentMap.ActualHeight));
var latLabelStart = Math.Ceiling(boundingBox.South / lineDistance) * lineDistance;
var lonLabelStart = Math.Ceiling(boundingBox.West / lineDistance) * lineDistance;
var bounds = ParentMap.GeoBounds;
var latLabelStart = Math.Ceiling(bounds.South / lineDistance) * lineDistance;
var lonLabelStart = Math.Ceiling(bounds.West / lineDistance) * lineDistance;
for (var lat = latLabelStart; lat <= boundingBox.North; lat += lineDistance)
for (var lat = latLabelStart; lat <= bounds.North; lat += lineDistance)
{
var p1 = ParentMap.LocationToView(lat, boundingBox.West);
var p2 = ParentMap.LocationToView(lat, boundingBox.East);
var p1 = ParentMap.LocationToView(lat, bounds.West);
var p2 = ParentMap.LocationToView(lat, bounds.East);
if (p1.HasValue && p2.HasValue)
{
@ -186,17 +186,17 @@ namespace MapControl
}
}
for (var lon = lonLabelStart; lon <= boundingBox.East; lon += lineDistance)
for (var lon = lonLabelStart; lon <= bounds.East; lon += lineDistance)
{
var p1 = ParentMap.LocationToView(boundingBox.South, lon);
var p2 = ParentMap.LocationToView(boundingBox.North, lon);
var p1 = ParentMap.LocationToView(bounds.South, lon);
var p2 = ParentMap.LocationToView(bounds.North, lon);
if (p1.HasValue && p2.HasValue)
{
figures.Add(CreateLineFigure(p1.Value, p2.Value));
}
for (var lat = latLabelStart; lat <= boundingBox.North; lat += lineDistance)
for (var lat = latLabelStart; lat <= bounds.North; lat += lineDistance)
{
var position = ParentMap.LocationToView(lat, lon);

View file

@ -192,7 +192,7 @@ namespace MapControl
var x = (ParentMap.ActualWidth - width) / 2d;
var y = (ParentMap.ActualHeight - height) / 2d;
boundingBox = ParentMap.ViewRectToBoundingBox(new Rect(x, y, width, height));
boundingBox = ParentMap.ViewRectToBoundingBox(x, y, width, height);
image = await GetImageAsync(boundingBox, loadingProgress);
}

View file

@ -220,7 +220,7 @@ namespace MapControl
private Rect GetViewRect(Rect mapRect)
{
var center = new Point(mapRect.X + mapRect.Width / 2d, mapRect.Y + mapRect.Height / 2d);
var position = parentMap.ViewTransform.MapToView(center);
var position = parentMap.ViewTransform.MapToViewMatrix.Transform(center);
if (parentMap.MapProjection.Type <= MapProjectionType.NormalCylindrical &&
!parentMap.InsideViewBounds(position))

View file

@ -118,7 +118,7 @@ namespace MapControl
if (point.HasValue)
{
point = parentMap.ViewTransform.MapToView(point.Value);
point = parentMap.ViewTransform.MapToViewMatrix.Transform(point.Value);
}
return point;

View file

@ -94,16 +94,10 @@ namespace MapControl
/// </summary>
public virtual Rect? BoundingBoxToMap(BoundingBox boundingBox)
{
Rect? rect = null;
var southWest = LocationToMap(boundingBox.South, boundingBox.West);
var northEast = LocationToMap(boundingBox.North, boundingBox.East);
if (southWest.HasValue && northEast.HasValue)
{
rect = new Rect(southWest.Value, northEast.Value);
}
return rect;
return southWest.HasValue && northEast.HasValue ? new Rect(southWest.Value, northEast.Value) : null;
}
/// <summary>
@ -112,16 +106,10 @@ namespace MapControl
/// </summary>
public virtual BoundingBox MapToBoundingBox(Rect rect)
{
BoundingBox boundingBox = null;
var southWest = MapToLocation(rect.X, rect.Y);
var northEast = MapToLocation(rect.X + rect.Width, rect.Y + rect.Height);
if (southWest != null && northEast != null)
{
boundingBox = new BoundingBox(southWest, northEast);
}
return boundingBox;
return southWest != null && northEast != null ? new BoundingBox(southWest, northEast) : null;
}
/// <summary>

View file

@ -90,7 +90,7 @@ namespace MapControl
{
double scale;
if (ParentMap == null || (scale = ParentMap.GetScale(ParentMap.Center).X) <= 0d)
if (ParentMap == null || (scale = ParentMap.GetMapScale(ParentMap.Center).X) <= 0d)
{
return new Size();
}

View file

@ -38,31 +38,6 @@ namespace MapControl
/// </summary>
public Matrix ViewToMapMatrix { get; private set; }
/// <summary>
/// Transforms a Point from projected map coordinates to view coordinates.
/// </summary>
public Point MapToView(Point point)
{
return MapToViewMatrix.Transform(point);
}
/// <summary>
/// Transforms a Point from view coordinates to projected map coordinates.
/// </summary>
public Point ViewToMap(Point point)
{
return ViewToMapMatrix.Transform(point);
}
/// <summary>
/// Transform relative to absolute map scale. Returns horizontal and vertical
/// scaling factors from meters to view coordinates.
/// </summary>
public Point GetMapScale(Point relativeScale)
{
return new Point(Scale * relativeScale.X, Scale * relativeScale.Y);
}
#if WPF || UWP || WINUI
/// <summary>
/// Initializes a ViewTransform from a map center point in projected coordinates,
@ -90,9 +65,7 @@ namespace MapControl
/// </summary>
public Matrix GetMapTransform(Point relativeScale)
{
var scale = GetMapScale(relativeScale);
var transform = new Matrix(scale.X, 0d, 0d, scale.Y, 0d, 0d);
var transform = new Matrix(Scale * relativeScale.X, 0d, 0d, Scale * relativeScale.Y, 0d, 0d);
transform.Rotate(Rotation);
return transform;
@ -111,7 +84,7 @@ namespace MapControl
// Tile matrix origin in view coordinates.
//
var viewOrigin = MapToView(mapOrigin);
var viewOrigin = MapToViewMatrix.Transform(mapOrigin);
var transformScale = Scale / tileMatrixScale;
@ -129,7 +102,7 @@ namespace MapControl
{
// View origin in map coordinates.
//
var origin = ViewToMap(new Point());
var origin = ViewToMapMatrix.Transform(new Point());
var transformScale = tileMatrixScale / Scale;

View file

@ -170,9 +170,7 @@ namespace MapControl
ParentMap.ActualWidth > 0d &&
ParentMap.ActualHeight > 0d)
{
var boundingBox = ParentMap.ViewRectToBoundingBox(new Rect(0d, 0d, ParentMap.ActualWidth, ParentMap.ActualHeight));
var uri = GetFeatureInfoRequestUri(boundingBox, position, format);
var uri = GetFeatureInfoRequestUri(ParentMap.GeoBounds, position, format);
if (uri != null)
{