Partager via


Données spatiales

Les données spatiales représentent l’emplacement physique et la forme des objets. De nombreuses bases de données prennent en charge ce type de données afin qu’elles puissent être indexées et interrogées en même temps que d’autres données. Parmi les scénarios courants, citons l’interrogation d’objets dans un rayon donné ou la sélection de l’objet dont la bordure contient un emplacement donné. EF Core prend en charge le mappage aux types de données spatiales à l’aide de la bibliothèque spatiale NetTopologySuite.

Installation de

Pour utiliser des données spatiales avec EF Core, vous devez installer le package NuGet approprié. Le package que vous devez installer dépend du fournisseur que vous utilisez.

Fournisseur EF Core Package NuGet spatiaux
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) est une bibliothèque spatiale pour .NET. EF Core permet le mappage aux types de données spatiales dans la base de données à l’aide de types NTS dans votre modèle.

Pour activer le mappage aux types spatiaux via NTS, appelez la méthode UseNetTopologySuite sur le générateur d’options DbContext du fournisseur. Par exemple, avec SQL Server, vous l’appelez comme suit.

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

Il existe plusieurs types de données spatiales. Le type que vous utilisez dépend des types de formes que vous souhaitez autoriser. Voici la hiérarchie des types NTS que vous pouvez utiliser pour les propriétés de votre modèle. Ils se trouvent dans l’espace de noms NetTopologySuite.Geometries.

  • Geometry
    • Point
    • LineString
    • Polygon
    • GeometryCollection
      • MultiPoint
      • MultiLineString
      • MultiPolygon

Avertissement

CircularString, CompoundCurve et CurePolygon ne sont pas pris en charge par NTS.

L’utilisation du type Geometry de base permet à n’importe quel type de forme d’être spécifié par la propriété.

Longitude et latitude

Les coordonnées dans NTS sont en termes de valeurs X et Y. Pour représenter la longitude et la latitude, utilisez X pour la longitude et Y pour la latitude. Notez que c’est à rebours à partir du format latitude, longitude où vous voyez généralement ces valeurs.

Interrogation des données

Les classes d’entité suivantes peuvent être utilisées pour mapper aux tables dans l’exemple de base de données 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; }
}

Dans LINQ, les méthodes et propriétés NTS disponibles en tant que fonctions de base de données sont traduites en SQL. Par exemple, les méthodes Distance et Contains sont traduites dans les requêtes suivantes. Consultez la documentation de votre fournisseur pour laquelle les méthodes sont prises en charge.

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

Ingénierie à rebours

Les packages NuGet spatiaux permettent d’avoir aussi des modèles d’ingénierie à rebours avec des propriétés spatiales, mais vous devez installer le package avant d’exécuter Scaffold-DbContext ou dotnet ef dbcontext scaffold. Si vous ne le faites pas, vous recevrez des avertissements indiquant que les mappages de types sont introuvables pour les colonnes, et les colonnes seront ignorées.

SRID ignoré pendant les opérations du client

NTS ignore les valeurs SRID pendant les opérations. Il part du principe que le système de coordonnées est planaire. Cela signifie que si vous spécifiez des coordonnées en termes de longitude et de latitude, certaines valeurs évaluées par le client telles que la distance, la longueur et la zone seront exprimées en degrés, et pas en mètres. Pour obtenir des valeurs plus significatives, vous devez d’abord projeter les coordonnées sur un autre système de coordonnées à l’aide d’une bibliothèque telle que ProjNet (pour GeoAPI).

Remarque

Utilisez le package NuGet ProjNet qui est plus récent, pas l’ancien package appelé ProjNet4GeoAPI.

Si une opération est évaluée par le serveur par EF Core via SQL, l’unité du résultat est déterminée par la base de données.

Voici un exemple d’utilisation de ProjNet pour calculer la distance entre deux villes.

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

Remarque

4326 fait référence à WGS 84, norme utilisée dans les GPS et autres systèmes de localisation.

Ressources supplémentaires

Informations spécifiques à la base de données

Veillez à lire la documentation de votre fournisseur pour plus d’informations sur l’utilisation des données spatiales.

Autres ressources