Skip to content
This repository has been archived by the owner on Oct 31, 2020. It is now read-only.

Spatial #6

Closed
bricelam opened this issue Apr 14, 2018 · 15 comments
Closed

Spatial #6

bricelam opened this issue Apr 14, 2018 · 15 comments

Comments

@bricelam
Copy link
Owner

bricelam commented Apr 14, 2018

Dependencies

Translations

Done .NET SQL
✔️ geometry.Area Area(geometry)
✔️ geometry.AsBinary() AsBinary(geometry)
✔️ geometry.AsText() AsText(geometry)
✔️ geometry.Boundary Boundary(geometry)
✔️ geometry.Buffer(distance) Buffer(geometry, distance)
✔️ geometry.Buffer(distance, segments) Buffer(geometry, distance, segments)
✔️ geometry.Centeroid Centroid(geometry)
✔️ geometry1.Contains(geometry2) Contains(geometry1, geometry2)
✔️ geometry.ConvexHull() ConvexHull(geometry)
✔️ geometryCollection.Count NumGeometries(geometryCollection)
✔️ lineString.Count NumPoints(lineString)
✔️ geometry1.CoveredBy(geometry2) CoveredBy(geometry1, geometry2)
✔️ geometry1.Covers(geometry2) Covers(geometry1, geometry2)
✔️ geometry1.Crosses(geometry2) Crosses(geometry1, geometry2)
✔️ geometry1.Difference(geometry2) Difference(geometry1, geometry2)
✔️ geometry.Dimension Dimension(geometry)
✔️ geometry1.Disjoint(geometry2) Disjoint(geometry1, geometry2)
✔️ geometry1.Distance(geometry2) Distance(geometry1, geometry2)
✔️ curve.EndPoint EndPoint(curve)
✔️ geometry.Envelope Envelope(geometry)
✔️ geometry1.EqualsTopologically(geometry2) Equals(geometry1, geometry2)
✔️ polygon.ExteriorRing ExteriorRing(polygon)
✔️ geometry.GeometryType CASE GeometryType(geometry) ...
✔️ geometry.GetGeometryN(n) GeometryN(geometry, n + 1)
✔️ polygon.GetInteriorRingN(n) InteriorRingN(polygon, n + 1)
✔️ lineString.GetPointN(n) PointN(lineString, n + 1)
✔️ geometry.InteriorPoint PointOnSurface(geometry)
✔️ geometry1.Intersection(geometry2) Intersection(geometry1, geometry2)
✔️ geometry1.Intersects(geometry2) Intersects(geometry1, geometry2)
✔️ curve.IsClosed IsClosed(curve)
✔️ geometry.IsEmpty IsEmpty(geometry)
✔️ curve.IsRing IsRing(curve)
✔️ geometry.IsSimple IsSimple(geometry)
✔️ geometry.IsValid IsValid(geometry)
✔️ geometry1.IsWithinDistance(geometry2, distance) Distance(geometry1, geometry2) <= distance
✔️ geometryCollection[n] GeometryN(geometryCollection, n + 1)
✔️ geometry.Length GLength(geometry)
✔️ point.M M(point)
✔️ geometry.NumGeometries NumGeometries(geometry)
✔️ polygon.NumInteriorRings NumInteriorRing(polygon)
✔️ geometry.NumPoints NumPoints(geometry)
✔️ geometry.OgcGeometryType CASE GeometryType(geometry) ...
✔️ geometry1.Overlaps(geometry2) Overlaps(geometry1, geometry2)
✔️ geometry.PointOnSurface PointOnSurface(geometry)
✔️ geometry1.Relate(geometry2) Relate(geometry1, geometry2)
✔️ geometry.Reverse() ST_Reverse(geometry)
✔️ geometry.SRID SRID(geometry)
✔️ curve.StartPoint StartPoint(curve)
✔️ geometry1.SymmetricDifference(geometry2) SymDifference(geometry1, geometry2)
✔️ geometry.ToBinary() AsBinary(geometry)
✔️ geometry.ToText() AsText(geometry)
✔️ geometry1.Touches(geometry2) Touches(geometry1, geometry2)
✔️ geometry.Union() UnaryUnion(geometry)
✔️ geometry1.Union(geometry2) GUnion(geometry1, geometry2)
✔️ geometry1.Within(geometry2) Within(geometry1, geometry2)
✔️ point.X X(point)
✔️ point.Y Y(point)
✔️ point.Z Z(point)

Original description:

Could possibly add some Migrations operations for creating R*Tree indexes

Some of this overlaps with dotnet/efcore#1100

@bricelam
Copy link
Owner Author

bricelam commented Apr 14, 2018

Entity splitting (dotnet/efcore#620) would make querying easier since the "index" is really just a table containing a key and the lat-long values.

from b in db.Buildings
where b.Lattitude >= minLat && b.Lattitude <= maxLat
    && b.Longitude >= minLong && b.Longitude <= maxLong
select b;

@bricelam
Copy link
Owner Author

bricelam commented Apr 14, 2018

On the other hand, maybe introducing a LatLong class with translatable methods that you can use as a weak, owned entity type with table splitting is a better way to go.

from b in db.Buildings
where b.Location.Inside(minLat, maxLat, minLong, maxLong)
select b;

@bricelam
Copy link
Owner Author

Oh wait, I missed something: R*Trees aren't for indexing points. They're for indexing areas.

Instead of a LatLong, it would be more like a Rectangle.

@bricelam
Copy link
Owner Author

bricelam commented Apr 17, 2018

One interesting point made by the documentation: You can use the R*Tree index to perform coarse-grained filtering before performing more expensive, fine-grained filtering.

For example, get all the things that fit within a square then figure out if those things fit within a circle:

image

@corstian
Copy link

What are your ideas about making geospatial queries work in SQLite as it isn't supported at all? Adding geospatial capabilities to this library?

@bricelam
Copy link
Owner Author

(Brainstorming here) Nearly all of the geospatial calculations would happen in EF on the client side, but we'd try and leverage the R*Tree index to do preliminary filtering.

In the illustration above, you could imagine the query would be something like "find all the places withing a 10 mile radius. We'd query SQLite for all the places inside a 10x10 mile square, then perform the actual radius calculation on just those results (instead of doing it for every row in the database).

@bricelam
Copy link
Owner Author

Also, if you were storing complex shapes inside the database, you could store the bounding box for those shapes inside the R*Tree index to help with the "first pass" (server-side) filtering.

@corstian
Copy link

I get it now! The R*Tree on SQLite would probably suffice for most of my use cases so far 😃. I'd be happy to help as this is one of the things I'd love to see implemented!

@bricelam
Copy link
Owner Author

bricelam commented Apr 25, 2018

My initial goal with this issue was just to make creating R*Tree indexes easier in EF Core Migrations.

There's nothing really blocking spatial on SQLite today. Apps are free to use a library like Microsoft.Spatial or NetTopologySuite to perform client-side calculations and persist the values as well-known text/binary in the database. They could even manually create an R*Tree index and leverage it in their queries.

Perhaps a better alternative to all of this is actually to leverage SpatiaLite (or similar) on the server and translate Microsoft.Spatial or NetTopologySuite calls on the client to the appropriate SQL. But I'd like to see how dotnet/efcore#1100 plays out before diving into an implementation like that.

@bricelam
Copy link
Owner Author

It looks I'll be working on spatial support for EF Core soon. We'll probably leverage NetTopologySuite.

Given that, I'll probably forego any R*Tree support and just translate to server-side SpatiaLite calls. I'll need to figure out how best to package and distribute SpatiaLite on NuGet for all the .NET Core and Xamarin platforms... (ericsink/SQLitePCL.raw#215)

@bricelam
Copy link
Owner Author

bricelam commented Jun 5, 2018

Looks like SpatiaLite is available on Debian/Ubuntu as the libsqlite3-mod-spatialite package. Windows binaries are available here.

@bricelam
Copy link
Owner Author

bricelam commented Jun 5, 2018

And it's available as libspatialite on Homebrew for OSX.

@bricelam
Copy link
Owner Author

Note to self: After creating a database, need to call SELECT InitSpatialMetaData();. We do something similar for memory-optimized tables on SQL Server.

@bricelam
Copy link
Owner Author

I've made good progress on this in bricelam/EFCore:nts. We hope to have something ready for 2.2.0-preview2.

@bricelam
Copy link
Owner Author

bricelam commented Sep 7, 2018

PR: dotnet/efcore#13245

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants