Dane przestrzenne

Dane przestrzenne reprezentują lokalizację fizyczną i kształt obiektów. Wiele baz danych zapewnia obsługę tego typu danych, dzięki czemu można je indeksować i wykonywać zapytania obok innych danych. Typowe scenariusze obejmują wykonywanie zapytań dotyczących obiektów w danej odległości od lokalizacji lub wybieranie obiektu, którego obramowanie zawiera daną lokalizację. Program EF Core obsługuje mapowanie na typy danych przestrzennych przy użyciu biblioteki przestrzennej NetTopologySuite.

Trwa instalowanie

Aby używać danych przestrzennych w programie EF Core, należy zainstalować odpowiedni pakiet NuGet obsługujący. Pakiet, który należy zainstalować, zależy od używanego dostawcy.

Dostawca platformy EF Core Pakiet NuGet przestrzenny
Microsoft.EntityFrameworkCore.SqlServer Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite
Microsoft.EntityFrameworkCore.Sqlite Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite
Microsoft.EntityFrameworkCore.InMemory NetTopologySuite
Npgsql.EntityFrameworkCore.PostgreSQL Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite
Pomelo.EntityFrameworkCore.MySql Pomelo.EntityFrameworkCore.MySql.NetTopologySuite
Devart.Data.MySql.EFCore Devart.Data.MySql.EFCore.NetTopologySuite
Devart.Data.Oracle.EFCore Devart.Data.Oracle.EFCore.NetTopologySuite
Devart.Data.PostgreSql.EFCore Devart.Data.PostgreSql.EFCore.NetTopologySuite
Devart.Data.SQLite.EFCore Devart.Data.SQLite.EFCore.NetTopologySuite
Teradata.EntityFrameworkCore Teradata.EntityFrameworkCore.NetTopologySuite

NetTopologySuite

NetTopologySuite (NTS) to biblioteka przestrzenna platformy .NET. Program EF Core umożliwia mapowanie na typy danych przestrzennych w bazie danych przy użyciu typów NTS w modelu.

Aby włączyć mapowanie typów przestrzennych za pośrednictwem NTS, wywołaj metodę UseNetTopologySuite w konstruktorze opcji DbContext dostawcy. Na przykład za pomocą programu SQL Server należy nazwać go w następujący sposób.

options.UseSqlServer(
    @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=WideWorldImporters",
    x => x.UseNetTopologySuite());

Istnieje kilka typów danych przestrzennych. Używany typ zależy od typów kształtów, które mają być dozwolone. Oto hierarchia typów NTS, których można użyć dla właściwości w modelu. Znajdują się one w NetTopologySuite.Geometries przestrzeni nazw.

  • Geometrii
    • Punkt
    • Linestring
    • Wielokąt
    • Geometrycollection
      • Multipoint
      • Multilinestring
      • Multipolygon

Ostrzeżenie

Cykliczne ciągi, CompoundCurve i CurePolygon nie są obsługiwane przez NTS.

Użycie podstawowego typu Geometria umożliwia określenie dowolnego typu kształtu przez właściwość .

Długość geograficzna i szerokość geograficzna

Współrzędne w NTS są wartościami X i Y. Aby reprezentować długość geograficzną i szerokość geograficzną, użyj wartości X dla długości geograficznej i Y dla szerokości geograficznej. Należy pamiętać, że jest to wstecz z latitude, longitude formatu, w którym zazwyczaj są widoczne te wartości.

Wykonanie zapytania o dane

Poniższe klasy jednostek mogą służyć do mapowania na tabele w przykładowej bazie danych Wide World Importers.

[Table("Cities", Schema = "Application")]
public class City
{
    public int CityID { get; set; }

    public string CityName { get; set; }

    public Point Location { get; set; }
}
[Table("Countries", Schema = "Application")]
public class Country
{
    public int CountryID { get; set; }

    public string CountryName { get; set; }

    // Database includes both Polygon and MultiPolygon values
    public Geometry Border { get; set; }
}

W LINQ metody i właściwości NTS dostępne jako funkcje bazy danych zostaną przetłumaczone na język SQL. Na przykład metody Distance i Contains są tłumaczone w następujących zapytaniach. Zapoznaj się z dokumentacją dostawcy, dla której metody są obsługiwane.

// Find the nearest city
var nearestCity = db.Cities
    .OrderBy(c => c.Location.Distance(currentLocation))
    .FirstOrDefault();
// Find the containing country
var currentCountry = db.Countries
    .FirstOrDefault(c => c.Border.Contains(currentLocation));

Inżynieria odwrotna

Pakiety NuGet przestrzenne włączają również modele inżynierii odwrotnej z właściwościami przestrzennymi, ale należy zainstalować pakiet przed uruchomieniem Scaffold-DbContext polecenia lub dotnet ef dbcontext scaffold. Jeśli tego nie zrobisz, otrzymasz ostrzeżenia dotyczące braku znajdowania mapowań typów dla kolumn, a kolumny zostaną pominięte.

Identyfikator SRID ignorowany podczas operacji klienta

NTS ignoruje wartości SRID podczas operacji. Przyjęto założenie, że planarny układ współrzędnych. Oznacza to, że jeśli określisz współrzędne pod względem długości geograficznej i szerokości geograficznej, niektóre wartości oceniane przez klienta, takie jak odległość, długość i obszar będą w stopniach, a nie metrach. Aby uzyskać bardziej znaczące wartości, należy najpierw projektować współrzędne do innego systemu współrzędnych przy użyciu biblioteki, takiej jak ProjNet (for GeoAPI).

Uwaga

Użyj nowszego pakietu NuGet ProjNet, a nie starszego pakietu o nazwie ProjNet4GeoAPI.

Jeśli operacja jest obliczana przez program EF Core za pośrednictwem programu SQL, jednostka wyniku zostanie określona przez bazę danych.

Oto przykład użycia sieci ProjNet do obliczenia odległości między dwoma miastami.

public static class GeometryExtensions
{
    private static readonly CoordinateSystemServices _coordinateSystemServices
        = new CoordinateSystemServices(
            new Dictionary<int, string>
            {
                // Coordinate systems:

                [4326] = GeographicCoordinateSystem.WGS84.WKT,

                // This coordinate system covers the area of our data.
                // Different data requires a different coordinate system.
                [2855] =
                    @"
                        PROJCS[""NAD83(HARN) / Washington North"",
                            GEOGCS[""NAD83(HARN)"",
                                DATUM[""NAD83_High_Accuracy_Regional_Network"",
                                    SPHEROID[""GRS 1980"",6378137,298.257222101,
                                        AUTHORITY[""EPSG"",""7019""]],
                                    AUTHORITY[""EPSG"",""6152""]],
                                PRIMEM[""Greenwich"",0,
                                    AUTHORITY[""EPSG"",""8901""]],
                                UNIT[""degree"",0.01745329251994328,
                                    AUTHORITY[""EPSG"",""9122""]],
                                AUTHORITY[""EPSG"",""4152""]],
                            PROJECTION[""Lambert_Conformal_Conic_2SP""],
                            PARAMETER[""standard_parallel_1"",48.73333333333333],
                            PARAMETER[""standard_parallel_2"",47.5],
                            PARAMETER[""latitude_of_origin"",47],
                            PARAMETER[""central_meridian"",-120.8333333333333],
                            PARAMETER[""false_easting"",500000],
                            PARAMETER[""false_northing"",0],
                            UNIT[""metre"",1,
                                AUTHORITY[""EPSG"",""9001""]],
                            AUTHORITY[""EPSG"",""2855""]]
                    "
            });

    public static Geometry ProjectTo(this Geometry geometry, int srid)
    {
        var transformation = _coordinateSystemServices.CreateTransformation(geometry.SRID, srid);

        var result = geometry.Copy();
        result.Apply(new MathTransformFilter(transformation.MathTransform));

        return result;
    }

    private class MathTransformFilter : ICoordinateSequenceFilter
    {
        private readonly MathTransform _transform;

        public MathTransformFilter(MathTransform transform)
            => _transform = transform;

        public bool Done => false;
        public bool GeometryChanged => true;

        public void Filter(CoordinateSequence seq, int i)
        {
            var x = seq.GetX(i);
            var y = seq.GetY(i);
            var z = seq.GetZ(i);
            _transform.Transform(ref x, ref y, ref z);
            seq.SetX(i, x);
            seq.SetY(i, y);
            seq.SetZ(i, z);
        }
    }
}
var seattle = new Point(-122.333056, 47.609722) { SRID = 4326 };
var redmond = new Point(-122.123889, 47.669444) { SRID = 4326 };

// In order to get the distance in meters, we need to project to an appropriate
// coordinate system. In this case, we're using SRID 2855 since it covers the
// geographic area of our data
var distanceInDegrees = seattle.Distance(redmond);
var distanceInMeters = seattle.ProjectTo(2855).Distance(redmond.ProjectTo(2855));

Uwaga

4326 odnosi się do WGS 84, standard używany w GPS i innych systemach geograficznych.

Dodatkowe zasoby

Informacje specyficzne dla bazy danych

Zapoznaj się z dokumentacją dostawcy, aby uzyskać dodatkowe informacje na temat pracy z danymi przestrzennymi.

Inne zasoby

  • NetTopologySuite Docs
  • Sesja standupu społeczności danych platformy .NET, koncentrująca się na danych przestrzennych i aplikacji NetTopologySuite.