From e6782cbb2d5443effd5b762904c3e2c8c1752f61 Mon Sep 17 00:00:00 2001 From: ClemensFischer Date: Fri, 5 Dec 2025 00:17:50 +0100 Subject: [PATCH] Added DrawingTileMatrixLayer --- MapControl/Shared/WmtsTileMatrixLayer.cs | 14 +- MapControl/WPF/BitmapTileMatrixLayer.cs | 33 ++--- MapControl/WPF/DrawingTileMatrixLayer.cs | 155 +++++++++++++++++++++++ 3 files changed, 176 insertions(+), 26 deletions(-) create mode 100644 MapControl/WPF/DrawingTileMatrixLayer.cs diff --git a/MapControl/Shared/WmtsTileMatrixLayer.cs b/MapControl/Shared/WmtsTileMatrixLayer.cs index 967d9230..7a8978d4 100644 --- a/MapControl/Shared/WmtsTileMatrixLayer.cs +++ b/MapControl/Shared/WmtsTileMatrixLayer.cs @@ -108,14 +108,14 @@ namespace MapControl { // Arrange tiles relative to TileMatrix.XMin/YMin. // - var tileWidth = WmtsTileMatrix.TileWidth; - var tileHeight = WmtsTileMatrix.TileHeight; - var x = tileWidth * (tile.X - TileMatrix.XMin); - var y = tileHeight * (tile.Y - TileMatrix.YMin); + var width = WmtsTileMatrix.TileWidth; + var height = WmtsTileMatrix.TileHeight; + var x = width * (tile.X - TileMatrix.XMin); + var y = height * (tile.Y - TileMatrix.YMin); - tile.Image.Width = tileWidth; - tile.Image.Height = tileHeight; - tile.Image.Arrange(new Rect(x, y, tileWidth, tileHeight)); + tile.Image.Width = width; + tile.Image.Height = height; + tile.Image.Arrange(new Rect(x, y, width, height)); } return finalSize; diff --git a/MapControl/WPF/BitmapTileMatrixLayer.cs b/MapControl/WPF/BitmapTileMatrixLayer.cs index 9e52f153..5c437832 100644 --- a/MapControl/WPF/BitmapTileMatrixLayer.cs +++ b/MapControl/WPF/BitmapTileMatrixLayer.cs @@ -135,16 +135,16 @@ namespace MapControl tile.IsPending = false; tile.PixelBuffer = equivalentTile.PixelBuffer; } + else + { + tile.Completed += OnTileCompleted; + } } if (tile.PixelBuffer != null) { CopyTile(tile); } - else - { - tile.Completed += OnTileCompleted; - } tiles.Add(tile); } @@ -155,13 +155,13 @@ namespace MapControl private void CopyTile(BitmapTile tile) { - var rect = new Int32Rect( - WmtsTileMatrix.TileWidth * (tile.X - TileMatrix.XMin), - WmtsTileMatrix.TileHeight * (tile.Y - TileMatrix.YMin), - WmtsTileMatrix.TileWidth, - WmtsTileMatrix.TileHeight); + var width = WmtsTileMatrix.TileWidth; + var height = WmtsTileMatrix.TileHeight; + var x = width * (tile.X - TileMatrix.XMin); + var y = height * (tile.Y - TileMatrix.YMin); - ((WriteableBitmap)imageBrush.ImageSource).WritePixels(rect, tile.PixelBuffer, 4 * WmtsTileMatrix.TileWidth, 0); + ((WriteableBitmap)imageBrush.ImageSource).WritePixels( + new Int32Rect(x, y, width, height), tile.PixelBuffer, 4 * WmtsTileMatrix.TileWidth, 0); } private void OnTileCompleted(object sender, EventArgs e) @@ -170,16 +170,11 @@ namespace MapControl tile.Completed -= OnTileCompleted; - if (tile.PixelBuffer != null) + if (tile.X >= TileMatrix.XMin && tile.X <= TileMatrix.XMax && + tile.Y >= TileMatrix.YMin && tile.Y <= TileMatrix.YMax && + tile.PixelBuffer != null) { - Dispatcher.Invoke(() => - { - if (tile.X >= TileMatrix.XMin && tile.X <= TileMatrix.XMax && - tile.Y >= TileMatrix.YMin && tile.Y <= TileMatrix.YMax) - { - CopyTile(tile); - } - }); + _ = Dispatcher.InvokeAsync(() => CopyTile(tile)); } } } diff --git a/MapControl/WPF/DrawingTileMatrixLayer.cs b/MapControl/WPF/DrawingTileMatrixLayer.cs new file mode 100644 index 00000000..dac741d1 --- /dev/null +++ b/MapControl/WPF/DrawingTileMatrixLayer.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Media; + +namespace MapControl +{ + public class ImageSourceTile(int zoomLevel, int x, int y, int columnCount) + : Tile(zoomLevel, x, y, columnCount) + { + public event EventHandler Completed; + + public ImageSource ImageSource { get; set; } + + public override async Task LoadImageAsync(Func> loadImageFunc) + { + ImageSource = await loadImageFunc().ConfigureAwait(false); + + Completed?.Invoke(this, EventArgs.Empty); + } + } + + public class DrawingTileMatrixLayer(WmtsTileMatrix wmtsTileMatrix, int zoomLevel) : UIElement + { + public WmtsTileMatrix WmtsTileMatrix => wmtsTileMatrix; + + public TileMatrix TileMatrix { get; private set; } = new TileMatrix(zoomLevel, 1, 1, 0, 0); + + public IEnumerable Tiles { get; private set; } = []; + + public DrawingGroup Drawing { get; } = new DrawingGroup { Transform = new MatrixTransform() }; + + protected override void OnRender(DrawingContext drawingContext) + { + drawingContext.DrawDrawing(Drawing); + } + + public void UpdateRenderTransform(ViewTransform viewTransform) + { + // Tile matrix origin in pixels. + // + var tileMatrixOrigin = new Point(WmtsTileMatrix.TileWidth * TileMatrix.XMin, WmtsTileMatrix.TileHeight * TileMatrix.YMin); + + ((MatrixTransform)Drawing.Transform).Matrix = + viewTransform.GetTileLayerTransform(WmtsTileMatrix.Scale, WmtsTileMatrix.TopLeft, tileMatrixOrigin); + } + + public bool UpdateTiles(ViewTransform viewTransform, double viewWidth, double viewHeight) + { + // Tile matrix bounds in pixels. + // + var bounds = viewTransform.GetTileMatrixBounds(WmtsTileMatrix.Scale, WmtsTileMatrix.TopLeft, viewWidth, viewHeight); + + // Tile X and Y bounds. + // + var xMin = (int)Math.Floor(bounds.X / WmtsTileMatrix.TileWidth); + var yMin = (int)Math.Floor(bounds.Y / WmtsTileMatrix.TileHeight); + var xMax = (int)Math.Floor((bounds.X + bounds.Width) / WmtsTileMatrix.TileWidth); + var yMax = (int)Math.Floor((bounds.Y + bounds.Height) / WmtsTileMatrix.TileHeight); + + if (!WmtsTileMatrix.HasFullHorizontalCoverage) + { + // Set X range limits. + // + xMin = Math.Max(xMin, 0); + xMax = Math.Min(Math.Max(xMax, 0), WmtsTileMatrix.MatrixWidth - 1); + } + + // Set Y range limits. + // + yMin = Math.Max(yMin, 0); + yMax = Math.Min(Math.Max(yMax, 0), WmtsTileMatrix.MatrixHeight - 1); + + if (TileMatrix.XMin == xMin && TileMatrix.YMin == yMin && + TileMatrix.XMax == xMax && TileMatrix.YMax == yMax) + { + // No change of the TileMatrix and the Tiles collection. + // + return false; + } + + TileMatrix = new TileMatrix(TileMatrix.ZoomLevel, xMin, yMin, xMax, yMax); + + Drawing.Children.Clear(); + CreateTiles(); + + return true; + } + + private void CreateTiles() + { + var tiles = new List(); + + for (var y = TileMatrix.YMin; y <= TileMatrix.YMax; y++) + { + for (var x = TileMatrix.XMin; x <= TileMatrix.XMax; x++) + { + var tile = Tiles.FirstOrDefault(t => t.X == x && t.Y == y); + + if (tile == null) + { + tile = new ImageSourceTile(TileMatrix.ZoomLevel, x, y, WmtsTileMatrix.MatrixWidth); + + var equivalentTile = Tiles.FirstOrDefault(t => t.ImageSource != null && t.Column == tile.Column && t.Row == tile.Row); + + if (equivalentTile != null) + { + tile.IsPending = false; + tile.ImageSource = equivalentTile.ImageSource; + } + else + { + tile.Completed += OnTileCompleted; + } + } + + if (tile.ImageSource != null) + { + DrawTile(tile); + } + + tiles.Add(tile); + } + } + + Tiles = tiles; + } + + private void DrawTile(ImageSourceTile tile) + { + var width = WmtsTileMatrix.TileWidth; + var height = WmtsTileMatrix.TileHeight; + var x = width * (tile.X - TileMatrix.XMin); + var y = height * (tile.Y - TileMatrix.YMin); + + Drawing.Children.Add(new ImageDrawing(tile.ImageSource, new Rect(x, y, width, height))); + } + + private void OnTileCompleted(object sender, EventArgs e) + { + var tile = (ImageSourceTile)sender; + + tile.Completed -= OnTileCompleted; + + if (tile.X >= TileMatrix.XMin && tile.X <= TileMatrix.XMax && + tile.Y >= TileMatrix.YMin && tile.Y <= TileMatrix.YMax && + tile.ImageSource != null) + { + _ = Dispatcher.InvokeAsync(() => DrawTile(tile)); + } + } + } +}