/// <summary> /// /// </summary> /// <param name="layers">The layers to query for MapInfo</param> /// <param name="viewport">The current Viewport</param> /// <param name="screenPosition">The screenposition to query</param> /// <param name="symbolCache">The </param> /// <param name="margin">Margin of error in pixels. If the distance between screen position and geometry /// is smaller than the margin it is seen as a hit.</param> /// <returns></returns> public static MapInfo GetMapInfo(IEnumerable <ILayer> layers, IReadOnlyViewport viewport, Point screenPosition, ISymbolCache symbolCache, int margin = 0) { var worldPosition = viewport.ScreenToWorld(screenPosition); return(GetMapInfo(layers, worldPosition, screenPosition, viewport.Resolution, symbolCache, margin)); }
public MapInfo GetMapInfo(double x, double y, IReadOnlyViewport viewport, IEnumerable <ILayer> layers, int margin = 0) { // todo: use margin to increase the pixel area // todo: We will need to select on style instead of layer layers = layers .Select(l => (l is RasterizingLayer rl) ? rl.ChildLayer : l) .Where(l => l.IsMapInfoLayer); var list = new List <MapInfoRecord>(); var result = new MapInfo() { ScreenPosition = new Point(x, y), WorldPosition = viewport.ScreenToWorld(x, y), Resolution = viewport.Resolution }; try { var width = (int)viewport.Width; var height = (int)viewport.Height; var imageInfo = new SKImageInfo(width, height, SKImageInfo.PlatformColorType, SKAlphaType.Unpremul); var intX = (int)x; var intY = (int)y; using (var surface = SKSurface.Create(imageInfo)) { if (surface == null) { return(null); } surface.Canvas.ClipRect(new SKRect((float)(x - 1), (float)(y - 1), (float)(x + 1), (float)(y + 1))); surface.Canvas.Clear(SKColors.Transparent); var pixmap = surface.PeekPixels(); var color = pixmap.GetPixelColor(intX, intY); VisibleFeatureIterator.IterateLayers(viewport, layers, (v, layer, style, feature, opacity) => { surface.Canvas.Save(); // 1) Clear the entire bitmap surface.Canvas.Clear(SKColors.Transparent); // 2) Render the feature to the clean canvas RenderFeature(surface.Canvas, v, layer, style, feature, opacity); // 3) Check if the pixel has changed. if (color != pixmap.GetPixelColor(intX, intY)) { // 4) Add feature and style to result list.Add(new MapInfoRecord(feature, style, layer)); } surface.Canvas.Restore(); }); } if (list.Count == 0) { return(result); } list.Reverse(); var itemDrawnOnTop = list.First(); result.Feature = itemDrawnOnTop.Feature; result.Style = itemDrawnOnTop.Style; result.Layer = itemDrawnOnTop.Layer; result.MapInfoRecords = list; } catch (Exception exception) { Logger.Log(LogLevel.Error, "Unexpected error in skia renderer", exception); } return(result); }
public void Draw(SKCanvas canvas, IReadOnlyViewport viewport, IWidget widget, float layerOpacity) { canvas.RotateDegrees((float)viewport.Rotation, 0.0f, 0.0f); var TL = viewport.ScreenToWorld(canvas.LocalClipBounds.Left, canvas.LocalClipBounds.Top); var BR = viewport.ScreenToWorld(canvas.LocalClipBounds.Right, canvas.LocalClipBounds.Bottom); var width = viewport.Extent.Width; var height = viewport.Extent.Height; var usedLineOffset = lineOffset; IEnumerable <SKPaint> usedPaints = paints; if (viewport.Resolution > 0.5) { usedLineOffset *= 10; usedPaints = usedPaints.Skip(1).ToArray(); } if (viewport.Resolution > 3) { usedLineOffset *= 10; usedPaints = usedPaints.Skip(1).ToArray(); } int lineCount100M = (int)(100 / usedLineOffset); //How many lines are 1000m int lineCount1000M = (int)(1000 / usedLineOffset); if (lineCount100M == 0) { lineCount100M = 9000; //will never be reached, if we only render 1k's } double screenWidthPerLine = canvas.LocalClipBounds.Width / (width / usedLineOffset); double screenHeightPerLine = canvas.LocalClipBounds.Height / (height / usedLineOffset); //World coordinates of first lineOffset line var first100mW = (TL.X + (usedLineOffset - TL.X % usedLineOffset)); var first100mH = (TL.Y + (usedLineOffset - TL.Y % usedLineOffset)); //Screen offset of first lineOffset line double offsetCW = ((first100mW - TL.X) / usedLineOffset) * screenWidthPerLine; double offsetCH = screenHeightPerLine + ((TL.Y - first100mH) / usedLineOffset) * screenHeightPerLine; //offset of next 1k int KOffsetW = (int)((first100mW % 1000) / usedLineOffset); if (KOffsetW < 0) { KOffsetW = 10 + KOffsetW; } int KOffsetH = (int)((first100mH % 1000) / usedLineOffset) - 1; if (lineCount1000M > 1) { KOffsetH = lineCount1000M - KOffsetH; } for (double curX = offsetCW; curX < canvas.LocalClipBounds.Right; curX += screenWidthPerLine) { SKPaint paint; if (KOffsetW >= 100 && KOffsetW % 100 == 0) { paint = usedPaints.ElementAt(2); } else if (KOffsetW >= 10 && KOffsetW % 10 == 0) { paint = usedPaints.ElementAt(1); } else { paint = usedPaints.ElementAt(0); } if (KOffsetW == lineCount1000M) { KOffsetW = 0; } canvas.DrawLine((float)curX, 0, (float)curX, canvas.LocalClipBounds.Height, paint); KOffsetW++; } for (double curH = offsetCH; curH < canvas.LocalClipBounds.Bottom; curH += screenHeightPerLine) { SKPaint paint; if (KOffsetH >= 100 && KOffsetH % 100 == 0) { paint = usedPaints.ElementAt(2); } else if (KOffsetH >= 10 && KOffsetH % 10 == 0) { paint = usedPaints.ElementAt(1); } else { paint = usedPaints.ElementAt(0); } if (KOffsetH == lineCount1000M) { KOffsetH = 0; } canvas.DrawLine(0, (float)curH, canvas.LocalClipBounds.Width, (float)curH, paint); KOffsetH++; } }
public MapInfo?GetMapInfo(double x, double y, IReadOnlyViewport viewport, IEnumerable <ILayer> layers, int margin = 0) { // todo: use margin to increase the pixel area // todo: We will need to select on style instead of layer layers = layers .Select(l => (l is ISourceLayer sl) ? sl.SourceLayer : l) .Where(l => l.IsMapInfoLayer); var list = new List <MapInfoRecord>(); var result = new MapInfo { ScreenPosition = new MPoint(x, y), WorldPosition = viewport.ScreenToWorld(x, y), Resolution = viewport.Resolution }; if (!viewport.Extent?.Contains(viewport.ScreenToWorld(result.ScreenPosition)) ?? false) { return(result); } try { var width = (int)viewport.Width; var height = (int)viewport.Height; var imageInfo = new SKImageInfo(width, height, SKImageInfo.PlatformColorType, SKAlphaType.Unpremul); var intX = (int)x; var intY = (int)y; if (intX >= width || intY >= height) { return(result); } using (var surface = SKSurface.Create(imageInfo)) { if (surface == null) { return(null); } surface.Canvas.ClipRect(new SKRect((float)(x - 1), (float)(y - 1), (float)(x + 1), (float)(y + 1))); surface.Canvas.Clear(SKColors.Transparent); using var pixmap = surface.PeekPixels(); var color = pixmap.GetPixelColor(intX, intY); VisibleFeatureIterator.IterateLayers(viewport, layers, 0, (v, layer, style, feature, opacity, iteration) => { // ReSharper disable AccessToDisposedClosure // There is no delayed fetch. After IterateLayers returns all is done. I do not see a problem. surface.Canvas.Save(); // 1) Clear the entire bitmap surface.Canvas.Clear(SKColors.Transparent); // 2) Render the feature to the clean canvas RenderFeature(surface.Canvas, v, layer, style, feature, opacity, 0); // 3) Check if the pixel has changed. if (color != pixmap.GetPixelColor(intX, intY)) { // 4) Add feature and style to result list.Add(new MapInfoRecord(feature, style, layer)); } surface.Canvas.Restore(); // ReSharper restore AccessToDisposedClosure }); } if (list.Count == 0) { return(result); } list.Reverse(); var itemDrawnOnTop = list.First(); result.Feature = itemDrawnOnTop.Feature; result.Style = itemDrawnOnTop.Style; result.Layer = itemDrawnOnTop.Layer; result.MapInfoRecords = list; } catch (Exception exception) { Logger.Log(LogLevel.Error, "Unexpected error in skia renderer", exception); } return(result); }
public void Draw(SKCanvas canvas, IReadOnlyViewport viewport, IWidget widget, float layerOpacity) { canvas.RotateDegrees((float)viewport.Rotation, 0.0f, 0.0f); var TL = viewport.ScreenToWorld(canvas.LocalClipBounds.Left, canvas.LocalClipBounds.Top); var BR = viewport.ScreenToWorld(canvas.LocalClipBounds.Right, canvas.LocalClipBounds.Bottom); var width = viewport.Extent.Width; var height = viewport.Extent.Height; var usedLineOffset = lineOffset; uint usedLineOffsetInt = (uint)usedLineOffset; IEnumerable <SKPaint> usedPaints = paints; if (viewport.Resolution > 0.25) { usedLineOffset *= 10; // hide 10m layer usedLineOffsetInt *= 10; // hide 10m layer usedPaints = paintsL2; } if (viewport.Resolution > 3) { usedLineOffset *= 10; // hide 100m layer usedLineOffsetInt *= 10; // hide 100m layer usedPaints = paintsL3; } int lineCount100M = (int)(100 / usedLineOffset); //How many lines are 1000m int lineCount1000M = (int)(1000 / usedLineOffset); if (lineCount100M == 0) { lineCount100M = 9000; //will never be reached, if we only render 1k's } double screenWidthPerLine = canvas.LocalClipBounds.Width / (width / usedLineOffset); double screenHeightPerLine = canvas.LocalClipBounds.Height / (height / usedLineOffset); //World coordinates of first lineOffset line var first100mW = (TL.X + (usedLineOffset - TL.X % usedLineOffset)); var first100mH = (TL.Y + (usedLineOffset - TL.Y % usedLineOffset)); //Screen offset of first lineOffset line double offsetCW = ((first100mW - TL.X) / usedLineOffset) * screenWidthPerLine; double offsetCH = screenHeightPerLine + ((TL.Y - first100mH) / usedLineOffset) * screenHeightPerLine; //offset of next 1k int KOffsetW = (int)((first100mW % 1000) / usedLineOffset); if (KOffsetW < 0) { KOffsetW = 10 + KOffsetW; } int KOffsetH = (int)((first100mH % 1000) / usedLineOffset) - 1; if (lineCount1000M > 1) { KOffsetH = lineCount1000M - KOffsetH; } for (double curX = offsetCW; curX < canvas.LocalClipBounds.Right; curX += screenWidthPerLine) { var worldPos = viewport.ScreenToWorld(curX, 0).X; var Xgrid10KM = (uint)(worldPos / 10000) % 10; var Xgrid1KM = (uint)(worldPos / 1000) % 10; var Xgrid100m = (uint)(worldPos / 100) % 10; //var Xgrid10m = (uint)(worldPos / 10) % 10; string gridPosString; if (usedLineOffsetInt == 1000) { gridPosString = $"{Xgrid10KM}{Xgrid1KM}"; } else if (usedLineOffsetInt == 100) { gridPosString = $"{Xgrid10KM}{Xgrid1KM}{Xgrid100m}"; } else { gridPosString = $"{Xgrid10KM}{Xgrid1KM}{Xgrid100m}{(uint)(worldPos / 10) % 10}"; } SKPaint paint; if (KOffsetW >= 1000 && KOffsetW % 1000 == 0) { paint = usedPaints.ElementAt(0); } if (KOffsetW >= 100 && KOffsetW % 100 == 0) { paint = usedPaints.ElementAt(2); } else if (KOffsetW >= 10 && KOffsetW % 10 == 0) { paint = usedPaints.ElementAt(1); } else { paint = usedPaints.ElementAt(0); } if (KOffsetW == lineCount1000M) { KOffsetW = 0; } canvas.DrawLine((float)curX, 0, (float)curX, canvas.LocalClipBounds.Height, paint); using (var gtext = SKTextBlob.Create(gridPosString, markerFont)) canvas.DrawText(gtext, (float)(curX + screenWidthPerLine / 2) - gtext.Bounds.MidX, 20 + gtext.Bounds.MidY, paint100m); KOffsetW++; } for (double curH = offsetCH; curH < canvas.LocalClipBounds.Bottom; curH += screenHeightPerLine) { var worldPos = viewport.ScreenToWorld(0, curH).Y; var Xgrid10KM = (uint)(worldPos / 10000) % 10; var Xgrid1KM = (uint)(worldPos / 1000) % 10; var Xgrid100m = (uint)(worldPos / 100) % 10; //var Xgrid10m = (uint)(worldPos / 10) % 10; string gridPosString; if (usedLineOffsetInt == 1000) { gridPosString = $"{Xgrid10KM}{Xgrid1KM}"; } else if (usedLineOffsetInt == 100) { gridPosString = $"{Xgrid10KM}{Xgrid1KM}{Xgrid100m}"; } else { gridPosString = $"{Xgrid10KM}{Xgrid1KM}{Xgrid100m}{(uint)(worldPos / 10) % 10}"; } SKPaint paint; if (KOffsetH >= 100 && KOffsetH % 100 == 0) { paint = usedPaints.ElementAt(2); } else if (KOffsetH >= 10 && KOffsetH % 10 == 0) { paint = usedPaints.ElementAt(1); } else { paint = usedPaints.ElementAt(0); } if (KOffsetH == lineCount1000M) { KOffsetH = 0; } canvas.DrawLine(0, (float)curH, canvas.LocalClipBounds.Width, (float)curH, paint); using (var gtext = SKTextBlob.Create(gridPosString, markerFont)) canvas.DrawText(gtext, 0, (float)(curH + screenWidthPerLine / 2) - gtext.Bounds.MidY, paint100m); KOffsetH++; } }