Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions LiteDB.Benchmarks/Benchmarks/Spatial/SpatialQueryBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BenchmarkDotNet.Attributes;
using LiteDB.Benchmarks.Models.Spatial;
using LiteDB.Spatial;
using SpatialApi = LiteDB.Spatial.Spatial;

namespace LiteDB.Benchmarks.Benchmarks.Spatial
{
[BenchmarkCategory(Constants.Categories.QUERIES)]
public class SpatialQueryBenchmarks : BenchmarkBase
{
private ILiteCollection<SpatialDocument> _collection = null!;
private GeoPoint _center = null!;
private GeoPolygon _searchArea = null!;
private double _radiusMeters;

[GlobalSetup]
public void GlobalSetup()
{
File.Delete(DatabasePath);

DatabaseInstance = new LiteDatabase(ConnectionString());
_collection = DatabaseInstance.GetCollection<SpatialDocument>("places");

SpatialApi.EnsurePointIndex(_collection, x => x.Location);
SpatialApi.EnsureShapeIndex(_collection, x => x.Region);
SpatialApi.EnsureShapeIndex(_collection, x => x.Route);

var documents = SpatialDocumentGenerator.Generate(DatasetSize);
_collection.Insert(documents);

DatabaseInstance.Checkpoint();

_center = new GeoPoint(0, 0);
_radiusMeters = 25_000;
_searchArea = SpatialDocumentGenerator.BuildSearchPolygon(0, 0, 0.1);
}

[Benchmark(Baseline = true)]
public List<SpatialDocument> NearQuery()
{
return SpatialApi.Near(_collection, x => x.Location, _center, _radiusMeters).ToList();
}

[Benchmark]
public List<SpatialDocument> BoundingBoxQuery()
{
return SpatialApi.WithinBoundingBox(_collection, x => x.Location, -0.2, -0.2, 0.2, 0.2).ToList();
}

[Benchmark]
public List<SpatialDocument> PolygonContainmentQuery()
{
return SpatialApi.Within(_collection, x => x.Region, _searchArea).ToList();
}

[Benchmark]
public List<SpatialDocument> RouteIntersectionQuery()
{
return SpatialApi.Intersects(_collection, x => x.Route, _searchArea).ToList();
}

[GlobalCleanup]
public void GlobalCleanup()
{
DatabaseInstance?.Checkpoint();
DatabaseInstance?.Dispose();
DatabaseInstance = null;

File.Delete(DatabasePath);
}
}
}
33 changes: 33 additions & 0 deletions LiteDB.Benchmarks/Models/Spatial/SpatialDocument.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using LiteDB.Spatial;

namespace LiteDB.Benchmarks.Models.Spatial
{
public class SpatialDocument
{
public int Id { get; set; }

public string Name { get; set; } = string.Empty;

public GeoPoint Location { get; set; } = new GeoPoint(0, 0);

public GeoPolygon Region { get; set; } = new GeoPolygon(new[]
{
new GeoPoint(0, 0),
new GeoPoint(0, 0.001),
new GeoPoint(0.001, 0.001),
new GeoPoint(0.001, 0),
new GeoPoint(0, 0)
});

public GeoLineString Route { get; set; } = new GeoLineString(new[]
{
new GeoPoint(0, 0),
new GeoPoint(0.001, 0.001)
});

internal long _gh { get; set; }

internal double[] _mbb { get; set; } = Array.Empty<double>();
}
}
104 changes: 104 additions & 0 deletions LiteDB.Benchmarks/Models/Spatial/SpatialDocumentGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using LiteDB.Spatial;

namespace LiteDB.Benchmarks.Models.Spatial
{
internal static class SpatialDocumentGenerator
{
public static List<SpatialDocument> Generate(int count)
{
var random = new Random(1337);
var documents = new List<SpatialDocument>(count);

for (var i = 0; i < count; i++)
{
var lat = random.NextDouble() * 0.8 - 0.4;
var lon = random.NextDouble() * 0.8 - 0.4;
var location = new GeoPoint(lat, lon);

var region = BuildSquare(location, random.NextDouble() * 0.05 + 0.01);
var route = BuildRoute(location, random);

documents.Add(new SpatialDocument
{
Id = i + 1,
Name = $"Place #{i + 1}",
Location = location,
Region = region,
Route = route
});
}

return documents;
}

public static GeoPolygon BuildSearchPolygon(double centerLat, double centerLon, double radiusDegrees)
{
var center = new GeoPoint(centerLat, centerLon);
return BuildSquare(center, radiusDegrees);
}

private static GeoPolygon BuildSquare(GeoPoint center, double halfExtent)
{
var minLat = ClampLatitude(center.Lat - halfExtent);
var maxLat = ClampLatitude(center.Lat + halfExtent);
var minLon = NormalizeLongitude(center.Lon - halfExtent);
var maxLon = NormalizeLongitude(center.Lon + halfExtent);

var points = new List<GeoPoint>
{
new GeoPoint(maxLat, minLon),
new GeoPoint(maxLat, maxLon),
new GeoPoint(minLat, maxLon),
new GeoPoint(minLat, minLon),
new GeoPoint(maxLat, minLon)
};

return new GeoPolygon(points);
}

private static GeoLineString BuildRoute(GeoPoint start, Random random)
{
var midLat = start.Lat + random.NextDouble() * 0.1 - 0.05;
var midLon = start.Lon + random.NextDouble() * 0.1 - 0.05;
var endLat = start.Lat + random.NextDouble() * 0.2 - 0.1;
var endLon = start.Lon + random.NextDouble() * 0.2 - 0.1;

var points = new List<GeoPoint>
{
start,
new GeoPoint(midLat, midLon),
new GeoPoint(endLat, endLon)
};

return new GeoLineString(points);
}

private static double ClampLatitude(double latitude)
{
return Math.Max(-90d, Math.Min(90d, latitude));
}

private static double NormalizeLongitude(double lon)
{
if (double.IsNaN(lon))
{
return lon;
}

var result = lon % 360d;

if (result <= -180d)
{
result += 360d;
}
else if (result > 180d)
{
result -= 360d;
}

return result;
}
}
}
Loading
Loading