Share via


Datos espaciales

Los datos espaciales representan la ubicación física y la forma de los objetos. Muchas bases de datos proporcionan compatibilidad con este tipo de datos para que se puedan indexar y consultar junto con otros datos. Entre los escenarios comunes se incluyen la consulta de objetos a una distancia determinada desde una ubicación o la selección del objeto cuyo borde contiene una ubicación determinada. EF Core admite la asignación a tipos de datos espaciales mediante la biblioteca espacial NetTopologySuite.

Instalación de

Para usar datos espaciales con EF Core, debe instalar el paquete NuGet compatible adecuado. El paquete que necesita instalar depende del proveedor que use.

Proveedor de EF Core Paquete NuGet espacial
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) es una biblioteca espacial para .NET. EF Core permite la asignación a tipos de datos espaciales en la base de datos mediante tipos NTS en el modelo.

Para habilitar la asignación a tipos espaciales a través de NTS, llame al método UseNetTopologySuite en el generador de opciones DbContext del proveedor. Por ejemplo, con SQL Server lo llamaría así.

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

Hay varios tipos de datos espaciales. El tipo que use depende de los tipos de formas que desee permitir. Esta es la jerarquía de tipos NTS que puede usar para las propiedades del modelo. Se encuentran dentro del espacio de nombres NetTopologySuite.Geometries.

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

Advertencia

NTS no admite CircularString, CompoundCurve ni CurePolygon.

El uso del tipo base Geometry permite especificar cualquier tipo de forma mediante la propiedad.

Longitud y latitud

Las coordenadas de NTS se encuentran en términos de valores X e Y. Para representar la longitud y la latitud, use X para la longitud e Y para la latitud. Tenga en cuenta que esto es a la inversa del formato latitude, longitude en el que normalmente se ven estos valores.

Consulta de datos

Las siguientes clases de entidad se pueden usar para asignarse a tablas de la base de datos de ejemplo de 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; }
}

En LINQ, los métodos y propiedades NTS disponibles como funciones de base de datos se traducirán a SQL. Por ejemplo, los métodos Distance y Contains se traducen en las siguientes consultas. Consulte la documentación del proveedor para ver qué métodos se admiten.

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

Ingeniería inversa

Los paquetes NuGet espaciales también permiten modelos de ingeniería inversa con propiedades espaciales, pero debe instalar el paquete antes de ejecutar Scaffold-DbContext o dotnet ef dbcontext scaffold. Si no lo hace, recibirá advertencias que dicen que no se encuentran asignaciones de tipos para las columnas y se omitirán las columnas.

SRID omitido durante las operaciones de cliente

NTS omite los valores SRID durante las operaciones. Se asume el uso de un sistema de coordenadas planas. Esto significa que si especifica coordenadas en términos de longitud y latitud, algunos valores evaluados por el cliente, como la distancia, la longitud y el área, estarán en grados, no en metros. Para obtener valores más significativos, primero debe proyectar las coordenadas en otro sistema de coordenadas mediante una biblioteca como ProjNet (para GeoAPI).

Nota:

Use el paquete NuGet de ProjNet más reciente, no el paquete anterior denominado ProjNet4GeoAPI.

Si EF Core evalúa una operación mediante SQL, la unidad del resultado se determinará mediante la base de datos.

Este es un ejemplo del uso de ProjNet para calcular la distancia entre dos ciudades.

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 hace referencia a WGS 84, un estándar utilizado en GPS y otros sistemas geográficos.

Recursos adicionales

Información específica de la base de datos

Asegúrese de leer la documentación del proveedor para obtener información adicional sobre cómo trabajar con datos espaciales.

Otros recursos