public static MultiPolycurve CreateMultiPolycurve([NotNull] IEnumerable <IPath> paths,
                                                          double tolerance          = 0,
                                                          [CanBeNull] IEnvelope aoi = null)
        {
            var result = new MultiPolycurve(new List <Linestring>());

            IEnvelope envelope = new EnvelopeClass();

            foreach (IPath path in paths)
            {
                if (aoi != null && !aoi.IsEmpty)
                {
                    path.QueryEnvelope(envelope);

                    if (GeometryUtils.Disjoint(aoi, envelope, tolerance))
                    {
                        continue;
                    }
                }

                // linestring without spatial index
                Linestring linestring = CreateLinestring(path, int.MaxValue);

                result.AddLinestring(linestring);
            }

            return(result);
        }
        public void CanWriteAndReadMultiLinestring()
        {
            var points1 = new List <Pnt3D>
            {
                new Pnt3D(0, 0, 9),
                new Pnt3D(0, 100, 8),
                new Pnt3D(100, 100, 5)
            };

            var points2 = new List <Pnt3D>();

            points2.Add(new Pnt3D(140, -10, 0));
            points2.Add(new Pnt3D(140, 30, 23));
            points2.Add(new Pnt3D(300, 30, 56));
            points2.Add(new Pnt3D(300, -10, 0));

            MultiPolycurve polycurve =
                new MultiPolycurve(new[] { new Linestring(points1), new Linestring(points2) });

            WkbGeomWriter writer = new WkbGeomWriter();

            byte[] bytes = writer.WriteMultiLinestring(polycurve);

            WkbGeomReader  reader       = new WkbGeomReader();
            MultiPolycurve deserialized = reader.ReadMultiPolycurve(new MemoryStream(bytes));

            Assert.IsTrue(deserialized.Equals(polycurve));
        }
Exemple #3
0
 public byte[] WriteMultipolygon(MultiPolycurve multipolygon,
                                 Ordinates ordinates = Ordinates.Xyz)
 {
     return(WriteMultipolygon(
                GeomTopoOpUtils.GetConnectedComponents(multipolygon, double.Epsilon).ToList(),
                ordinates));
 }
        private static IList <RingGroup> CutRingGroupPlanar(
            [NotNull] RingGroup ringGroup,
            [NotNull] IPolyline cutLine,
            double tolerance,
            ChangeAlongZSource zSource,
            double zTolerance)
        {
            cutLine = GeometryFactory.Clone(cutLine);

            if (GeometryUtils.IsZAware(cutLine) &&
                zSource != ChangeAlongZSource.Target)
            {
                ((IZAware)cutLine).DropZs();
            }

            Plane3D plane = null;

            if (zSource == ChangeAlongZSource.SourcePlane)
            {
                plane = ChangeAlongZUtils.GetSourcePlane(
                    ringGroup.ExteriorRing.GetPoints().ToList(), zTolerance);
            }

            GeometryUtils.Simplify(cutLine, true, true);

            MultiPolycurve cutLinestrings = new MultiPolycurve(
                GeometryUtils.GetPaths(cutLine).Select(
                    cutPath => GeometryConversionUtils.CreateLinestring(cutPath)));

            IList <RingGroup> resultGroups =
                GeomTopoOpUtils.CutPlanar(ringGroup, cutLinestrings, tolerance);

            foreach (RingGroup resultPoly in resultGroups)
            {
                resultPoly.Id = ringGroup.Id;

                if (plane != null)
                {
                    resultPoly.AssignUndefinedZs(plane);
                }
                else
                {
                    resultPoly.InterpolateUndefinedZs();
                }
            }

            Marshal.ReleaseComObject(cutLine);

            if (resultGroups.Count == 0)
            {
                // Return uncut original
                resultGroups.Add(ringGroup);
            }

            return(resultGroups);
        }
        public void CanReadWriteSinglePartPolylineXy()
        {
            var points = new WKSPointZ[4];

            points[0] = new WKSPointZ {
                X = 2600000, Y = 1200000, Z = double.NaN
            };
            points[1] = new WKSPointZ {
                X = 2600030, Y = 1200020, Z = double.NaN
            };
            points[2] = new WKSPointZ {
                X = 2600020, Y = 1200030, Z = double.NaN
            };
            points[3] = new WKSPointZ {
                X = 2600040, Y = 1200040, Z = double.NaN
            };

            IPolyline polyline = GeometryFactory.CreatePolyline(points, null);

            GeometryUtils.MakeNonZAware(polyline);

            GeometryUtils.Simplify(polyline);

            WkbGeometryWriter writer = new WkbGeometryWriter();

            byte[] wkb = writer.WritePolyline(polyline);

            // ArcObjects
            byte[] arcObjectsWkb = GeometryUtils.ToWkb(polyline);
            Assert.AreEqual(wkb, arcObjectsWkb);

            // Wkx
            byte[] wkx = ToChristianSchwarzWkb(ToWkxLineString(points, Ordinates.Xy));
            Assert.AreEqual(wkx, wkb);

            // Bonus test: Geom
            WkbGeomWriter  geomWriter    = new WkbGeomWriter();
            MultiPolycurve multiPlycurve = GeometryConversionUtils.CreateMultiPolycurve(polyline);

            byte[] wkbGeom = geomWriter.WriteMultiLinestring(multiPlycurve, Ordinates.Xy);
            Assert.AreEqual(wkb, wkbGeom);

            WkbGeometryReader reader = new WkbGeometryReader();

            IPolyline restored = reader.ReadPolyline(new MemoryStream(wkb));

            Assert.IsTrue(GeometryUtils.AreEqual(polyline, restored));

            // Geom
            WkbGeomReader geomReader = new WkbGeomReader();

            Assert.IsTrue(
                multiPlycurve.Equals(geomReader.ReadMultiPolycurve(new MemoryStream(wkbGeom))));
        }
        private static IList <IGeometry> TryCutXY(
            IPolygon inputPolygon,
            IPolyline cutPolyline,
            ChangeAlongZSource zSource)
        {
            // TODO:
            // In order to avoid the arbitrary grouping of multipart polygons, try to apply left/right logic
            // provided by GeomTopoOpUtils

            double tolerance  = GeometryUtils.GetXyTolerance(inputPolygon);
            double zTolerance = GeometryUtils.GetZTolerance(inputPolygon);

            MultiPolycurve inputMultipoly =
                GeometryConversionUtils.CreateMultiPolycurve(inputPolygon);

            var cutLine = GeometryFactory.Clone(cutPolyline);

            if (GeometryUtils.IsZAware(cutLine) &&
                zSource != ChangeAlongZSource.Target)
            {
                ((IZAware)cutLine).DropZs();
            }

            Plane3D plane = null;

            if (zSource == ChangeAlongZSource.SourcePlane)
            {
                plane = ChangeAlongZUtils.GetSourcePlane(
                    inputMultipoly.GetPoints().ToList(), zTolerance);
            }

            GeometryUtils.Simplify(cutLine, true, true);

            MultiPolycurve cutLinestrings = GeometryConversionUtils.CreateMultiPolycurve(cutLine);

            bool isMultipart = GeometryUtils.GetExteriorRingCount(inputPolygon) > 1;

            IList <MultiLinestring> resultGeoms =
                GeomTopoOpUtils.CutXY(inputMultipoly, cutLinestrings, tolerance, !isMultipart);

            var result = new List <IGeometry>();

            foreach (MultiLinestring resultPoly in resultGeoms)
            {
                if (plane != null)
                {
                    resultPoly.AssignUndefinedZs(plane);
                }
                else
                {
                    resultPoly.InterpolateUndefinedZs();
                }

                result.Add(GeometryConversionUtils.CreatePolygon(inputPolygon,
                                                                 resultPoly.GetLinestrings()));
            }

            Marshal.ReleaseComObject(cutLine);

            return(result.Count == 0 ? null : result);
        }
        public void CanNavigateRings()
        {
            var multilinestring = new MultiPolycurve(
                new[]
            {
                new Linestring(new[]
                {
                    new Pnt3D(0, 0, 9),
                    new Pnt3D(0, 100, 9),
                    new Pnt3D(100, 100, 9),
                    new Pnt3D(100, 0, 9),
                    new Pnt3D(0, 0, 9)
                }),
                new Linestring(new[]
                {
                    // interior ring
                    new Pnt3D(40, 40, 123),
                    new Pnt3D(60, 40, 123),
                    new Pnt3D(60, 60, 12),
                    new Pnt3D(40, 60, 12),
                    new Pnt3D(40, 40, 123)
                })
            });

            Assert.True(multilinestring.IsClosed);

            int partIdx;
            int localSegmentIdx = multilinestring.GetLocalSegmentIndex(5, out partIdx);

            Assert.AreEqual(6, multilinestring.GetSegmentStartPointIndex(5));

            Assert.AreEqual(1, partIdx);
            Assert.AreEqual(1, localSegmentIdx);

            Line3D previousSegment = multilinestring.PreviousSegment(5);

            Assert.AreEqual(multilinestring.GetSegment(4), previousSegment);
            Assert.AreEqual(multilinestring.GetSegment(1, 0), previousSegment);

            int?previousSegmentIdx = multilinestring.PreviousSegmentIndex(5);

            Assert.NotNull(previousSegmentIdx);
            Assert.AreEqual(4, previousSegmentIdx);

            previousSegmentIdx = multilinestring.PreviousSegmentIndex(previousSegmentIdx.Value);
            Assert.NotNull(previousSegmentIdx);
            Assert.AreEqual(7, previousSegmentIdx);

            int?nextSegmentIndex = multilinestring.NextSegmentIndex(previousSegmentIdx.Value);

            Assert.NotNull(nextSegmentIndex);
            Assert.AreEqual(4, nextSegmentIndex);

            Line3D nextSegment = multilinestring.NextSegment(previousSegmentIdx.Value);

            Assert.AreEqual(multilinestring.GetSegment(4), nextSegment);
            Assert.AreEqual(multilinestring.GetSegment(1, 0), nextSegment);
            Assert.AreEqual(5, multilinestring.GetSegmentStartPointIndex(4));

            // With the exterior ring:
            localSegmentIdx = multilinestring.GetLocalSegmentIndex(1, out partIdx);

            Assert.AreEqual(0, partIdx);
            Assert.AreEqual(1, localSegmentIdx);

            previousSegment = multilinestring.PreviousSegment(1);
            Assert.AreEqual(multilinestring.GetSegment(0), previousSegment);
            Assert.AreEqual(multilinestring.GetSegment(0, 0), previousSegment);

            previousSegmentIdx = multilinestring.PreviousSegmentIndex(1);
            Assert.NotNull(previousSegmentIdx);
            Assert.AreEqual(0, previousSegmentIdx);

            previousSegmentIdx = multilinestring.PreviousSegmentIndex(previousSegmentIdx.Value);
            Assert.NotNull(previousSegmentIdx);
            Assert.AreEqual(3, previousSegmentIdx);

            nextSegmentIndex = multilinestring.NextSegmentIndex(previousSegmentIdx.Value);
            Assert.NotNull(nextSegmentIndex);
            Assert.AreEqual(0, nextSegmentIndex);

            nextSegment = multilinestring.NextSegment(previousSegmentIdx.Value);

            Assert.AreEqual(multilinestring.GetSegment(0), nextSegment);
            Assert.AreEqual(multilinestring.GetSegment(0, 0), nextSegment);
        }
        public void CanUseBasicProperties()
        {
            var multilinestring = new MultiPolycurve(
                new[]
            {
                new Linestring(new[]
                {
                    new Pnt3D(-5, -15, -25),
                    new Pnt3D(0, 0, 0)
                }),
                new Linestring(new[]
                {
                    new Pnt3D(44, 45, 123),
                    new Pnt3D(33, 33, 123),
                    new Pnt3D(22, 22, 12)
                })
            });

            Assert.AreEqual(2, multilinestring.Count);
            Assert.AreEqual(multilinestring.Count, multilinestring.GetLinestrings().Count());

            Assert.AreEqual(3, multilinestring.SegmentCount);
            Assert.AreEqual(multilinestring.SegmentCount, multilinestring.Count());

            Assert.AreEqual(5, multilinestring.PointCount);
            Assert.AreEqual(multilinestring.PointCount, multilinestring.GetPoints().Count());

            Assert.False(multilinestring.IsClosed);
            Assert.False(multilinestring.IsEmpty);

            int partIndex;

            Assert.True(multilinestring.IsFirstPointInPart(0, out partIndex));
            Assert.AreEqual(0, partIndex);
            Assert.True(multilinestring.IsFirstPointInPart(2, out partIndex));
            Assert.AreEqual(1, partIndex);

            Assert.True(multilinestring.IsFirstSegmentInPart(0));
            Assert.True(multilinestring.IsFirstSegmentInPart(1));
            Assert.True(multilinestring.IsLastPointInPart(1, out partIndex));
            Assert.AreEqual(0, partIndex);
            Assert.True(multilinestring.IsLastPointInPart(4, out partIndex));
            Assert.AreEqual(1, partIndex);

            Assert.True(multilinestring.IsLastSegmentInPart(0));
            Assert.True(multilinestring.IsLastSegmentInPart(2));

            const double xMin = -5;

            Assert.AreEqual(xMin, multilinestring.GetLinestring(0).XMin);
            Assert.AreEqual(xMin, multilinestring.XMin);

            const double xMax = 44;

            Assert.AreEqual(xMax, multilinestring.GetLinestring(1).XMax);
            Assert.AreEqual(xMax, multilinestring.XMax);

            const double yMin = -15;

            Assert.AreEqual(yMin, multilinestring.GetLinestring(0).YMin);
            Assert.AreEqual(yMin, multilinestring.YMin);

            const double yMax = 45;

            Assert.AreEqual(yMax, multilinestring.GetLinestring(1).YMax);
            Assert.AreEqual(yMax, multilinestring.YMax);

            int partIdx;
            int localSegmentIndex = multilinestring.GetLocalSegmentIndex(2, out partIdx);

            Assert.AreEqual(1, partIdx);
            Assert.AreEqual(1, localSegmentIndex);
            Assert.AreEqual(2, multilinestring.GetGlobalSegmentIndex(partIdx, localSegmentIndex));

            localSegmentIndex = multilinestring.GetLocalSegmentIndex(0, out partIdx);
            Assert.AreEqual(0, partIdx);
            Assert.AreEqual(0, localSegmentIndex);
            Assert.AreEqual(0, multilinestring.GetGlobalSegmentIndex(partIdx, localSegmentIndex));

            Assert.AreEqual(multilinestring.GetLinestrings().Sum(l => l.GetLength2D()),
                            multilinestring.GetLength2D());
        }
        public void CanReadWriteMultipartPolygonXyz()
        {
            ISpatialReference sr = SpatialReferenceUtils.CreateSpatialReference(
                WellKnownHorizontalCS.LV95, WellKnownVerticalCS.LHN95);

            IPolygon outerRing =
                GeometryFactory.CreatePolygon(2600000, 1200000, 2601000, 1201000, 432);

            outerRing.SpatialReference = sr;
            IPolygon innerRing =
                GeometryFactory.CreatePolygon(2600100, 1200100, 2600200, 1200200, 421);

            innerRing.SpatialReference = sr;

            IPolygon polyWithHole = (IPolygon)IntersectionUtils.Difference(outerRing, innerRing);

            IPolygon poly2 = GeometryFactory.CreatePolygon(2610000, 1200000, 2611000, 1201000, 543);

            poly2.SpatialReference = sr;

            IPolygon multiPolygon = (IPolygon)GeometryUtils.Union(polyWithHole, poly2);

            Assert.IsTrue(GeometryUtils.IsZAware(multiPolygon));

            GeometryUtils.Simplify(multiPolygon);

            WkbGeometryWriter writer = new WkbGeometryWriter();

            byte[] wkb = writer.WritePolygon(multiPolygon);

            // Wkx
            var ordinates                        = Ordinates.Xyz;
            IList <IGeometry> parts              = GeometryUtils.GetParts(multiPolygon).ToList();
            IGeometry         exteriorRing       = parts[0];
            IGeometry         interiorRing       = parts[1];
            IGeometry         secondExteriorRing = parts[2];

            LinearRing wkxOuterRing = ToWkxLinearRing(exteriorRing, ordinates);
            LinearRing wkxInnerRing = ToWkxLinearRing(interiorRing, ordinates);

            Polygon wkxPolygon1 = ToWkxPolygon(wkxOuterRing, new[] { wkxInnerRing });
            Polygon wkxPolygon2 = ToWkxPolygon(ToWkxLinearRing(secondExteriorRing, ordinates));

            MultiPolygon wkxMultiPolygon = new MultiPolygon(new[] { wkxPolygon1, wkxPolygon2 });

            byte[] wkx = ToChristianSchwarzWkb(wkxMultiPolygon);
            Assert.AreEqual(wkx, wkb);

            // Bonus test: Geom
            WkbGeomWriter  geomWriter = new WkbGeomWriter();
            MultiPolycurve multiPly   = GeometryConversionUtils.CreateMultiPolycurve(multiPolygon);

            byte[] wkbGeom = geomWriter.WriteMultipolygon(multiPly, ordinates);
            Assert.AreEqual(wkb, wkbGeom);

            WkbGeometryReader reader = new WkbGeometryReader();

            IPolygon restored = reader.ReadPolygon(new MemoryStream(wkb));

            Assert.IsTrue(GeometryUtils.AreEqual(multiPolygon, restored));

            // Geom:
            WkbGeomReader     geomReader       = new WkbGeomReader();
            IList <RingGroup> readMultiPolygon =
                geomReader.ReadMultiPolygon(new MemoryStream(wkbGeom));
            var readMultiPolycurve = new MultiPolycurve(
                readMultiPolygon.SelectMany(g => g.GetLinestrings()));

            Assert.IsTrue(multiPly.Equals(readMultiPolycurve));

            // As multipatch
            IMultiPatch multipatch = GeometryFactory.CreateMultiPatch(multiPolygon);

            wkb = writer.WriteMultipatch(multipatch);

            IMultiPatch readMultipatch = (IMultiPatch)reader.ReadGeometry(new MemoryStream(wkb));

            // TODO: Implement AreEqual for multipatch that is more permissive regarding First/Outer ring type
            AssertEqual(multipatch, readMultipatch);
        }
        public void CanReadWriteMultiPartPolylineXyz()
        {
            // The spatial reference is important to avoid small differences in
            // coordinates (snap to spatial reference!)
            ISpatialReference sr = SpatialReferenceUtils.CreateSpatialReference(
                WellKnownHorizontalCS.LV95, WellKnownVerticalCS.LHN95);

            var points1 = new WKSPointZ[4];

            points1[0] = new WKSPointZ {
                X = 2600000, Y = 1200000, Z = 456
            };
            points1[1] = new WKSPointZ {
                X = 2600030, Y = 1200020, Z = 457
            };
            points1[2] = new WKSPointZ {
                X = 2600020, Y = 1200030, Z = 459
            };
            points1[3] = new WKSPointZ {
                X = 2600040, Y = 1200040, Z = 416
            };

            IPolyline polyline1 = GeometryFactory.CreatePolyline(points1, sr);

            var points2 = new WKSPointZ[4];

            points2[0] = new WKSPointZ {
                X = 2610000, Y = 1200000, Z = 656
            };
            points2[1] = new WKSPointZ {
                X = 2610030, Y = 1200020, Z = 657
            };
            points2[2] = new WKSPointZ {
                X = 2610020, Y = 1200030, Z = 659
            };
            points2[3] = new WKSPointZ {
                X = 2610040, Y = 1200040, Z = 616
            };

            IPolyline polyline2 = GeometryFactory.CreatePolyline(points2, sr);

            IPolyline polyline = (IPolyline)GeometryUtils.Union(polyline1, polyline2);

            GeometryUtils.Simplify(polyline);

            WkbGeometryWriter writer = new WkbGeometryWriter();

            byte[] wkb = writer.WritePolyline(polyline);

            // Wkx
            var             ordinates       = Ordinates.Xyz;
            MultiLineString multiLineString = new MultiLineString(
                new List <LineString>
            {
                ToWkxLineString(points1, ordinates), ToWkxLineString(points2, ordinates)
            });

            byte[] wkx = ToChristianSchwarzWkb(multiLineString);
            Assert.AreEqual(wkx, wkb);

            // Bonus test: Geom
            WkbGeomWriter  geomWriter    = new WkbGeomWriter();
            MultiPolycurve multiPlycurve = GeometryConversionUtils.CreateMultiPolycurve(polyline);

            byte[] wkbGeom = geomWriter.WriteMultiLinestring(multiPlycurve, ordinates);
            Assert.AreEqual(wkb, wkbGeom);

            WkbGeometryReader reader = new WkbGeometryReader();

            IPolyline restored = reader.ReadPolyline(new MemoryStream(wkb));

            Assert.IsTrue(GeometryUtils.AreEqual(polyline, restored));

            // Geom:
            WkbGeomReader geomReader = new WkbGeomReader();

            Assert.IsTrue(
                multiPlycurve.Equals(geomReader.ReadMultiPolycurve(new MemoryStream(wkbGeom))));
        }
Exemple #11
0
        public void CompareSpatialIndexPerformance()
        {
            const int pointCount = 100000;

            Linestring linestring1 =
                new Linestring(CreateRandomPoints(pointCount, 2600000, 1200000, 400));

            Linestring linestring2 =
                new Linestring(CreateRandomPoints(pointCount, 2600000, 1200000, 400));

            Stopwatch watch;
            int       foundCountBoxtree;
            int       foundSpatialHash;
            int       envelopeIntersections;
            double    tolerance = 0.01;

            MemoryUsageInfo memUsageBox = new MemoryUsageInfo();

            watch = Stopwatch.StartNew();

            linestring2.SpatialIndex =
                BoxTreeSearcher <int> .CreateSpatialSearcher(linestring2);

            watch.Stop();

            Console.WriteLine(
                $"Linestring ({pointCount}) - Box tree created: {watch.ElapsedMilliseconds}ms. PB: {memUsageBox.Refresh().PrivateBytesDelta:N0}");

            watch                 = Stopwatch.StartNew();
            foundCountBoxtree     = 0;
            envelopeIntersections = 0;
            foreach (Line3D line3D in linestring1)
            {
                int foundInEnv = linestring2.FindSegments(
                    line3D.XMin, line3D.YMin, line3D.XMax,
                    line3D.YMax, tolerance).Count();

                if (foundInEnv > 0)
                {
                    envelopeIntersections++;
                }

                foundCountBoxtree += foundInEnv;
            }

            watch.Stop();

            Console.WriteLine(
                $"Linestring ({pointCount}) - Box tree ({foundCountBoxtree} segments found): {watch.ElapsedMilliseconds}ms");

            MemoryUsageInfo memUsageHash = new MemoryUsageInfo();

            watch = Stopwatch.StartNew();

            linestring2.SpatialIndex = CreateSpatialHash(linestring2);

            watch.Stop();
            Console.WriteLine(
                $"Linestring ({pointCount}) - spatial hash created: {watch.ElapsedMilliseconds}ms. PB: {memUsageHash.Refresh().PrivateBytesDelta:N0}");

            watch                 = Stopwatch.StartNew();
            foundSpatialHash      = 0;
            envelopeIntersections = 0;
            foreach (Line3D line3D in linestring1)
            {
                int foundInEnv = linestring2.FindSegments(
                    line3D.XMin, line3D.YMin, line3D.XMax,
                    line3D.YMax, tolerance).Count();

                if (foundInEnv > 0)
                {
                    envelopeIntersections++;
                }

                foundSpatialHash += foundInEnv;
            }

            watch.Stop();

            Console.WriteLine(
                $"Linestring ({pointCount}) - Spatial hash ({foundSpatialHash} segments found): {watch.ElapsedMilliseconds}ms");

            Assert.AreEqual(foundCountBoxtree, foundSpatialHash);

            MultiPolycurve poly2 = new MultiPolycurve(new List <Linestring> {
                linestring2
            });

            memUsageHash.Refresh();
            watch = Stopwatch.StartNew();

            poly2.SpatialIndex =
                SpatialHashSearcher <SegmentIndex> .CreateSpatialSearcher(poly2);

            watch.Stop();

            Console.WriteLine(
                $"Linestring ({pointCount}) - spatial hash (using multi-part index) created: {watch.ElapsedMilliseconds}ms. PB: {memUsageHash.Refresh().PrivateBytesDelta:N0}");

            watch                 = Stopwatch.StartNew();
            foundSpatialHash      = 0;
            envelopeIntersections = 0;
            foreach (Line3D line3D in linestring1)
            {
                int foundInEnv = poly2.FindSegments(
                    line3D.XMin, line3D.YMin, line3D.XMax,
                    line3D.YMax, tolerance).Count();

                if (foundInEnv > 0)
                {
                    envelopeIntersections++;
                }

                foundSpatialHash += foundInEnv;
            }

            watch.Stop();

            Console.WriteLine(
                $"Linestring ({pointCount}) - Spatial hash on poly ({foundSpatialHash} segments found): {watch.ElapsedMilliseconds}ms");
            Assert.AreEqual(foundCountBoxtree, foundSpatialHash);
        }