public static async Task DrawLayerAsync( ITileSource source, int width, int height, Models.Bounds boundingBox, SKCanvas outputCanvas, bool isTransparent, uint backgroundColor) { // TODO: check SRS support in source if ((String.Compare(source.Configuration.Type, SourceConfiguration.TypeWms, StringComparison.OrdinalIgnoreCase) == 0) && (source.Configuration.Cache == null)) { // Cascading GetMap request to WMS source as single GetMap request var imageData = await((TileSources.HttpTileSource)source).GetWmsMapAsync(width, height, boundingBox, isTransparent, backgroundColor); if (imageData != null) { WmsHelper.DrawImageUnscaledToRasterCanvas(outputCanvas, imageData); } } else { var tileCoordinates = WmsHelper.BuildTileCoordinatesList(boundingBox, width); var sourceTiles = await GetSourceTilesAsync(source, tileCoordinates); if (sourceTiles.Count > 0) { WmsHelper.DrawWebMercatorTilesToRasterCanvas(outputCanvas, width, height, boundingBox, sourceTiles, backgroundColor, U.WebMercator.TileSize); } } }
private async Task <byte[]> CreateMapImageAsync( int width, int height, Models.Bounds boundingBox, string mediaType, bool isTransparent, uint backgroundColor, IList <string> layerNames) { var imageInfo = new SKImageInfo( width: width, height: height, colorType: SKColorType.Rgba8888, alphaType: SKAlphaType.Premul); using var surface = SKSurface.Create(imageInfo); using var canvas = surface.Canvas; canvas.Clear(new SKColor(backgroundColor)); foreach (var layerName in layerNames) { if (this.tileSourceFabric.Contains(layerName)) { await WmsHelper.DrawLayerAsync( // TODO: ? pass required format to avoid conversions this.tileSourceFabric.Get(layerName), width, height, boundingBox, canvas, isTransparent, backgroundColor); } } using SKImage image = surface.Snapshot(); if (String.Compare(mediaType, MediaTypeNames.Image.Tiff, StringComparison.OrdinalIgnoreCase) == 0) { using var bitmap = SKBitmap.FromImage(image); // TODO: improve performance of pixels processing, maybe using unsafe/pointers var pixels = bitmap.Pixels.SelectMany(p => new byte[] { p.Red, p.Green, p.Blue, p.Alpha }).ToArray(); var tiff = ImageHelper.CreateTiffImage(pixels, image.Width, image.Height); return(tiff); } else { var imageFormat = U.ImageHelper.SKEncodedImageFormatFromMediaType(mediaType); using SKData data = image.Encode(imageFormat, 90); // TODO: ? quality parameter return(data.ToArray()); } }
private static void DrawWebMercatorTilesToRasterCanvas( SKCanvas outputCanvas, int width, int height, Models.Bounds boundingBox, IList <Models.TileDataset> sourceTiles, uint backgroundColor, int tileSize) { var zoom = sourceTiles[0].Z; var tileMinX = sourceTiles.Min(t => t.X); var tileMinY = sourceTiles.Min(t => t.Y); var tilesCountX = sourceTiles.Max(t => t.X) - tileMinX + 1; var tilesCountY = sourceTiles.Max(t => t.Y) - tileMinY + 1; var canvasWidth = tilesCountX * tileSize; var canvasHeight = tilesCountY * tileSize; var imageInfo = new SKImageInfo( width: canvasWidth, height: canvasHeight, colorType: SKColorType.Rgba8888, alphaType: SKAlphaType.Premul); using var surface = SKSurface.Create(imageInfo); using var canvas = surface.Canvas; canvas.Clear(new SKColor(backgroundColor)); // Draw all tiles foreach (var sourceTile in sourceTiles) { var offsetX = (sourceTile.X - tileMinX) * tileSize; var offsetY = (sourceTile.Y - tileMinY) * tileSize; using var sourceImage = SKImage.FromEncodedData(sourceTile.ImageData); canvas.DrawImage(sourceImage, SKRect.Create(offsetX, offsetY, tileSize, tileSize)); // Source tile scaled to dest rectangle, if needed } // Clip and scale to requested size of output image var geoBBox = EntitiesConverter.MapRectangleToGeographicalBounds(boundingBox); var pixelOffsetX = WebMercator.LongitudeToPixelXAtZoom(geoBBox.MinLongitude, zoom) - tileSize * tileMinX; var pixelOffsetY = WebMercator.LatitudeToPixelYAtZoom(geoBBox.MaxLatitude, zoom) - tileSize * tileMinY; var pixelWidth = WebMercator.LongitudeToPixelXAtZoom(geoBBox.MaxLongitude, zoom) - WebMercator.LongitudeToPixelXAtZoom(geoBBox.MinLongitude, zoom); var pixelHeight = WebMercator.LatitudeToPixelYAtZoom(geoBBox.MinLatitude, zoom) - WebMercator.LatitudeToPixelYAtZoom(geoBBox.MaxLatitude, zoom); var sourceRectangle = SKRect.Create((float)pixelOffsetX, (float)pixelOffsetY, (float)pixelWidth, (float)pixelHeight); var destRectangle = SKRect.Create(0, 0, width, height); using SKImage canvasImage = surface.Snapshot(); outputCanvas.DrawImage(canvasImage, sourceRectangle, destRectangle, new SKPaint { FilterQuality = SKFilterQuality.High, }); }
public static List <Models.TileCoordinates> BuildTileCoordinatesList(Models.Bounds boundingBox, int width) { var geoBBox = EntitiesConverter.MapRectangleToGeographicalBounds(boundingBox); var zoomLevel = FindOptimalTileZoomLevel(width, geoBBox); var tileCoordMin = GetTileCoordinatesAtPoint(geoBBox.MinLongitude, geoBBox.MinLatitude, zoomLevel); var tileCoordMax = GetTileCoordinatesAtPoint(geoBBox.MaxLongitude, geoBBox.MaxLatitude, zoomLevel); var tileCoordinates = new List <Models.TileCoordinates>(); for (var tileX = tileCoordMin.X; tileX <= tileCoordMax.X; tileX++) { for (var tileY = tileCoordMax.Y; tileY <= tileCoordMin.Y; tileY++) { tileCoordinates.Add(new Models.TileCoordinates(tileX, tileY, zoomLevel)); } } return(tileCoordinates); }
internal async Task <byte[]?> GetWmsMapAsync( int width, int height, Models.Bounds boundingBox, bool isTransparent, uint backgroundColor) { if (this.client == null) { throw new InvalidOperationException("HTTP client was not initialized."); } if (String.IsNullOrEmpty(this.configuration.Location)) { throw new InvalidOperationException("configuration.Location is null or empty"); } var url = Wms.QueryUtility.GetMapUrl(this.configuration, width, height, boundingBox, isTransparent, backgroundColor); var response = await client.GetAsync(url); if (response.StatusCode == HttpStatusCode.OK) { if (response.Content.Headers.ContentType != null && response.Content.Headers.ContentType.MediaType == MediaTypeNames.Application.OgcServiceExceptionXml) { var message = await response.Content.ReadAsStringAsync(); System.Diagnostics.Debug.WriteLine(message); // TODO: write error details to log return(null); } // TODO: more checks of Content-Type, response size, etc. var data = await response.Content.ReadAsByteArrayAsync(); return(data); } else { return(null); } }
public static string GetMapUrl( SourceConfiguration configuration, int width, int height, Models.Bounds boundingBox, bool isTransparent, uint backgroundColor) { var location = configuration.Location; if (String.IsNullOrWhiteSpace(location)) { throw new ArgumentException("Location must be valid string"); } var baseUri = Utils.UrlHelper.GetQueryBase(location); var items = Utils.UrlHelper.GetQueryParameters(location); // Version var wmsVersion = Identifiers.Version111; // Default WMS version is 1.1.1 if (configuration.Wms != null && !String.IsNullOrWhiteSpace(configuration.Wms.Version)) { wmsVersion = configuration.Wms.Version; } else if (items.Any(kvp => kvp.Key == WmsQueryVersion)) { wmsVersion = items.First(kvp => kvp.Key == WmsQueryVersion).Value; } // Layers var layers = String.Empty; if (configuration.Wms != null && !String.IsNullOrWhiteSpace(configuration.Wms.Layer)) { layers = configuration.Wms.Layer; // TODO: ? multiple layers } else if (items.Any(kvp => kvp.Key == WmsQueryLayers)) { layers = items.First(kvp => kvp.Key == WmsQueryLayers).Value; } // Format var format = MediaTypeNames.Image.Png; if (!String.IsNullOrWhiteSpace(configuration.ContentType)) { format = configuration.ContentType; } RemoveKnownParameters(items); var qb = new QueryBuilder(items) { { WmsQueryService, Identifiers.Wms }, { WmsQueryRequest, Identifiers.GetMap }, { WmsQueryVersion, wmsVersion }, { WmsQueryLayers, layers }, { (wmsVersion == Identifiers.Version130) ? WmsQueryCrs : WmsQuerySrs, EPSG3857 }, // TODO: EPSG:4326 support { WmsQueryBBox, boundingBox.ToBBoxString() }, { WmsQueryWidth, width.ToString(CultureInfo.InvariantCulture) }, { WmsQueryHeight, height.ToString(CultureInfo.InvariantCulture) }, { WmsQueryFormat, format }, }; if (isTransparent) { qb.Add(WmsQueryTransparent, "true"); } qb.Add(WmsQueryBackgroundColor, "0x" + backgroundColor.ToString("X8")); return(baseUri + qb.ToQueryString()); }