Dati spaziali

I dati spaziali rappresentano la posizione fisica e la forma degli oggetti. Molti database forniscono supporto per questo tipo di dati in modo che possano essere indicizzati ed sottoposti a query insieme ad altri dati. Gli scenari comuni includono l'esecuzione di query per gli oggetti all'interno di una determinata distanza da una posizione o la selezione dell'oggetto il cui bordo contiene una determinata posizione. EF Core supporta il mapping ai tipi di dati spaziali tramite la libreria spaziale NetTopologySuite.

Installazione

Per usare i dati spaziali con EF Core, è necessario installare il pacchetto NuGet di supporto appropriato. Il pacchetto da installare dipende dal provider in uso.

Provider EF Core Pacchetto NuGet spaziale
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) è una libreria spaziale per .NET. EF Core consente il mapping ai tipi di dati spaziali nel database usando i tipi NTS nel modello.

Per abilitare il mapping ai tipi spaziali tramite NTS, chiamare il metodo UseNetTopologySuite nel generatore di opzioni DbContext del provider. Ad esempio, con SQL Server è necessario chiamarlo come questo.

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

Esistono diversi tipi di dati spaziali. Il tipo utilizzato dipende dai tipi di forme che si desidera consentire. Ecco la gerarchia dei tipi NTS che è possibile usare per le proprietà nel modello. Si trovano all'interno dello spazio dei NetTopologySuite.Geometries nomi.

  • Geometria
    • Point
    • LineString
    • Polygon
    • Geometrycollection
      • MultiPoint
      • MultiLineString
      • MultiPolygon

Avviso

CircularString, CompoundCurve e CurePolygon non sono supportati da NTS.

L'utilizzo del tipo Geometry di base consente di specificare qualsiasi tipo di forma dalla proprietà .

Longitudine e latitudine

Le coordinate in NTS sono in termini di valori X e Y. Per rappresentare longitudine e latitudine, usare X per longitudine e Y per la latitudine. Si noti che si tratta di un'operazione all'indietro rispetto al latitude, longitude formato in cui vengono in genere visualizzati questi valori.

Query sui dati

È possibile usare le classi di entità seguenti per eseguire il mapping alle tabelle nel database di esempio 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; }
}

In LINQ i metodi e le proprietà NTS disponibili come funzioni di database verranno convertiti in SQL. Ad esempio, i metodi Distance e Contains vengono convertiti nelle query seguenti. Vedere la documentazione del provider per i metodi supportati.

// 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));

Reverse Engineering

I pacchetti NuGet spaziali abilitano anche modelli di reverse engineering con proprietà spaziali, ma è necessario installare il pacchetto prima di eseguire Scaffold-DbContext o dotnet ef dbcontext scaffold. In caso contrario, verranno visualizzati avvisi sulla mancata ricerca dei mapping dei tipi per le colonne e le colonne verranno ignorate.

SRID Ignorato durante le operazioni client

NTS ignora i valori SRID durante le operazioni. Presuppone un sistema di coordinate planari. Ciò significa che se si specificano coordinate in termini di longitudine e latitudine, alcuni valori valutati dal client, ad esempio distanza, lunghezza e area saranno in gradi, non metri. Per valori più significativi, è prima necessario proiettare le coordinate in un altro sistema di coordinate usando una libreria come ProjNet (per GeoAPI).

Nota

Usare il pacchetto NuGet ProjNet più recente, non il pacchetto precedente denominato ProjNet4GeoAPI.

Se un'operazione viene valutata dal server da EF Core tramite SQL, l'unità del risultato verrà determinata dal database.

Di seguito è riportato un esempio di utilizzo di ProjNet per calcolare la distanza tra due città.

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));

Nota

4326 si riferisce a WGS 84, uno standard usato in GPS e altri sistemi geografici.

Risorse aggiuntive

Informazioni specifiche del database

Assicurarsi di leggere la documentazione del provider per altre informazioni sull'uso dei dati spaziali.

Altre risorse