diff --git a/MBTiles/Shared/MBTileData.cs b/MBTiles/Shared/MBTileData.cs deleted file mode 100644 index d0d64f7c..00000000 --- a/MBTiles/Shared/MBTileData.cs +++ /dev/null @@ -1,139 +0,0 @@ -// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control -// Copyright © 2024 Clemens Fischer -// Licensed under the Microsoft Public License (Ms-PL) - -using System; -using System.Collections.Generic; -using System.Data.SQLite; -using System.Diagnostics; -using System.IO; -using System.Threading.Tasks; - -namespace MapControl.MBTiles -{ - public sealed class MBTileData : IDisposable - { - private readonly SQLiteConnection connection; - - public IDictionary Metadata { get; } = new Dictionary(); - - private MBTileData(string file) - { - connection = new SQLiteConnection("Data Source=" + Path.GetFullPath(file)); - } - - public void Dispose() - { - connection.Dispose(); - } - - public static async Task CreateAsync(string file) - { - var tileData = new MBTileData(file); - - await tileData.OpenAsync(); - await tileData.ReadMetadataAsync(); - - return tileData; - } - - private async Task OpenAsync() - { - await connection.OpenAsync(); - - using (var command = new SQLiteCommand("create table if not exists metadata (name string, value string)", connection)) - { - await command.ExecuteNonQueryAsync(); - } - - using (var command = new SQLiteCommand("create table if not exists tiles (zoom_level integer, tile_column integer, tile_row integer, tile_data blob)", connection)) - { - await command.ExecuteNonQueryAsync(); - } - } - - private async Task ReadMetadataAsync() - { - try - { - using (var command = new SQLiteCommand("select * from metadata", connection)) - { - var reader = await command.ExecuteReaderAsync(); - - while (await reader.ReadAsync()) - { - Metadata[(string)reader["name"]] = (string)reader["value"]; - } - } - } - catch (Exception ex) - { - Debug.WriteLine($"MBTileData: {ex.Message}"); - } - } - - public async Task WriteMetadataAsync() - { - try - { - using (var command = new SQLiteCommand("insert or replace into metadata (name, value) values (@n, @v)", connection)) - { - foreach (var keyValue in Metadata) - { - command.Parameters.AddWithValue("@n", keyValue.Key); - command.Parameters.AddWithValue("@v", keyValue.Value); - - await command.ExecuteNonQueryAsync(); - } - } - } - catch (Exception ex) - { - Debug.WriteLine($"MBTileData: {ex.Message}"); - } - } - - public async Task ReadImageBufferAsync(int x, int y, int zoomLevel) - { - byte[] imageBuffer = null; - - try - { - using (var command = new SQLiteCommand("select tile_data from tiles where zoom_level=@z and tile_column=@x and tile_row=@y", connection)) - { - command.Parameters.AddWithValue("@z", zoomLevel); - command.Parameters.AddWithValue("@x", x); - command.Parameters.AddWithValue("@y", (1 << zoomLevel) - y - 1); - - imageBuffer = await command.ExecuteScalarAsync() as byte[]; - } - } - catch (Exception ex) - { - Debug.WriteLine($"MBTileData: {zoomLevel}/{x}/{y}: {ex.Message}"); - } - - return imageBuffer; - } - - public async Task WriteImageBufferAsync(int x, int y, int zoomLevel, byte[] imageBuffer) - { - try - { - using (var command = new SQLiteCommand("insert or replace into tiles (zoom_level, tile_column, tile_row, tile_data) values (@z, @x, @y, @b)", connection)) - { - command.Parameters.AddWithValue("@z", zoomLevel); - command.Parameters.AddWithValue("@x", x); - command.Parameters.AddWithValue("@y", (1 << zoomLevel) - y - 1); - command.Parameters.AddWithValue("@b", imageBuffer); - - await command.ExecuteNonQueryAsync(); - } - } - catch (Exception ex) - { - Debug.WriteLine($"MBTileData: {zoomLevel}/{x}/{y}: {ex.Message}"); - } - } - } -} diff --git a/MBTiles/Shared/MBTileLayer.cs b/MBTiles/Shared/MBTileLayer.cs index b6949632..309bd139 100644 --- a/MBTiles/Shared/MBTileLayer.cs +++ b/MBTiles/Shared/MBTileLayer.cs @@ -2,6 +2,8 @@ // Copyright © 2024 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) +using System; +using System.Diagnostics; using System.Threading.Tasks; #if WPF using System.Windows; @@ -33,14 +35,25 @@ namespace MapControl.MBTiles /// /// May be overridden to create a derived MBTileSource that handles other tile formats than png and jpg. /// - protected virtual MBTileSource CreateTileSource(MBTileData tileData) + protected virtual async Task CreateTileSourceAsync(string file) { - return new MBTileSource(tileData); + var tileSource = new MBTileSource(); + + await tileSource.OpenAsync(file); + + if (tileSource.Metadata.TryGetValue("format", out string format) && format != "png" && format != "jpg") + { + tileSource.Dispose(); + + throw new NotSupportedException($"Tile image format {format} is not supported."); + } + + return tileSource; } private async Task FilePropertyChanged(string file) { - (TileSource as MBTileSource)?.Dispose(); + (TileSource as MBTileSource)?.Close(); ClearValue(TileSourceProperty); ClearValue(SourceNameProperty); @@ -48,31 +61,38 @@ namespace MapControl.MBTiles ClearValue(MinZoomLevelProperty); ClearValue(MaxZoomLevelProperty); - if (file != null) + if (!string.IsNullOrEmpty(file)) { - var tileData = await MBTileData.CreateAsync(file); - - if (tileData.Metadata.TryGetValue("name", out string sourceName)) + try { - SourceName = sourceName; - } + var tileSource = await CreateTileSourceAsync(file); - if (tileData.Metadata.TryGetValue("description", out string description)) + TileSource = tileSource; + + if (tileSource.Metadata.TryGetValue("name", out string value)) + { + SourceName = value; + } + + if (tileSource.Metadata.TryGetValue("description", out value)) + { + Description = value; + } + + if (tileSource.Metadata.TryGetValue("minzoom", out value) && int.TryParse(value, out int zoomLevel)) + { + MinZoomLevel = zoomLevel; + } + + if (tileSource.Metadata.TryGetValue("maxzoom", out value) && int.TryParse(value, out zoomLevel)) + { + MaxZoomLevel = zoomLevel; + } + } + catch (Exception ex) { - Description = description; + Debug.WriteLine($"MBTileLayer: {ex.Message}"); } - - if (tileData.Metadata.TryGetValue("minzoom", out sourceName) && int.TryParse(sourceName, out int minZoom)) - { - MinZoomLevel = minZoom; - } - - if (tileData.Metadata.TryGetValue("maxzoom", out sourceName) && int.TryParse(sourceName, out int maxZoom)) - { - MaxZoomLevel = maxZoom; - } - - TileSource = CreateTileSource(tileData); } } } diff --git a/MBTiles/Shared/MBTileSource.cs b/MBTiles/Shared/MBTileSource.cs index c88fe899..06b663e4 100644 --- a/MBTiles/Shared/MBTileSource.cs +++ b/MBTiles/Shared/MBTileSource.cs @@ -3,7 +3,10 @@ // Licensed under the Microsoft Public License (Ms-PL) using System; +using System.Collections.Generic; +using System.Data.SQLite; using System.Diagnostics; +using System.IO; using System.Threading.Tasks; #if WPF using System.Windows.Media; @@ -19,19 +22,46 @@ namespace MapControl.MBTiles { public class MBTileSource : TileSource, IDisposable { - public MBTileData TileData { get; } + private SQLiteConnection connection; - public MBTileSource(MBTileData tiledata) + public IDictionary Metadata { get; } = new Dictionary(); + + public async Task OpenAsync(string file) { - var format = tiledata.Metadata["format"]; + Close(); - if (format == "png" || format == "jpg") + connection = new SQLiteConnection("Data Source=" + Path.GetFullPath(file)); + + await connection.OpenAsync(); + + using (var command = new SQLiteCommand("create table if not exists metadata (name string, value string)", connection)) { - TileData = tiledata; + await command.ExecuteNonQueryAsync(); } - else + + using (var command = new SQLiteCommand("create table if not exists tiles (zoom_level integer, tile_column integer, tile_row integer, tile_data blob)", connection)) { - Debug.WriteLine($"MBTileSource: unsupported format '{format}'"); + await command.ExecuteNonQueryAsync(); + } + + using (var command = new SQLiteCommand("select * from metadata", connection)) + { + var reader = await command.ExecuteReaderAsync(); + + while (await reader.ReadAsync()) + { + Metadata[(string)reader["name"]] = (string)reader["value"]; + } + } + } + + public void Close() + { + if (connection != null) + { + Metadata.Clear(); + connection.Dispose(); + connection = null; } } @@ -43,9 +73,21 @@ namespace MapControl.MBTiles protected virtual void Dispose(bool disposing) { - if (disposing && TileData != null) + if (disposing) { - TileData.Dispose(); + Close(); + } + } + + public async Task ReadImageBufferAsync(int x, int y, int zoomLevel) + { + using (var command = new SQLiteCommand("select tile_data from tiles where zoom_level=@z and tile_column=@x and tile_row=@y", connection)) + { + command.Parameters.AddWithValue("@z", zoomLevel); + command.Parameters.AddWithValue("@x", x); + command.Parameters.AddWithValue("@y", (1 << zoomLevel) - y - 1); + + return await command.ExecuteScalarAsync() as byte[]; } } @@ -53,22 +95,19 @@ namespace MapControl.MBTiles { ImageSource image = null; - if (TileData != null) + try { - var buffer = await TileData.ReadImageBufferAsync(x, y, zoomLevel); + var buffer = await ReadImageBufferAsync(x, y, zoomLevel); if (buffer != null) { - try - { - image = await ImageLoader.LoadImageAsync(buffer); - } - catch (Exception ex) - { - Debug.WriteLine($"MBTileSource : {ex.Message}"); - } + image = await ImageLoader.LoadImageAsync(buffer); } } + catch (Exception ex) + { + Debug.WriteLine($"MBTileSource: {ex.Message}"); + } return image; }