From 1f72ddd76dced5ab282f998880f8dcb9b9acf16a Mon Sep 17 00:00:00 2001 From: ClemensFischer Date: Fri, 31 May 2024 19:51:37 +0200 Subject: [PATCH] Avalonia GeoImage --- MapControl/Avalonia/MapBase.Avalonia.cs | 1 + .../Avalonia/MapControl.Avalonia.csproj | 1 - MapControl/Shared/GeoImage.cs | 152 +++++++++--------- MapControl/WPF/GeoImage.WPF.cs | 22 ++- MapControl/WinUI/GeoImage.WinUI.cs | 19 ++- SampleApps/AvaloniaApp/MainWindow.axaml | 2 +- 6 files changed, 97 insertions(+), 100 deletions(-) diff --git a/MapControl/Avalonia/MapBase.Avalonia.cs b/MapControl/Avalonia/MapBase.Avalonia.cs index 8d7066f4..666becaf 100644 --- a/MapControl/Avalonia/MapBase.Avalonia.cs +++ b/MapControl/Avalonia/MapBase.Avalonia.cs @@ -23,6 +23,7 @@ global using HorizontalAlignment = Avalonia.Layout.HorizontalAlignment; global using VerticalAlignment = Avalonia.Layout.VerticalAlignment; global using Brush = Avalonia.Media.IBrush; global using ImageSource = Avalonia.Media.IImage; +global using BitmapSource = Avalonia.Media.Imaging.Bitmap; global using PathFigureCollection = Avalonia.Media.PathFigures; global using PointCollection = System.Collections.Generic.List; global using PropertyPath = System.String; diff --git a/MapControl/Avalonia/MapControl.Avalonia.csproj b/MapControl/Avalonia/MapControl.Avalonia.csproj index 4e4459e6..cc49aef8 100644 --- a/MapControl/Avalonia/MapControl.Avalonia.csproj +++ b/MapControl/Avalonia/MapControl.Avalonia.csproj @@ -23,7 +23,6 @@ - diff --git a/MapControl/Shared/GeoImage.cs b/MapControl/Shared/GeoImage.cs index 20be1b5b..c268fb08 100644 --- a/MapControl/Shared/GeoImage.cs +++ b/MapControl/Shared/GeoImage.cs @@ -5,6 +5,7 @@ using System; using System.Globalization; using System.IO; +using Path = System.IO.Path; using System.Linq; using System.Threading.Tasks; #if WPF @@ -26,20 +27,13 @@ using Microsoft.UI.Xaml.Media.Imaging; namespace MapControl { - public partial class GeoImage : ContentControl + public partial class GeoImage : Grid { private class GeoBitmap { - public GeoBitmap(BitmapSource bitmap, Matrix transform, MapProjection projection = null) - { - Bitmap = bitmap; - Transform = transform; - Projection = projection; - } - - public BitmapSource Bitmap { get; } - public Matrix Transform { get; } - public MapProjection Projection { get; } + public BitmapSource Bitmap { get; set; } + public Matrix Transform { get; set; } + public MapProjection Projection { get; set; } } private const ushort ProjectedCRSGeoKey = 3072; @@ -55,12 +49,6 @@ namespace MapControl DependencyPropertyHelper.Register(nameof(SourcePath), null, async (image, oldValue, newValue) => await image.SourcePathPropertyChanged(newValue)); - public GeoImage() - { - HorizontalContentAlignment = HorizontalAlignment.Stretch; - VerticalContentAlignment = VerticalAlignment.Stretch; - } - public string SourcePath { get => (string)GetValue(SourcePathProperty); @@ -69,78 +57,90 @@ namespace MapControl private async Task SourcePathPropertyChanged(string sourcePath) { - Image image = null; - BoundingBox boundingBox = null; - - if (sourcePath != null) + if (sourcePath == null) { - GeoBitmap geoBitmap = null; + return; + } - var ext = Path.GetExtension(sourcePath); + GeoBitmap geoBitmap = null; + var ext = Path.GetExtension(sourcePath); - if (ext.Length >= 4) + if (ext.Length >= 4) + { + var dir = Path.GetDirectoryName(sourcePath); + var file = Path.GetFileNameWithoutExtension(sourcePath); + var worldFilePath = Path.Combine(dir, file + ext.Remove(2, 1) + "w"); + + if (File.Exists(worldFilePath)) { - var dir = Path.GetDirectoryName(sourcePath); - var file = Path.GetFileNameWithoutExtension(sourcePath); - var worldFilePath = Path.Combine(dir, file + ext.Remove(2, 1) + "w"); - - if (File.Exists(worldFilePath)) - { - geoBitmap = await ReadWorldFileImageAsync(sourcePath, worldFilePath); - } - } - - if (geoBitmap == null) - { - geoBitmap = await ReadGeoTiffAsync(sourcePath); - } - - image = new Image - { - Source = geoBitmap.Bitmap, - Stretch = Stretch.Fill - }; - - var transform = geoBitmap.Transform; - - if (transform.M12 != 0 || transform.M21 != 0) - { - var rotation = (Math.Atan2(transform.M12, transform.M11) + Math.Atan2(transform.M21, -transform.M22)) * 90d / Math.PI; - - image.RenderTransform = new RotateTransform { Angle = -rotation }; - - // Calculate effective unrotated transform. - // - transform.M11 = Math.Sqrt(transform.M11 * transform.M11 + transform.M12 * transform.M12); - transform.M22 = -Math.Sqrt(transform.M22 * transform.M22 + transform.M21 * transform.M21); - transform.M12 = 0; - transform.M21 = 0; - } - - var p1 = transform.Transform(new Point()); - var p2 = transform.Transform(new Point(geoBitmap.Bitmap.PixelWidth, geoBitmap.Bitmap.PixelHeight)); - var rect = new Rect(p1, p2); - - if (geoBitmap.Projection != null) - { - boundingBox = geoBitmap.Projection.MapToBoundingBox(rect); - } - else - { - boundingBox = new BoundingBox(rect.Y, rect.X, rect.Y + rect.Height, rect.X + rect.Width); + geoBitmap = await ReadWorldFileImageAsync(sourcePath, worldFilePath); } } - Content = image; + if (geoBitmap == null) + { +#if AVALONIA + return; +#else + geoBitmap = await ReadGeoTiffAsync(sourcePath); +#endif + } + + var image = new Image + { + Source = geoBitmap.Bitmap, + Stretch = Stretch.Fill, + HorizontalAlignment = HorizontalAlignment.Stretch, + VerticalAlignment = VerticalAlignment.Stretch + }; + + var transform = geoBitmap.Transform; + + if (transform.M12 != 0 && transform.M21 != 0) + { + var rotation = (Math.Atan2(transform.M12, transform.M11) + Math.Atan2(transform.M21, -transform.M22)) * 90d / Math.PI; + + image.RenderTransform = new RotateTransform { Angle = -rotation }; + + // Calculate effective unrotated transform. + // + geoBitmap.Transform = new Matrix( + Math.Sqrt(transform.M11 * transform.M11 + transform.M12 * transform.M12), 0d, 0d, + -Math.Sqrt(transform.M22 * transform.M22 + transform.M21 * transform.M21), 0d, 0d); + } +#if AVALONIA + var width = geoBitmap.Bitmap.PixelSize.Width; + var height = geoBitmap.Bitmap.PixelSize.Height; +#else + var width = geoBitmap.Bitmap.PixelWidth; + var height = geoBitmap.Bitmap.PixelHeight; +#endif + var rect = new Rect(transform.Transform(new Point()), transform.Transform(new Point(width, height))); + + BoundingBox boundingBox = null; + + if (geoBitmap.Projection != null) + { + boundingBox = geoBitmap.Projection.MapToBoundingBox(rect); + } + else + { + boundingBox = new BoundingBox(rect.Y, rect.X, rect.Y + rect.Height, rect.X + rect.Width); + } + + Children.Clear(); + Children.Add(image); MapPanel.SetBoundingBox(this, boundingBox); } private static async Task ReadWorldFileImageAsync(string sourcePath, string worldFilePath) { - var bitmap = (BitmapSource)await ImageLoader.LoadImageAsync(sourcePath); + var geoBitmap = new GeoBitmap(); - var transform = await Task.Run(() => + geoBitmap.Bitmap = (BitmapSource)await ImageLoader.LoadImageAsync(sourcePath); + + geoBitmap.Transform = await Task.Run(() => { var parameters = File.ReadLines(worldFilePath) .Take(6) @@ -168,7 +168,7 @@ namespace MapControl parameters[5]); // line 6: F or OffsetY }); - return new GeoBitmap(bitmap, transform); + return geoBitmap; } private static MapProjection GetProjection(string sourcePath, short[] geoKeyDirectory) diff --git a/MapControl/WPF/GeoImage.WPF.cs b/MapControl/WPF/GeoImage.WPF.cs index e1c6df4f..ee2364ba 100644 --- a/MapControl/WPF/GeoImage.WPF.cs +++ b/MapControl/WPF/GeoImage.WPF.cs @@ -17,30 +17,28 @@ namespace MapControl { return await Task.Run(() => { - BitmapSource bitmap; - Matrix transform; - MapProjection projection = null; + var geoBitmap = new GeoBitmap(); using (var stream = File.OpenRead(sourcePath)) { - bitmap = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); + geoBitmap.Bitmap = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); } - var metadata = (BitmapMetadata)bitmap.Metadata; + var metadata = (BitmapMetadata)geoBitmap.Bitmap.Metadata; if (metadata.GetQuery(QueryString(ModelPixelScaleTag)) is double[] pixelScale && pixelScale.Length == 3 && metadata.GetQuery(QueryString(ModelTiePointTag)) is double[] tiePoint && tiePoint.Length >= 6) { - transform = new Matrix(pixelScale[0], 0d, 0d, -pixelScale[1], tiePoint[3], tiePoint[4]); + geoBitmap.Transform = new Matrix(pixelScale[0], 0d, 0d, -pixelScale[1], tiePoint[3], tiePoint[4]); } else if (metadata.GetQuery(QueryString(ModelTransformationTag)) is double[] transformValues && transformValues.Length == 16) { - transform = new Matrix(transformValues[0], transformValues[1], - transformValues[4], transformValues[5], - transformValues[3], transformValues[7]); + geoBitmap.Transform = new Matrix(transformValues[0], transformValues[1], + transformValues[4], transformValues[5], + transformValues[3], transformValues[7]); } else { @@ -49,16 +47,16 @@ namespace MapControl if (metadata.GetQuery(QueryString(GeoKeyDirectoryTag)) is short[] geoKeyDirectory) { - projection = GetProjection(sourcePath, geoKeyDirectory); + geoBitmap.Projection = GetProjection(sourcePath, geoKeyDirectory); } if (metadata.GetQuery(QueryString(NoDataTag)) is string noData && int.TryParse(noData, out int noDataValue)) { - bitmap = ConvertTransparentPixel(bitmap, noDataValue); + geoBitmap.Bitmap = ConvertTransparentPixel(geoBitmap.Bitmap, noDataValue); } - return new GeoBitmap(bitmap, transform, projection); + return geoBitmap; }); } diff --git a/MapControl/WinUI/GeoImage.WinUI.cs b/MapControl/WinUI/GeoImage.WinUI.cs index c8a52b37..bfe9d7be 100644 --- a/MapControl/WinUI/GeoImage.WinUI.cs +++ b/MapControl/WinUI/GeoImage.WinUI.cs @@ -17,11 +17,10 @@ namespace MapControl using (var stream = await file.OpenReadAsync()) { - Matrix transform; - MapProjection projection = null; - + var geoBitmap = new GeoBitmap(); var decoder = await BitmapDecoder.CreateAsync(stream); - var bitmap = await ImageLoader.LoadImageAsync(decoder); + + geoBitmap.Bitmap = await ImageLoader.LoadImageAsync(decoder); var geoKeyDirectoryQuery = QueryString(GeoKeyDirectoryTag); var pixelScaleQuery = QueryString(ModelPixelScaleTag); @@ -43,15 +42,15 @@ namespace MapControl tiePointValue.Value is double[] tiePoint && tiePoint.Length >= 6) { - transform = new Matrix(pixelScale[0], 0d, 0d, -pixelScale[1], tiePoint[3], tiePoint[4]); + geoBitmap.Transform = new Matrix(pixelScale[0], 0d, 0d, -pixelScale[1], tiePoint[3], tiePoint[4]); } else if (metadata.TryGetValue(transformationQuery, out BitmapTypedValue transformValue) && transformValue.Value is double[] transformValues && transformValues.Length == 16) { - transform = new Matrix(transformValues[0], transformValues[1], - transformValues[4], transformValues[5], - transformValues[3], transformValues[7]); + geoBitmap.Transform = new Matrix(transformValues[0], transformValues[1], + transformValues[4], transformValues[5], + transformValues[3], transformValues[7]); } else { @@ -61,10 +60,10 @@ namespace MapControl if (metadata.TryGetValue(geoKeyDirectoryQuery, out BitmapTypedValue geoKeyDirValue) && geoKeyDirValue.Value is short[] geoKeyDirectory) { - projection = GetProjection(sourcePath, geoKeyDirectory); + geoBitmap.Projection = GetProjection(sourcePath, geoKeyDirectory); } - return new GeoBitmap(bitmap, transform, projection); + return geoBitmap; } } } diff --git a/SampleApps/AvaloniaApp/MainWindow.axaml b/SampleApps/AvaloniaApp/MainWindow.axaml index 22a61e99..638108ad 100644 --- a/SampleApps/AvaloniaApp/MainWindow.axaml +++ b/SampleApps/AvaloniaApp/MainWindow.axaml @@ -5,7 +5,7 @@ xmlns:tools="clr-namespace:MapControl.UiTools;assembly=MapUiTools.Avalonia" xmlns:local="clr-namespace:SampleApplication" x:Class="SampleApplication.MainWindow" - Title="MainWindow"> + Title="XAML MapControl - Avalonia Sample Application">