/// <summary> /// Creates a node and either splits the objects recursively into sub-nodes, or stores them at the node depending on the heuristics. /// Tree is built top->down /// </summary> /// <param name="objList">Geometries to index</param> /// <param name="depth">Current depth of tree</param> /// <param name="heurdata">Heuristics data</param> public QuadTree(List <BoxObjects> objList, uint depth, Heuristic heurdata) { _depth = depth; _box = objList[0].Box; for (var i = 0; i < objList.Count; i++) { _box = _box.Join(objList[i].Box); } // test our build heuristic - if passes, make children if (depth < heurdata.Maxdepth && objList.Count > heurdata.Mintricnt && (objList.Count > heurdata.Tartricnt || ErrorMetric(_box) > heurdata.Minerror)) { var objBuckets = new List <BoxObjects> [2]; // buckets of geometries objBuckets[0] = new List <BoxObjects>(); objBuckets[1] = new List <BoxObjects>(); var useXAxis = _box.Width > _box.Height; // longest axis double geoAverage = 0; // geometric average - midpoint of ALL the objects // go through all bbox and calculate the average of the midpoints double fraction = 1.0f / objList.Count; for (var i = 0; i < objList.Count; i++) { var centroid = useXAxis ? objList[i].Box.Centroid.X : objList[i].Box.Centroid.Y; geoAverage += centroid * fraction; } // bucket bbox based on their midpoint's side of the geo average in the longest axis for (var i = 0; i < objList.Count; i++) { var centroid = useXAxis ? objList[i].Box.Centroid.X : objList[i].Box.Centroid.Y; objBuckets[geoAverage > centroid ? 1 : 0].Add(objList[i]); } //If objects couldn't be split, just store them at the leaf //TODO: Try splitting on another axis if (objBuckets[0].Count == 0 || objBuckets[1].Count == 0) { _child0 = null; _child1 = null; // copy object list _objList = objList; } else { // create new children using the buckets _child0 = new QuadTree(objBuckets[0], depth + 1, heurdata); _child1 = new QuadTree(objBuckets[1], depth + 1, heurdata); } } else { // otherwise the build heuristic failed, this is // set the first child to null (identifies a leaf) _child0 = null; _child1 = null; // copy object list _objList = objList; } }
public void Draw(SKCanvas canvas, IReadOnlyViewport viewport, IWidget widget, float layerOpacity) { var scaleBar = (ScaleBarWidget)widget; if (!scaleBar.CanProject()) { return; } // If this is the first time, we call this renderer, ... if (_paintScaleBar == null) { // ... than create the paints _paintScaleBar = CreateScaleBarPaint(SKPaintStyle.Fill); _paintScaleBarStroke = CreateScaleBarPaint(SKPaintStyle.Stroke); _paintScaleText = CreateTextPaint(SKPaintStyle.Fill); _paintScaleTextStroke = CreateTextPaint(SKPaintStyle.Stroke); } // Update paints with new values _paintScaleBar.Color = scaleBar.TextColor.ToSkia(layerOpacity); _paintScaleBar.StrokeWidth = scaleBar.StrokeWidth * scaleBar.Scale; _paintScaleBarStroke !.Color = scaleBar.Halo.ToSkia(layerOpacity); _paintScaleBarStroke.StrokeWidth = scaleBar.StrokeWidthHalo * scaleBar.Scale; _paintScaleText !.Color = scaleBar.TextColor.ToSkia(layerOpacity); _paintScaleText.StrokeWidth = scaleBar.StrokeWidth * scaleBar.Scale; _paintScaleText.Typeface = SKTypeface.FromFamilyName(scaleBar.Font?.FontFamily, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright); _paintScaleText.TextSize = (float)(scaleBar.Font?.Size ?? 10) * scaleBar.Scale; _paintScaleTextStroke !.Color = scaleBar.Halo.ToSkia(layerOpacity); _paintScaleTextStroke.StrokeWidth = scaleBar.StrokeWidthHalo / 2 * scaleBar.Scale; _paintScaleTextStroke.Typeface = SKTypeface.FromFamilyName(scaleBar.Font?.FontFamily, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright); _paintScaleTextStroke.TextSize = (float)(scaleBar.Font?.Size ?? 10) * scaleBar.Scale; float scaleBarLength1; string?scaleBarText1; float scaleBarLength2; string?scaleBarText2; (scaleBarLength1, scaleBarText1, scaleBarLength2, scaleBarText2) = scaleBar.GetScaleBarLengthAndText(viewport); // Calc height of scale bar var textSize = SKRect.Empty; // Do this, because height of text changes sometimes (e.g. from 2 m to 1 m) _paintScaleTextStroke.MeasureText("9999 m", ref textSize); var scaleBarHeight = textSize.Height + (scaleBar.TickLength + scaleBar.StrokeWidthHalo * 0.5f + scaleBar.TextMargin) * scaleBar.Scale; if (scaleBar.ScaleBarMode == ScaleBarMode.Both && scaleBar.SecondaryUnitConverter != null) { scaleBarHeight *= 2; } else { scaleBarHeight += scaleBar.StrokeWidthHalo * 0.5f * scaleBar.Scale; } scaleBar.Height = scaleBarHeight; // Draw lines // Get lines for scale bar var points = scaleBar.GetScaleBarLinePositions(viewport, scaleBarLength1, scaleBarLength2, scaleBar.StrokeWidthHalo); // Draw outline of scale bar for (var i = 0; i < points.Count; i += 2) { canvas.DrawLine((float)points[i].X, (float)points[i].Y, (float)points[i + 1].X, (float)points[i + 1].Y, _paintScaleBarStroke); } // Draw scale bar for (var i = 0; i < points.Count; i += 2) { canvas.DrawLine((float)points[i].X, (float)points[i].Y, (float)points[i + 1].X, (float)points[i + 1].Y, _paintScaleBar); } if (!points.Any()) { throw new NotImplementedException($"A {nameof(ScaleBarWidget)} can not be drawn without line positions"); } var envelop = new MRect(points.Select(p => p.MRect)); envelop = envelop.Grow(scaleBar.StrokeWidthHalo * 0.5f * scaleBar.Scale); // Draw text // Calc text height var textSize1 = SKRect.Empty; var textSize2 = SKRect.Empty; scaleBarText1 ??= string.Empty; _paintScaleTextStroke.MeasureText(scaleBarText1, ref textSize1); if (scaleBar.ScaleBarMode == ScaleBarMode.Both && scaleBar.SecondaryUnitConverter != null) { // If there is SecondaryUnitConverter we need to calculate the size before passing it into GetScaleBarTextPositions scaleBarText2 ??= string.Empty; _paintScaleTextStroke.MeasureText(scaleBarText2, ref textSize2); } var(posX1, posY1, posX2, posY2) = scaleBar.GetScaleBarTextPositions(viewport, textSize.ToMRect(), textSize1.ToMRect(), textSize2.ToMRect(), scaleBar.StrokeWidthHalo); // Now draw text canvas.DrawText(scaleBarText1, posX1, posY1 - textSize1.Top, _paintScaleTextStroke); canvas.DrawText(scaleBarText1, posX1, posY1 - textSize1.Top, _paintScaleText); envelop = envelop?.Join(new MRect(posX1, posY1, posX1 + textSize1.Width, posY1 + textSize1.Height)); if (scaleBar.ScaleBarMode == ScaleBarMode.Both && scaleBar.SecondaryUnitConverter != null) { // Now draw second text canvas.DrawText(scaleBarText2, posX2, posY2 - textSize2.Top, _paintScaleTextStroke); canvas.DrawText(scaleBarText2, posX2, posY2 - textSize2.Top, _paintScaleText); envelop = envelop?.Join(new MRect(posX2, posY2, posX2 + textSize2.Width, posY2 + textSize2.Height)); } scaleBar.Envelope = envelop; if (scaleBar.ShowEnvelop && envelop != null) { // Draw a rect around the scale bar for testing var tempPaint = _paintScaleTextStroke; canvas.DrawRect(new SKRect((float)envelop.MinX, (float)envelop.MinY, (float)envelop.MaxX, (float)envelop.MaxY), tempPaint); } }