diff --git a/MapControl/Avalonia/GeoImage.Avalonia.cs b/MapControl/Avalonia/GeoImage.Avalonia.cs index 25804ca0..1cfa2a9f 100644 --- a/MapControl/Avalonia/GeoImage.Avalonia.cs +++ b/MapControl/Avalonia/GeoImage.Avalonia.cs @@ -7,15 +7,11 @@ using System.Threading.Tasks; namespace MapControl { - public partial class GeoImage : Image + public partial class GeoImage { - private void SetImage(ImageSource image) - { - Source = image; - Stretch = Stretch.Fill; - } + private Point BitmapSize => new(bitmapSource.PixelSize.Width, bitmapSource.PixelSize.Height); - private static Task ReadGeoTiffAsync(string sourcePath) + private Task LoadGeoTiffAsync(string sourcePath) { throw new InvalidOperationException("GeoTIFF is not supported."); } diff --git a/MapControl/Shared/GeoImage.cs b/MapControl/Shared/GeoImage.cs index 842cf10a..410b520d 100644 --- a/MapControl/Shared/GeoImage.cs +++ b/MapControl/Shared/GeoImage.cs @@ -10,13 +10,18 @@ using System.Linq; using System.Threading.Tasks; #if WPF using System.Windows; +using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Imaging; #elif UWP using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media.Imaging; #elif WINUI using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Media.Imaging; #endif @@ -24,29 +29,6 @@ namespace MapControl { public partial class GeoImage { - private class GeoBitmap - { - public BitmapSource Bitmap { get; set; } - public Matrix Transform { get; set; } - public MapProjection Projection { get; set; } - - public BoundingBox BoundingBox - { - get - { - var p1 = Transform.Transform(new Point()); -#if AVALONIA - var p2 = Transform.Transform(new Point(Bitmap.PixelSize.Width, Bitmap.PixelSize.Height)); -#else - var p2 = Transform.Transform(new Point(Bitmap.PixelWidth, Bitmap.PixelHeight)); -#endif - return Projection != null - ? Projection.MapToBoundingBox(new Rect(p1, p2)) - : new BoundingBox(p1.Y, p1.X, p2.Y, p2.X); - } - } - } - private const ushort ProjectedCRSGeoKey = 3072; private const ushort GeoKeyDirectoryTag = 34735; private const ushort ModelPixelScaleTag = 33550; @@ -56,23 +38,48 @@ namespace MapControl private static string QueryString(ushort tag) => $"/ifd/{{ushort={tag}}}"; - public static readonly DependencyProperty SourcePathProperty = - DependencyPropertyHelper.Register(nameof(SourcePath), null, - async (image, oldValue, newValue) => await image.SourcePathPropertyChanged(newValue)); + private BitmapSource bitmapSource; + private Matrix transformMatrix; + private MapProjection mapProjection; + private BoundingBox boundingBox; - public string SourcePath + public static readonly DependencyProperty SourcePathProperty = + DependencyPropertyHelper.RegisterAttached("SourcePath", null, + async (image, oldValue, newValue) => await LoadGeoImageAsync((Image)image, newValue)); + + public static string GetSourcePath(Image image) { - get => (string)GetValue(SourcePathProperty); - set => SetValue(SourcePathProperty, value); + return (string)image.GetValue(SourcePathProperty); } - private async Task SourcePathPropertyChanged(string sourcePath) + public static void SetSourcePath(Image image, string value) + { + image.SetValue(SourcePathProperty, value); + } + + public static Image LoadGeoImage(string sourcePath) + { + var image = new Image(); + + SetSourcePath(image, sourcePath); + + return image; + } + + private static async Task LoadGeoImageAsync(Image image, string sourcePath) { if (sourcePath != null) { try { - await ReadGeoImageAsync(sourcePath); + var geoImage = new GeoImage(); + + await geoImage.LoadGeoImageAsync(sourcePath); + + image.Source = geoImage.bitmapSource; + image.Stretch = Stretch.Fill; + + MapPanel.SetBoundingBox(image, geoImage.boundingBox); } catch (Exception ex) { @@ -81,9 +88,8 @@ namespace MapControl } } - private async Task ReadGeoImageAsync(string sourcePath) + private async Task LoadGeoImageAsync(string sourcePath) { - GeoBitmap geoBitmap = null; var ext = Path.GetExtension(sourcePath); if (ext.Length >= 4) @@ -94,27 +100,28 @@ namespace MapControl if (File.Exists(worldFilePath)) { - geoBitmap = await ReadWorldFileImageAsync(sourcePath, worldFilePath); + await LoadWorldFileImageAsync(sourcePath, worldFilePath); } } - if (geoBitmap == null) + if (bitmapSource == null) { - geoBitmap = await ReadGeoTiffAsync(sourcePath); + await LoadGeoTiffAsync(sourcePath); } - MapPanel.SetBoundingBox(this, geoBitmap.BoundingBox); + var p1 = transformMatrix.Transform(new Point()); + var p2 = transformMatrix.Transform(BitmapSize); - SetImage(geoBitmap.Bitmap); + boundingBox = mapProjection != null + ? mapProjection.MapToBoundingBox(new Rect(p1, p2)) + : new BoundingBox(p1.Y, p1.X, p2.Y, p2.X); } - private static async Task ReadWorldFileImageAsync(string sourcePath, string worldFilePath) + private async Task LoadWorldFileImageAsync(string sourcePath, string worldFilePath) { - return new GeoBitmap - { - Bitmap = (BitmapSource)await ImageLoader.LoadImageAsync(sourcePath), - Transform = await Task.Run(() => ReadWorldFileMatrix(worldFilePath)) - }; + transformMatrix = await Task.Run(() => ReadWorldFileMatrix(worldFilePath)); + + bitmapSource = (BitmapSource)await ImageLoader.LoadImageAsync(sourcePath); } private static Matrix ReadWorldFileMatrix(string worldFilePath) @@ -140,7 +147,7 @@ namespace MapControl parameters[5]); // line 6: F or OffsetY } - private static MapProjection GetProjection(short[] geoKeyDirectory) + private void SetProjection(short[] geoKeyDirectory) { for (var i = 4; i < geoKeyDirectory.Length - 3; i += 4) { @@ -148,12 +155,10 @@ namespace MapControl { var epsgCode = geoKeyDirectory[i + 3]; - return MapProjectionFactory.Instance.GetProjection(epsgCode) ?? + mapProjection = MapProjectionFactory.Instance.GetProjection(epsgCode) ?? throw new ArgumentException($"Can not create projection EPSG:{epsgCode}."); } } - - return null; } } } diff --git a/MapControl/Shared/MapBase.cs b/MapControl/Shared/MapBase.cs index c5cddb4a..3a8c9de2 100644 --- a/MapControl/Shared/MapBase.cs +++ b/MapControl/Shared/MapBase.cs @@ -32,10 +32,14 @@ namespace MapControl public partial class MapBase : MapPanel { public static double ZoomLevelToScale(double zoomLevel) - => 256d * Math.Pow(2d, zoomLevel) / (360d * MapProjection.Wgs84MeterPerDegree); + { + return 256d * Math.Pow(2d, zoomLevel) / (360d * MapProjection.Wgs84MeterPerDegree); + } public static double ScaleToZoomLevel(double scale) - => Math.Log(scale * 360d * MapProjection.Wgs84MeterPerDegree / 256d, 2d); + { + return Math.Log(scale * 360d * MapProjection.Wgs84MeterPerDegree / 256d, 2d); + } public static TimeSpan ImageFadeDuration { get; set; } = TimeSpan.FromSeconds(0.1); @@ -375,7 +379,9 @@ namespace MapControl } internal bool InsideViewBounds(Point point) - => point.X >= 0d && point.Y >= 0d && point.X <= ActualWidth && point.Y <= ActualHeight; + { + return point.X >= 0d && point.Y >= 0d && point.X <= ActualWidth && point.Y <= ActualHeight; + } internal double CoerceLongitude(double longitude) { diff --git a/MapControl/Shared/MapOverlaysPanel.cs b/MapControl/Shared/MapOverlaysPanel.cs index 1905cfd9..db888220 100644 --- a/MapControl/Shared/MapOverlaysPanel.cs +++ b/MapControl/Shared/MapOverlaysPanel.cs @@ -118,7 +118,7 @@ namespace MapControl } else { - overlay = new GeoImage { SourcePath = sourcePath }; + overlay = GeoImage.LoadGeoImage(sourcePath); } } catch (Exception ex) diff --git a/MapControl/WPF/GeoImage.WPF.cs b/MapControl/WPF/GeoImage.WPF.cs index 202c63fb..0ef68336 100644 --- a/MapControl/WPF/GeoImage.WPF.cs +++ b/MapControl/WPF/GeoImage.WPF.cs @@ -6,46 +6,40 @@ using System; using System.IO; using System.Linq; using System.Threading.Tasks; -using System.Windows.Controls; +using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; namespace MapControl { - public partial class GeoImage : Image + public partial class GeoImage { - private void SetImage(ImageSource image) - { - Source = image; - Stretch = Stretch.Fill; - } + private Point BitmapSize => new Point(bitmapSource.PixelWidth, bitmapSource.PixelHeight); - private static Task ReadGeoTiffAsync(string sourcePath) + private Task LoadGeoTiffAsync(string sourcePath) { return Task.Run(() => { - var geoBitmap = new GeoBitmap(); - using (var stream = File.OpenRead(sourcePath)) { - geoBitmap.Bitmap = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); + bitmapSource = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); } - var metadata = (BitmapMetadata)geoBitmap.Bitmap.Metadata; + var metadata = (BitmapMetadata)bitmapSource.Metadata; if (metadata.GetQuery(QueryString(ModelPixelScaleTag)) is double[] pixelScale && pixelScale.Length == 3 && metadata.GetQuery(QueryString(ModelTiePointTag)) is double[] tiePoint && tiePoint.Length >= 6) { - geoBitmap.Transform = new Matrix(pixelScale[0], 0d, 0d, -pixelScale[1], tiePoint[3], tiePoint[4]); + transformMatrix = new Matrix(pixelScale[0], 0d, 0d, -pixelScale[1], tiePoint[3], tiePoint[4]); } else if (metadata.GetQuery(QueryString(ModelTransformationTag)) is double[] transformValues && transformValues.Length == 16) { - geoBitmap.Transform = new Matrix(transformValues[0], transformValues[1], - transformValues[4], transformValues[5], - transformValues[3], transformValues[7]); + transformMatrix = new Matrix(transformValues[0], transformValues[1], + transformValues[4], transformValues[5], + transformValues[3], transformValues[7]); } else { @@ -54,16 +48,14 @@ namespace MapControl if (metadata.GetQuery(QueryString(GeoKeyDirectoryTag)) is short[] geoKeyDirectory) { - geoBitmap.Projection = GetProjection(geoKeyDirectory); + SetProjection(geoKeyDirectory); } if (metadata.GetQuery(QueryString(NoDataTag)) is string noData && int.TryParse(noData, out int noDataValue)) { - geoBitmap.Bitmap = ConvertTransparentPixel(geoBitmap.Bitmap, noDataValue); + bitmapSource = ConvertTransparentPixel(bitmapSource, noDataValue); } - - return geoBitmap; }); } diff --git a/MapControl/WinUI/GeoImage.WinUI.cs b/MapControl/WinUI/GeoImage.WinUI.cs index d18b6ef6..43d32c48 100644 --- a/MapControl/WinUI/GeoImage.WinUI.cs +++ b/MapControl/WinUI/GeoImage.WinUI.cs @@ -6,42 +6,22 @@ using System; using System.Threading.Tasks; using Windows.Graphics.Imaging; using Windows.Storage; -#if UWP -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Media; -#else -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Media; -#endif namespace MapControl { - public partial class GeoImage : Grid + public partial class GeoImage { - private void SetImage(ImageSource image) - { - Children.Clear(); - Children.Add(new Image - { - Source = image, - Stretch = Stretch.Fill, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch - }); - } + private Point BitmapSize => new Point(bitmapSource.PixelWidth, bitmapSource.PixelHeight); - private static async Task ReadGeoTiffAsync(string sourcePath) + private async Task LoadGeoTiffAsync(string sourcePath) { var file = await StorageFile.GetFileFromPathAsync(FilePath.GetFullPath(sourcePath)); using (var stream = await file.OpenReadAsync()) { - var geoBitmap = new GeoBitmap(); var decoder = await BitmapDecoder.CreateAsync(stream); - geoBitmap.Bitmap = await ImageLoader.LoadImageAsync(decoder); + bitmapSource = await ImageLoader.LoadImageAsync(decoder); var geoKeyDirectoryQuery = QueryString(GeoKeyDirectoryTag); var pixelScaleQuery = QueryString(ModelPixelScaleTag); @@ -63,15 +43,15 @@ namespace MapControl tiePointValue.Value is double[] tiePoint && tiePoint.Length >= 6) { - geoBitmap.Transform = new Matrix(pixelScale[0], 0d, 0d, -pixelScale[1], tiePoint[3], tiePoint[4]); + transformMatrix = 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) { - geoBitmap.Transform = new Matrix(transformValues[0], transformValues[1], - transformValues[4], transformValues[5], - transformValues[3], transformValues[7]); + transformMatrix = new Matrix(transformValues[0], transformValues[1], + transformValues[4], transformValues[5], + transformValues[3], transformValues[7]); } else { @@ -81,10 +61,8 @@ namespace MapControl if (metadata.TryGetValue(geoKeyDirectoryQuery, out BitmapTypedValue geoKeyDirValue) && geoKeyDirValue.Value is short[] geoKeyDirectory) { - geoBitmap.Projection = GetProjection(geoKeyDirectory); + SetProjection(geoKeyDirectory); } - - return geoBitmap; } } }