Avalonia GeoImage

This commit is contained in:
ClemensFischer 2024-05-31 19:51:37 +02:00
parent 5718f68608
commit 1f72ddd76d
6 changed files with 97 additions and 100 deletions

View file

@ -23,6 +23,7 @@ global using HorizontalAlignment = Avalonia.Layout.HorizontalAlignment;
global using VerticalAlignment = Avalonia.Layout.VerticalAlignment; global using VerticalAlignment = Avalonia.Layout.VerticalAlignment;
global using Brush = Avalonia.Media.IBrush; global using Brush = Avalonia.Media.IBrush;
global using ImageSource = Avalonia.Media.IImage; global using ImageSource = Avalonia.Media.IImage;
global using BitmapSource = Avalonia.Media.Imaging.Bitmap;
global using PathFigureCollection = Avalonia.Media.PathFigures; global using PathFigureCollection = Avalonia.Media.PathFigures;
global using PointCollection = System.Collections.Generic.List<Avalonia.Point>; global using PointCollection = System.Collections.Generic.List<Avalonia.Point>;
global using PropertyPath = System.String; global using PropertyPath = System.String;

View file

@ -23,7 +23,6 @@
<ItemGroup> <ItemGroup>
<Compile Include="..\Shared\*.cs" /> <Compile Include="..\Shared\*.cs" />
<Compile Remove="..\Shared\GeoImage.cs" />
<Compile Remove="..\Shared\ViewTransform.cs" /> <Compile Remove="..\Shared\ViewTransform.cs" />
</ItemGroup> </ItemGroup>

View file

@ -5,6 +5,7 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using Path = System.IO.Path;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
#if WPF #if WPF
@ -26,20 +27,13 @@ using Microsoft.UI.Xaml.Media.Imaging;
namespace MapControl namespace MapControl
{ {
public partial class GeoImage : ContentControl public partial class GeoImage : Grid
{ {
private class GeoBitmap private class GeoBitmap
{ {
public GeoBitmap(BitmapSource bitmap, Matrix transform, MapProjection projection = null) public BitmapSource Bitmap { get; set; }
{ public Matrix Transform { get; set; }
Bitmap = bitmap; public MapProjection Projection { get; set; }
Transform = transform;
Projection = projection;
}
public BitmapSource Bitmap { get; }
public Matrix Transform { get; }
public MapProjection Projection { get; }
} }
private const ushort ProjectedCRSGeoKey = 3072; private const ushort ProjectedCRSGeoKey = 3072;
@ -55,12 +49,6 @@ namespace MapControl
DependencyPropertyHelper.Register<GeoImage, string>(nameof(SourcePath), null, DependencyPropertyHelper.Register<GeoImage, string>(nameof(SourcePath), null,
async (image, oldValue, newValue) => await image.SourcePathPropertyChanged(newValue)); async (image, oldValue, newValue) => await image.SourcePathPropertyChanged(newValue));
public GeoImage()
{
HorizontalContentAlignment = HorizontalAlignment.Stretch;
VerticalContentAlignment = VerticalAlignment.Stretch;
}
public string SourcePath public string SourcePath
{ {
get => (string)GetValue(SourcePathProperty); get => (string)GetValue(SourcePathProperty);
@ -69,13 +57,12 @@ namespace MapControl
private async Task SourcePathPropertyChanged(string sourcePath) private async Task SourcePathPropertyChanged(string sourcePath)
{ {
Image image = null; if (sourcePath == null)
BoundingBox boundingBox = null;
if (sourcePath != null)
{ {
GeoBitmap geoBitmap = null; return;
}
GeoBitmap geoBitmap = null;
var ext = Path.GetExtension(sourcePath); var ext = Path.GetExtension(sourcePath);
if (ext.Length >= 4) if (ext.Length >= 4)
@ -92,18 +79,24 @@ namespace MapControl
if (geoBitmap == null) if (geoBitmap == null)
{ {
#if AVALONIA
return;
#else
geoBitmap = await ReadGeoTiffAsync(sourcePath); geoBitmap = await ReadGeoTiffAsync(sourcePath);
#endif
} }
image = new Image var image = new Image
{ {
Source = geoBitmap.Bitmap, Source = geoBitmap.Bitmap,
Stretch = Stretch.Fill Stretch = Stretch.Fill,
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch
}; };
var transform = geoBitmap.Transform; var transform = geoBitmap.Transform;
if (transform.M12 != 0 || transform.M21 != 0) if (transform.M12 != 0 && transform.M21 != 0)
{ {
var rotation = (Math.Atan2(transform.M12, transform.M11) + Math.Atan2(transform.M21, -transform.M22)) * 90d / Math.PI; var rotation = (Math.Atan2(transform.M12, transform.M11) + Math.Atan2(transform.M21, -transform.M22)) * 90d / Math.PI;
@ -111,15 +104,20 @@ namespace MapControl
// Calculate effective unrotated transform. // Calculate effective unrotated transform.
// //
transform.M11 = Math.Sqrt(transform.M11 * transform.M11 + transform.M12 * transform.M12); geoBitmap.Transform = new Matrix(
transform.M22 = -Math.Sqrt(transform.M22 * transform.M22 + transform.M21 * transform.M21); Math.Sqrt(transform.M11 * transform.M11 + transform.M12 * transform.M12), 0d, 0d,
transform.M12 = 0; -Math.Sqrt(transform.M22 * transform.M22 + transform.M21 * transform.M21), 0d, 0d);
transform.M21 = 0;
} }
#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)));
var p1 = transform.Transform(new Point()); BoundingBox boundingBox = null;
var p2 = transform.Transform(new Point(geoBitmap.Bitmap.PixelWidth, geoBitmap.Bitmap.PixelHeight));
var rect = new Rect(p1, p2);
if (geoBitmap.Projection != null) if (geoBitmap.Projection != null)
{ {
@ -129,18 +127,20 @@ namespace MapControl
{ {
boundingBox = new BoundingBox(rect.Y, rect.X, rect.Y + rect.Height, rect.X + rect.Width); boundingBox = new BoundingBox(rect.Y, rect.X, rect.Y + rect.Height, rect.X + rect.Width);
} }
}
Content = image; Children.Clear();
Children.Add(image);
MapPanel.SetBoundingBox(this, boundingBox); MapPanel.SetBoundingBox(this, boundingBox);
} }
private static async Task<GeoBitmap> ReadWorldFileImageAsync(string sourcePath, string worldFilePath) private static async Task<GeoBitmap> 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) var parameters = File.ReadLines(worldFilePath)
.Take(6) .Take(6)
@ -168,7 +168,7 @@ namespace MapControl
parameters[5]); // line 6: F or OffsetY parameters[5]); // line 6: F or OffsetY
}); });
return new GeoBitmap(bitmap, transform); return geoBitmap;
} }
private static MapProjection GetProjection(string sourcePath, short[] geoKeyDirectory) private static MapProjection GetProjection(string sourcePath, short[] geoKeyDirectory)

View file

@ -17,28 +17,26 @@ namespace MapControl
{ {
return await Task.Run(() => return await Task.Run(() =>
{ {
BitmapSource bitmap; var geoBitmap = new GeoBitmap();
Matrix transform;
MapProjection projection = null;
using (var stream = File.OpenRead(sourcePath)) 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 && if (metadata.GetQuery(QueryString(ModelPixelScaleTag)) is double[] pixelScale &&
pixelScale.Length == 3 && pixelScale.Length == 3 &&
metadata.GetQuery(QueryString(ModelTiePointTag)) is double[] tiePoint && metadata.GetQuery(QueryString(ModelTiePointTag)) is double[] tiePoint &&
tiePoint.Length >= 6) 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 && else if (metadata.GetQuery(QueryString(ModelTransformationTag)) is double[] transformValues &&
transformValues.Length == 16) transformValues.Length == 16)
{ {
transform = new Matrix(transformValues[0], transformValues[1], geoBitmap.Transform = new Matrix(transformValues[0], transformValues[1],
transformValues[4], transformValues[5], transformValues[4], transformValues[5],
transformValues[3], transformValues[7]); transformValues[3], transformValues[7]);
} }
@ -49,16 +47,16 @@ namespace MapControl
if (metadata.GetQuery(QueryString(GeoKeyDirectoryTag)) is short[] geoKeyDirectory) 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 && if (metadata.GetQuery(QueryString(NoDataTag)) is string noData &&
int.TryParse(noData, out int noDataValue)) int.TryParse(noData, out int noDataValue))
{ {
bitmap = ConvertTransparentPixel(bitmap, noDataValue); geoBitmap.Bitmap = ConvertTransparentPixel(geoBitmap.Bitmap, noDataValue);
} }
return new GeoBitmap(bitmap, transform, projection); return geoBitmap;
}); });
} }

View file

@ -17,11 +17,10 @@ namespace MapControl
using (var stream = await file.OpenReadAsync()) using (var stream = await file.OpenReadAsync())
{ {
Matrix transform; var geoBitmap = new GeoBitmap();
MapProjection projection = null;
var decoder = await BitmapDecoder.CreateAsync(stream); var decoder = await BitmapDecoder.CreateAsync(stream);
var bitmap = await ImageLoader.LoadImageAsync(decoder);
geoBitmap.Bitmap = await ImageLoader.LoadImageAsync(decoder);
var geoKeyDirectoryQuery = QueryString(GeoKeyDirectoryTag); var geoKeyDirectoryQuery = QueryString(GeoKeyDirectoryTag);
var pixelScaleQuery = QueryString(ModelPixelScaleTag); var pixelScaleQuery = QueryString(ModelPixelScaleTag);
@ -43,13 +42,13 @@ namespace MapControl
tiePointValue.Value is double[] tiePoint && tiePointValue.Value is double[] tiePoint &&
tiePoint.Length >= 6) 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) && else if (metadata.TryGetValue(transformationQuery, out BitmapTypedValue transformValue) &&
transformValue.Value is double[] transformValues && transformValue.Value is double[] transformValues &&
transformValues.Length == 16) transformValues.Length == 16)
{ {
transform = new Matrix(transformValues[0], transformValues[1], geoBitmap.Transform = new Matrix(transformValues[0], transformValues[1],
transformValues[4], transformValues[5], transformValues[4], transformValues[5],
transformValues[3], transformValues[7]); transformValues[3], transformValues[7]);
} }
@ -61,10 +60,10 @@ namespace MapControl
if (metadata.TryGetValue(geoKeyDirectoryQuery, out BitmapTypedValue geoKeyDirValue) && if (metadata.TryGetValue(geoKeyDirectoryQuery, out BitmapTypedValue geoKeyDirValue) &&
geoKeyDirValue.Value is short[] geoKeyDirectory) geoKeyDirValue.Value is short[] geoKeyDirectory)
{ {
projection = GetProjection(sourcePath, geoKeyDirectory); geoBitmap.Projection = GetProjection(sourcePath, geoKeyDirectory);
} }
return new GeoBitmap(bitmap, transform, projection); return geoBitmap;
} }
} }
} }

View file

@ -5,7 +5,7 @@
xmlns:tools="clr-namespace:MapControl.UiTools;assembly=MapUiTools.Avalonia" xmlns:tools="clr-namespace:MapControl.UiTools;assembly=MapUiTools.Avalonia"
xmlns:local="clr-namespace:SampleApplication" xmlns:local="clr-namespace:SampleApplication"
x:Class="SampleApplication.MainWindow" x:Class="SampleApplication.MainWindow"
Title="MainWindow"> Title="XAML MapControl - Avalonia Sample Application">
<Grid> <Grid>
<Grid.Resources> <Grid.Resources>
<local:MapHeadingToVisibilityConverter x:Key="MapHeadingToVisibilityConverter" /> <local:MapHeadingToVisibilityConverter x:Key="MapHeadingToVisibilityConverter" />