diff --git a/MapImages/Shared/GeoImage.cs b/MapImages/Shared/GeoImage.cs deleted file mode 100644 index 31ed76f5..00000000 --- a/MapImages/Shared/GeoImage.cs +++ /dev/null @@ -1,204 +0,0 @@ -// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control -// © 2022 Clemens Fischer -// Licensed under the Microsoft Public License (Ms-PL) - -using MapControl.Projections; -using System; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -#if WINUI -using Windows.Foundation; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Media; -using Microsoft.UI.Xaml.Media.Imaging; -#elif UWP -using Windows.Foundation; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Media; -using Windows.UI.Xaml.Media.Imaging; -#else -using System.Windows; -using System.Windows.Controls; -using System.Windows.Media; -using System.Windows.Media.Imaging; -#endif - -namespace MapControl.Images -{ - public partial class GeoImage - { - private const string PixelScaleQuery = "/ifd/{ushort=33550}"; - private const string TiePointQuery = "/ifd/{ushort=33922}"; - private const string TransformQuery = "/ifd/{ushort=34264}"; - private const string NoDataQuery = "/ifd/{ushort=42113}"; - - public static readonly DependencyProperty PathProperty = DependencyProperty.RegisterAttached( - "Path", typeof(string), typeof(GeoImage), new PropertyMetadata(null, PathPropertyChanged)); - - public BitmapSource Bitmap { get; } - public Matrix Transform { get; } - public MapProjection Projection { get; } - public BoundingBox BoundingBox { get; } - public double Rotation { get; } - - public GeoImage(BitmapSource bitmap, Matrix transform, MapProjection projection) - { - Bitmap = bitmap; - Transform = transform; - Projection = projection; - - if (transform.M12 != 0 || transform.M21 != 0) - { - Rotation = (Math.Atan2(transform.M12, transform.M11) + Math.Atan2(transform.M21, -transform.M22)) * 90d / Math.PI; - - // 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 rect = new Rect( - transform.Transform(new Point()), - transform.Transform(new Point(bitmap.PixelWidth, bitmap.PixelHeight))); - - BoundingBox = projection != null - ? projection.RectToBoundingBox(rect) - : new BoundingBox - { - West = rect.X, - East = rect.X + rect.Width, - South = rect.Y, - North = rect.Y + rect.Height - }; - } - - public static string GetPath(Image image) - { - return (string)image.GetValue(PathProperty); - } - - public static void SetPath(Image image, string path) - { - image.SetValue(PathProperty, path); - } - - public static Task ReadImage(string imageFilePath) - { - var ext = Path.GetExtension(imageFilePath); - if (ext.Length < 4) - { - throw new ArgumentException("Invalid image file path extension, must have at least three characters."); - } - - var dir = Path.GetDirectoryName(imageFilePath); - var file = Path.GetFileNameWithoutExtension(imageFilePath); - var worldFilePath = Path.Combine(dir, file + ext.Remove(2, 1) + "w"); - - if (File.Exists(worldFilePath)) - { - return ReadImage(imageFilePath, worldFilePath, Path.Combine(dir, file + ".prj")); - } - - return ReadGeoTiff(imageFilePath); - } - - public static async Task ReadImage(string imageFilePath, string worldFilePath, string projFilePath = null) - { - var transform = ReadWorldFile(worldFilePath); - - var projection = (projFilePath != null && File.Exists(projFilePath)) - ? ReadProjectionFile(projFilePath) - : null; - - var bitmap = (BitmapSource)await ImageLoader.LoadImageAsync(imageFilePath); - - return new GeoImage(bitmap, transform, projection); - } - - public static Matrix ReadWorldFile(string path) - { - var parameters = File.ReadLines(path) - .Take(6) - .Select((line, i) => - { - if (!double.TryParse(line, NumberStyles.Float, CultureInfo.InvariantCulture, out double parameter)) - { - throw new ArgumentException("Failed parsing line " + (i + 1) + " in world file \"" + path + "\"."); - } - return parameter; - }) - .ToList(); - - if (parameters.Count != 6) - { - throw new ArgumentException("Insufficient number of parameters in world file \"" + path + "\"."); - } - - return new Matrix( - parameters[0], // line 1: A or M11 - parameters[1], // line 2: D or M12 - parameters[2], // line 3: B or M21 - parameters[3], // line 4: E or M22 - parameters[4], // line 5: C or OffsetX - parameters[5]); // line 6: F or OffsetY - } - - public static MapProjection ReadProjectionFile(string path) - { - return new GeoApiProjection { WKT = File.ReadAllText(path) }; - } - - public void ApplyToImage(Image image) - { - if (Rotation != 0d) - { - throw new InvalidOperationException("Rotation must be zero."); - } - - image.Source = Bitmap; - image.Stretch = Stretch.Fill; - - MapPanel.SetBoundingBox(image, BoundingBox); - } - - public FrameworkElement CreateImage() - { - FrameworkElement image = new Image - { - Source = Bitmap, - Stretch = Stretch.Fill - }; - - if (Rotation != 0d) - { - image.RenderTransform = new RotateTransform { Angle = Rotation }; - var panel = new Grid(); - panel.Children.Add(image); - image = panel; - } - - MapPanel.SetBoundingBox(image, BoundingBox); - - return image; - } - - public static async Task CreateImage(string imageFilePath) - { - return (await ReadImage(imageFilePath)).CreateImage(); - } - - private static async void PathPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) - { - if (o is Image image && e.NewValue is string imageFilePath) - { - (await ReadImage(imageFilePath)).ApplyToImage(image); - } - } - } -} - diff --git a/MapImages/Shared/GroundOverlayPanel.cs b/MapImages/Shared/GroundOverlayPanel.cs deleted file mode 100644 index 932edd82..00000000 --- a/MapImages/Shared/GroundOverlayPanel.cs +++ /dev/null @@ -1,276 +0,0 @@ -// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control -// © 2022 Clemens Fischer -// Licensed under the Microsoft Public License (Ms-PL) - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Threading.Tasks; -using System.Xml; -#if WINUI -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Media; -#elif UWP -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Media; -#else -using System.Windows; -using System.Windows.Controls; -using System.Windows.Media; -#endif - -namespace MapControl.Images -{ - public class GroundOverlayPanel : MapPanel - { - class LatLonBox : BoundingBox - { - public LatLonBox(double south, double west, double north, double east, double rotation) - : base(south, west, north, east) - { - Rotation = rotation; - } - - public double Rotation { get; } - } - - class ImageOverlay - { - public ImageOverlay(LatLonBox latLonBox, string imagePath, int zIndex) - { - LatLonBox = latLonBox; - ImagePath = imagePath; - ZIndex = zIndex; - } - - public LatLonBox LatLonBox { get; } - public string ImagePath { get; } - public int ZIndex { get; } - public ImageSource ImageSource { get; set; } - } - - public static readonly DependencyProperty KmlFileProperty = DependencyProperty.Register( - nameof(KmlFile), typeof(string), typeof(GroundOverlayPanel), - new PropertyMetadata(null, async (o, e) => await ((GroundOverlayPanel)o).KmlFilePropertyChanged((string)e.NewValue))); - - public string KmlFile - { - get { return (string)GetValue(KmlFileProperty); } - set { SetValue(KmlFileProperty, value); } - } - - private async Task KmlFilePropertyChanged(string path) - { - IEnumerable imageOverlays = null; - - if (!string.IsNullOrEmpty(path)) - { - try - { - var ext = Path.GetExtension(path).ToLower(); - if (ext == ".kmz") - { - imageOverlays = await ReadGroundOverlaysFromArchiveAsync(path); - } - else if (ext == ".kml") - { - imageOverlays = await ReadGroundOverlaysFromFileAsync(path); - } - } - catch (Exception ex) - { - Debug.WriteLine($"GroundOverlayPanel: {path}: {ex.Message}"); - } - } - - Children.Clear(); - - if (imageOverlays != null) - { - AddImageOverlays(imageOverlays); - } - } - - private void AddImageOverlays(IEnumerable imageOverlays) - { - foreach (var imageOverlay in imageOverlays.Where(i => i.ImageSource != null)) - { - FrameworkElement overlay = new Image - { - Source = imageOverlay.ImageSource, - Stretch = Stretch.Fill, - UseLayoutRounding = false - }; - - if (imageOverlay.LatLonBox.Rotation != 0d) - { - overlay.RenderTransform = new RotateTransform { Angle = -imageOverlay.LatLonBox.Rotation }; - overlay.RenderTransformOrigin = new Point(0.5, 0.5); - - // additional Panel for map rotation, see MapPanel.ArrangeElementWithBoundingBox - var panel = new Grid { UseLayoutRounding = false }; - panel.Children.Add(overlay); - overlay = panel; - } - - SetBoundingBox(overlay, imageOverlay.LatLonBox); - Canvas.SetZIndex(overlay, imageOverlay.ZIndex); - Children.Add(overlay); - } - } - - private static async Task> ReadGroundOverlaysFromArchiveAsync(string archiveFile) - { - using (var archive = await Task.Run(() => ZipFile.OpenRead(archiveFile))) - { - var docEntry = await Task.Run(() => archive.GetEntry("doc.kml") ?? archive.Entries.FirstOrDefault(e => e.Name.EndsWith(".kml"))); - - if (docEntry == null) - { - throw new ArgumentException("No KML entry found in " + archiveFile); - } - - var imageOverlays = await Task.Run(() => - { - var kmlDocument = new XmlDocument(); - - using (var docStream = docEntry.Open()) - { - kmlDocument.Load(docStream); - } - - return ReadGroundOverlays(kmlDocument).ToList(); - }); - - foreach (var imageOverlay in imageOverlays) - { - var imageEntry = await Task.Run(() => archive.GetEntry(imageOverlay.ImagePath)); - - if (imageEntry != null) - { - using (var zipStream = imageEntry.Open()) - using (var memoryStream = new MemoryStream()) - { - await zipStream.CopyToAsync(memoryStream); - memoryStream.Seek(0, SeekOrigin.Begin); - - imageOverlay.ImageSource = await ImageLoader.LoadImageAsync(memoryStream); - } - } - } - - return imageOverlays; - } - } - - private static async Task> ReadGroundOverlaysFromFileAsync(string docFile) - { - docFile = Path.GetFullPath(docFile); - var docUri = new Uri(docFile); - - var imageOverlays = await Task.Run(() => - { - var kmlDocument = new XmlDocument(); - kmlDocument.Load(docFile); - - return ReadGroundOverlays(kmlDocument).ToList(); - }); - - foreach (var imageOverlay in imageOverlays) - { - imageOverlay.ImageSource = await ImageLoader.LoadImageAsync(new Uri(docUri, imageOverlay.ImagePath)); - } - - return imageOverlays; - } - - private static IEnumerable ReadGroundOverlays(XmlDocument kmlDocument) - { - foreach (XmlElement groundOverlayElement in kmlDocument.GetElementsByTagName("GroundOverlay")) - { - LatLonBox latLonBox = null; - string imagePath = null; - int zIndex = 0; - - foreach (var childElement in groundOverlayElement.ChildNodes.OfType()) - { - switch (childElement.LocalName) - { - case "LatLonBox": - latLonBox = ReadLatLonBox(childElement); - break; - case "Icon": - imagePath = ReadImagePath(childElement); - break; - case "drawOrder": - int.TryParse(childElement.InnerText.Trim(), out zIndex); - break; - } - } - - if (latLonBox != null && imagePath != null) - { - yield return new ImageOverlay(latLonBox, imagePath, zIndex); - } - } - } - - private static string ReadImagePath(XmlElement element) - { - string href = null; - - foreach (var childElement in element.ChildNodes.OfType()) - { - switch (childElement.LocalName) - { - case "href": - href = childElement.InnerText.Trim(); - break; - } - } - - return href; - } - - private static LatLonBox ReadLatLonBox(XmlElement element) - { - double north = double.NaN; - double south = double.NaN; - double east = double.NaN; - double west = double.NaN; - double rotation = 0d; - - foreach (var childElement in element.ChildNodes.OfType()) - { - switch (childElement.LocalName) - { - case "north": - double.TryParse(childElement.InnerText.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out north); - break; - case "south": - double.TryParse(childElement.InnerText.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out south); - break; - case "east": - double.TryParse(childElement.InnerText.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out east); - break; - case "west": - double.TryParse(childElement.InnerText.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out west); - break; - case "rotation": - double.TryParse(childElement.InnerText.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out rotation); - break; - } - } - - return !double.IsNaN(north) && !double.IsNaN(south) && !double.IsNaN(east) && !double.IsNaN(west) - ? new LatLonBox(south, west, north, east, rotation) - : null; - } - } -} diff --git a/MapImages/UWP/MapImages.UWP.csproj b/MapImages/UWP/MapImages.UWP.csproj deleted file mode 100644 index 06c5da19..00000000 --- a/MapImages/UWP/MapImages.UWP.csproj +++ /dev/null @@ -1,85 +0,0 @@ - - - - - Debug - AnyCPU - {BE08B7BC-8C89-4837-BCE7-EDDDABEAB372} - Library - Properties - MapControl.Images - MapImages.UWP - en-US - UAP - 10.0.19041.0 - 10.0.17134.0 - 14 - 512 - {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;UWP - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - UWP - prompt - 4 - - - PackageReference - - - - GeoImage.cs - - - GroundOverlayPanel.cs - - - GeoImage.WinUI.cs - - - - - - - 6.2.13 - - - - - {9545f73c-9c35-4cf6-baae-19a0baebd344} - MapControl.UWP - - - {9ee69591-5edc-45e3-893e-2f9a4b82d538} - MapProjections.UWP - - - - - MapControl.snk - - - - 14.0 - - - true - - - ..\..\MapControl.snk - - - \ No newline at end of file diff --git a/MapImages/UWP/Properties/AssemblyInfo.cs b/MapImages/UWP/Properties/AssemblyInfo.cs deleted file mode 100644 index 9763a405..00000000 --- a/MapImages/UWP/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("XAML Map Control Image Support Library for UWP")] -[assembly: AssemblyProduct("XAML Map Control")] -[assembly: AssemblyCompany("Clemens Fischer")] -[assembly: AssemblyCopyright("Copyright © 2022 Clemens Fischer")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyVersion("7.1.0")] -[assembly: AssemblyFileVersion("7.1.0")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] diff --git a/MapImages/UWP/Properties/MapImages.UWP.rd.xml b/MapImages/UWP/Properties/MapImages.UWP.rd.xml deleted file mode 100644 index bebda206..00000000 --- a/MapImages/UWP/Properties/MapImages.UWP.rd.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - diff --git a/MapImages/WPF/GeoImage.WPF.cs b/MapImages/WPF/GeoImage.WPF.cs deleted file mode 100644 index 5847e937..00000000 --- a/MapImages/WPF/GeoImage.WPF.cs +++ /dev/null @@ -1,109 +0,0 @@ -// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control -// © 2022 Clemens Fischer -// Licensed under the Microsoft Public License (Ms-PL) - -using System; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using System.Windows.Media; -using System.Windows.Media.Imaging; - -namespace MapControl.Images -{ - public partial class GeoImage - { - public static Task ReadGeoTiff(string imageFilePath) - { - return Task.Run(() => - { - BitmapSource bitmap; - Matrix transform; - - using (var stream = File.OpenRead(imageFilePath)) - { - bitmap = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); - } - - var metadata = bitmap.Metadata as BitmapMetadata; - - if (metadata.GetQuery((string)PixelScaleQuery) is double[] pixelScale && pixelScale.Length == 3 && - metadata.GetQuery((string)TiePointQuery) is double[] tiePoint && tiePoint.Length >= 6) - { - transform = new Matrix(pixelScale[0], 0d, 0d, -pixelScale[1], tiePoint[3], tiePoint[4]); - } - else if (metadata.GetQuery((string)TransformQuery) is double[] tform && tform.Length == 16) - { - transform = new Matrix(tform[0], tform[1], tform[4], tform[5], tform[3], tform[7]); - } - else - { - throw new ArgumentException("No coordinate transformation found in \"" + imageFilePath + "\"."); - } - - if (metadata.GetQuery((string)NoDataQuery) is string noData && int.TryParse(noData, out int noDataValue)) - { - bitmap = ConvertTransparentPixel(bitmap, noDataValue); - } - - return new GeoImage(bitmap, transform, (MapProjection)null); - }); - } - - public static BitmapSource ConvertTransparentPixel(BitmapSource source, int transparentPixel) - { - BitmapPalette sourcePalette = null; - var targetFormat = source.Format; - - if (source.Format == PixelFormats.Indexed8 || - source.Format == PixelFormats.Indexed4 || - source.Format == PixelFormats.Indexed2 || - source.Format == PixelFormats.Indexed1) - { - sourcePalette = source.Palette; - } - else if (source.Format == PixelFormats.Gray8) - { - sourcePalette = BitmapPalettes.Gray256; - targetFormat = PixelFormats.Indexed8; - } - else if (source.Format == PixelFormats.Gray4) - { - sourcePalette = BitmapPalettes.Gray16; - targetFormat = PixelFormats.Indexed4; - } - else if (source.Format == PixelFormats.Gray2) - { - sourcePalette = BitmapPalettes.Gray4; - targetFormat = PixelFormats.Indexed2; - } - else if (source.Format == PixelFormats.BlackWhite) - { - sourcePalette = BitmapPalettes.BlackAndWhite; - targetFormat = PixelFormats.Indexed1; - } - - if (sourcePalette == null || transparentPixel >= sourcePalette.Colors.Count) - { - return source; - } - - var colors = sourcePalette.Colors.ToList(); - - colors[transparentPixel] = Colors.Transparent; - - var stride = (source.PixelWidth * source.Format.BitsPerPixel + 7) / 8; - var buffer = new byte[stride * source.PixelHeight]; - - source.CopyPixels(buffer, stride, 0); - - var target = BitmapSource.Create( - source.PixelWidth, source.PixelHeight, source.DpiX, source.DpiY, - targetFormat, new BitmapPalette(colors), buffer, stride); - - target.Freeze(); - - return target; - } - } -} diff --git a/MapImages/WPF/MapImages.WPF.csproj b/MapImages/WPF/MapImages.WPF.csproj deleted file mode 100644 index 1d9be0bc..00000000 --- a/MapImages/WPF/MapImages.WPF.csproj +++ /dev/null @@ -1,35 +0,0 @@ - - - net6.0-windows;net5.0-windows;netcoreapp3.1;net48;net462 - true - MapControl.Images - XAML Map Control Image Support Library for WPF - XAML Map Control - 7.1.0 - Clemens Fischer - Copyright © 2022 Clemens Fischer - true - ..\..\MapControl.snk - false - false - XAML.MapControl.MapImages - - - - - - - - - - - - - - - - - - - - diff --git a/MapImages/WinUI/GeoImage.WinUI.cs b/MapImages/WinUI/GeoImage.WinUI.cs deleted file mode 100644 index eeb8b6c7..00000000 --- a/MapImages/WinUI/GeoImage.WinUI.cs +++ /dev/null @@ -1,66 +0,0 @@ -// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control -// © 2022 Clemens Fischer -// Licensed under the Microsoft Public License (Ms-PL) - -using System; -using System.IO; -using System.Threading.Tasks; -using System.Collections.Generic; -using Windows.Graphics.Imaging; -using Windows.Storage; -#if WINUI -using Microsoft.UI.Xaml.Media.Imaging; -#else -using Windows.UI.Xaml.Media.Imaging; -#endif - -namespace MapControl.Images -{ - public partial class GeoImage - { - public static async Task ReadGeoTiff(string imageFilePath) - { - var file = await StorageFile.GetFileFromPathAsync(Path.GetFullPath(imageFilePath)); - - using (var stream = await file.OpenReadAsync()) - { - WriteableBitmap bitmap; - Matrix transform; - - var decoder = await BitmapDecoder.CreateAsync(stream); - - using (var swbmp = await decoder.GetSoftwareBitmapAsync()) - { - bitmap = new WriteableBitmap(swbmp.PixelWidth, swbmp.PixelHeight); - swbmp.CopyToBuffer(bitmap.PixelBuffer); - } - - var query = new List - { - PixelScaleQuery, TiePointQuery, TransformQuery //, NoDataQuery - }; - - var metadata = await decoder.BitmapProperties.GetPropertiesAsync(query); - - if (metadata.TryGetValue(PixelScaleQuery, out BitmapTypedValue pixelScaleValue) && - pixelScaleValue.Value is double[] pixelScale && pixelScale.Length == 3 && - metadata.TryGetValue(TiePointQuery, out BitmapTypedValue tiePointValue) && - tiePointValue.Value is double[] tiePoint && tiePoint.Length >= 6) - { - transform = new Matrix(pixelScale[0], 0d, 0d, -pixelScale[1], tiePoint[3], tiePoint[4]); - } - else if (metadata.TryGetValue(TransformQuery, out BitmapTypedValue tformValue) && - tformValue.Value is double[] tform && tform.Length == 16) - { - transform = new Matrix(tform[0], tform[1], tform[4], tform[5], tform[3], tform[7]); - } - else - { - throw new ArgumentException("No coordinate transformation found in \"" + imageFilePath + "\"."); - } - - return new GeoImage(bitmap, transform, null); - } - } - } -} diff --git a/MapImages/WinUI/MapImages.WinUI.csproj b/MapImages/WinUI/MapImages.WinUI.csproj deleted file mode 100644 index 64212e9b..00000000 --- a/MapImages/WinUI/MapImages.WinUI.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - net6.0-windows10.0.19041.0 - 10.0.17763.0 - win10-x86;win10-x64;win10-arm64 - true - MapControl.Images - XAML Map Control Image Support Library for WinUI - XAML Map Control - 7.1.0 - Clemens Fischer - Copyright © 2022 Clemens Fischer - true - ..\..\MapControl.snk - false - false - XAML.MapControl.MapImages - WINUI - - - - - - - - - - - - - - - - - - - -