From afc93704d9021f18c2766bbfcc5f9c40c1f17d62 Mon Sep 17 00:00:00 2001 From: ClemensFischer Date: Thu, 24 Nov 2022 23:24:51 +0100 Subject: [PATCH] Fixed moving transform center across dateline. --- MapControl/Shared/BoundingBox.cs | 6 --- MapControl/Shared/MapBase.cs | 13 ++++-- MapControl/Shared/MapImageLayer.cs | 31 +++++++++----- MapControl/Shared/MapTileLayer.cs | 40 +++++++++---------- MapControl/Shared/MapTileLayerBase.cs | 4 +- MapControl/Shared/ViewportChangedEventArgs.cs | 15 +++---- MapControl/Shared/WmtsTileLayer.cs | 9 ++--- 7 files changed, 63 insertions(+), 55 deletions(-) diff --git a/MapControl/Shared/BoundingBox.cs b/MapControl/Shared/BoundingBox.cs index a0151a50..e34af0e5 100644 --- a/MapControl/Shared/BoundingBox.cs +++ b/MapControl/Shared/BoundingBox.cs @@ -30,12 +30,6 @@ namespace MapControl East = east; } - public BoundingBox(BoundingBox boundingBox, double longitudeOffset) - : this(boundingBox.South, boundingBox.West + longitudeOffset, - boundingBox.North, boundingBox.East + longitudeOffset) - { - } - public double West { get; set; } public double East { get; set; } diff --git a/MapControl/Shared/MapBase.cs b/MapControl/Shared/MapBase.cs index 10e5c56a..7fd8d35b 100644 --- a/MapControl/Shared/MapBase.cs +++ b/MapControl/Shared/MapBase.cs @@ -272,7 +272,8 @@ namespace MapControl /// /// 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 + /// or by the methods TranslateMap, TransformMap, ZoomMap and ZoomToBounds. /// public void SetTransformCenter(Point center) { @@ -728,6 +729,7 @@ namespace MapControl private void UpdateTransform(bool resetTransformCenter = false, bool projectionChanged = false) { + var transformCenterChanged = false; var viewScale = ViewTransform.ZoomLevelToScale(ZoomLevel); var projection = MapProjection; @@ -762,6 +764,9 @@ namespace MapControl if (resetTransformCenter) { + // check if transform center moved across the dateline + transformCenterChanged = Math.Abs(center.Longitude - transformCenter.Longitude) > 180d; + ResetTransformCenter(); projection.Center = ProjectionCenter ?? Center; @@ -778,9 +783,11 @@ namespace MapControl SetViewScale(ViewTransform.Scale); - OnViewportChanged(new ViewportChangedEventArgs(projectionChanged, Center.Longitude - centerLongitude)); - + // check if view center moved across the dateline + transformCenterChanged = transformCenterChanged || Math.Abs(Center.Longitude - centerLongitude) > 180d; centerLongitude = Center.Longitude; + + OnViewportChanged(new ViewportChangedEventArgs(projectionChanged, transformCenterChanged)); } } diff --git a/MapControl/Shared/MapImageLayer.cs b/MapControl/Shared/MapImageLayer.cs index 3889a327..470947aa 100644 --- a/MapControl/Shared/MapImageLayer.cs +++ b/MapControl/Shared/MapImageLayer.cs @@ -171,11 +171,11 @@ namespace MapControl base.OnViewportChanged(e); - await UpdateImageAsync(); + await UpdateImageAsync(); // update immediately } else { - AdjustBoundingBox(e.LongitudeOffset); + ValidateBoundingBox(); base.OnViewportChanged(e); @@ -233,21 +233,30 @@ namespace MapControl BoundingBox = ParentMap.ViewRectToBoundingBox(rect); } - private void AdjustBoundingBox(double longitudeOffset) + private void ValidateBoundingBox() { - if (Math.Abs(longitudeOffset) > 180d && BoundingBox != null) + if (BoundingBox != null) { - var offset = 360d * Math.Sign(longitudeOffset); + var offset = ParentMap.Center.Longitude - BoundingBox.Center.Longitude; - BoundingBox = new BoundingBox(BoundingBox, offset); - - foreach (var image in Children.OfType()) + if (Math.Abs(offset) > 180d) { - var imageBoundingBox = GetBoundingBox(image); + offset = 360d * Math.Sign(offset); - if (imageBoundingBox != null) + BoundingBox = new BoundingBox( + BoundingBox.South, BoundingBox.West + offset, + BoundingBox.North, BoundingBox.East + offset); + + foreach (var image in Children.OfType()) { - SetBoundingBox(image, new BoundingBox(imageBoundingBox, offset)); + var imageBbox = GetBoundingBox(image); + + if (imageBbox != null) + { + SetBoundingBox(image, new BoundingBox( + imageBbox.South, imageBbox.West + offset, + imageBbox.North, imageBbox.East + offset)); + } } } } diff --git a/MapControl/Shared/MapTileLayer.cs b/MapControl/Shared/MapTileLayer.cs index c2a931ca..335055cf 100644 --- a/MapControl/Shared/MapTileLayer.cs +++ b/MapControl/Shared/MapTileLayer.cs @@ -3,6 +3,7 @@ // Licensed under the Microsoft Public License (Ms-PL) using System; +using System.Linq; using System.Threading.Tasks; #if WINUI using Windows.Foundation; @@ -125,11 +126,11 @@ namespace MapControl protected override Task UpdateTileLayer() { - var update = false; + var updateTiles = false; if (ParentMap == null || ParentMap.MapProjection.Type != MapProjectionType.WebMercator) { - update = TileMatrix != null; + updateTiles = TileMatrix != null; TileMatrix = null; } else @@ -137,36 +138,33 @@ namespace MapControl if (TileSource != TileImageLoader.TileSource) { Tiles = new TileCollection(); // clear all - update = true; + updateTiles = true; } if (SetTileMatrix()) { - SetRenderTransform(); - update = true; + updateTiles = true; } + + SetRenderTransform(); } - if (update) - { - UpdateTiles(); - - return TileImageLoader.LoadTiles(Tiles, TileSource, SourceName); - } - - return Task.CompletedTask; + return updateTiles ? UpdateTiles() : Task.CompletedTask; } protected override void SetRenderTransform() { - // tile matrix origin in pixels - // - var tileMatrixOrigin = new Point(TileSize * TileMatrix.XMin, TileSize * TileMatrix.YMin); + if (TileMatrix != null) + { + // tile matrix origin in pixels + // + var tileMatrixOrigin = new Point(TileSize * TileMatrix.XMin, TileSize * TileMatrix.YMin); - var tileMatrixScale = ViewTransform.ZoomLevelToScale(TileMatrix.ZoomLevel); + var tileMatrixScale = ViewTransform.ZoomLevelToScale(TileMatrix.ZoomLevel); - ((MatrixTransform)RenderTransform).Matrix = - ParentMap.ViewTransform.GetTileLayerTransform(tileMatrixScale, MapTopLeft, tileMatrixOrigin); + ((MatrixTransform)RenderTransform).Matrix = + ParentMap.ViewTransform.GetTileLayerTransform(tileMatrixScale, MapTopLeft, tileMatrixOrigin); + } } private bool SetTileMatrix() @@ -199,7 +197,7 @@ namespace MapControl return true; } - private void UpdateTiles() + private Task UpdateTiles() { var tiles = new TileCollection(); @@ -241,6 +239,8 @@ namespace MapControl { Children.Add(tile.Image); } + + return TileImageLoader.LoadTiles(tiles, TileSource, SourceName); } } } diff --git a/MapControl/Shared/MapTileLayerBase.cs b/MapControl/Shared/MapTileLayerBase.cs index a9c42f86..291f73a8 100644 --- a/MapControl/Shared/MapTileLayerBase.cs +++ b/MapControl/Shared/MapTileLayerBase.cs @@ -201,9 +201,9 @@ namespace MapControl private async void OnViewportChanged(object sender, ViewportChangedEventArgs e) { - if (Children.Count == 0 || e.ProjectionChanged || Math.Abs(e.LongitudeOffset) > 180d) + if (Children.Count == 0 || e.ProjectionChanged || e.TransformCenterChanged) { - await Update(); // update immediately when projection has changed or center has moved across 180° longitude + await Update(); // update immediately } else { diff --git a/MapControl/Shared/ViewportChangedEventArgs.cs b/MapControl/Shared/ViewportChangedEventArgs.cs index 6dd39ef2..a5918fb7 100644 --- a/MapControl/Shared/ViewportChangedEventArgs.cs +++ b/MapControl/Shared/ViewportChangedEventArgs.cs @@ -8,22 +8,23 @@ namespace MapControl { public class ViewportChangedEventArgs : EventArgs { - public ViewportChangedEventArgs(bool projectionChanged = false, double longitudeOffset = 0d) + public ViewportChangedEventArgs(bool projectionChanged = false, bool transformCenterChanged = false) { ProjectionChanged = projectionChanged; - LongitudeOffset = longitudeOffset; + TransformCenterChanged = transformCenterChanged; } /// - /// Indicates if the map projection has changed. Used to control when a MapTileLayer or MapImageLayer - /// should be updated immediately, or MapPath Data in projected map coordinates should be recalculated. + /// Indicates that the map projection has changed. Used to control when + /// a MapTileLayer or a MapImageLayer should be updated immediately, + /// or MapPath Data in projected map coordinates should be recalculated. /// public bool ProjectionChanged { get; } /// - /// Offset of the map center longitude value from the previous viewport. - /// Used to detect if the map center has moved across 180° longitude. + /// Indicates that the view transform center has moved across the dateline. + /// Used to control when a MapTileLayer should be updated immediately. /// - public double LongitudeOffset { get; } + public bool TransformCenterChanged { get; } } } diff --git a/MapControl/Shared/WmtsTileLayer.cs b/MapControl/Shared/WmtsTileLayer.cs index 4e9f6db8..65a88b3e 100644 --- a/MapControl/Shared/WmtsTileLayer.cs +++ b/MapControl/Shared/WmtsTileLayer.cs @@ -94,14 +94,11 @@ namespace MapControl return UpdateTiles(null); } - if (UpdateChildLayers(tileMatrixSet)) - { - SetRenderTransform(); + var updateTiles = UpdateChildLayers(tileMatrixSet); - return UpdateTiles(tileMatrixSet); - } + SetRenderTransform(); - return Task.CompletedTask; + return updateTiles ? UpdateTiles(tileMatrixSet) : Task.CompletedTask; } protected override void SetRenderTransform()