Partilhar via


Dados espaciais

Os dados espaciais representam a localização física e a forma dos objetos. Muitas bases de dados fornecem suporte para este tipo de dados para que possam ser indexados e consultados juntamente com outros dados. Os cenários comuns incluem a consulta de objetos dentro de uma determinada distância de um local ou a seleção do objeto cuja borda contém um determinado local. O EF Core suporta mapeamento para tipos de dados espaciais usando a biblioteca espacial NetTopologySuite.

Instalação

Para usar dados espaciais com o EF Core, você precisa instalar o pacote NuGet de suporte apropriado. O pacote que você precisa instalar depende do provedor que você está usando.

Fornecedor EF Core Pacote NuGet espacial
Microsoft.EntityFrameworkCore.SqlServer Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite
Microsoft.EntityFrameworkCore.Sqlite Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite
Microsoft.EntityFrameworkCore.InMemory NetTopologySuite
Oracle.EntityFrameworkCore Oracle.EntityFrameworkCore.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
FileBaseContext NetTopologySuite

NetTopologySuite

NetTopologySuite (NTS) é uma biblioteca espacial para .NET. O EF Core permite o mapeamento para tipos de dados espaciais no banco de dados usando tipos NTS em seu modelo.

Para habilitar o mapeamento para tipos espaciais via NTS, chame o método UseNetTopologySuite no construtor de opções DbContext do provedor. Por exemplo, com o SQL Server você o chamaria assim.

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

Existem vários tipos de dados espaciais. O tipo que você usa depende dos tipos de formas que você deseja permitir. Aqui está a hierarquia de tipos NTS que você pode usar para propriedades em seu modelo. Eles estão localizados dentro do NetTopologySuite.Geometries namespace.

  • Geometria
    • Ponto
    • LineString
    • Polígono
    • Coleção de Geometria
      • MultiPonto
      • MultiLineString
      • Multipolígono

Advertência

CircularString, CompoundCurve e CurePolygon não são suportados pelo NTS.

O uso do tipo Geometria base permite que qualquer tipo de forma seja especificado pela propriedade.

Longitude e Latitude

As coordenadas em NTS são em termos de valores X e Y. Para representar longitude e latitude, use X para longitude e Y para latitude. Observe que isso é na forma invertida em relação ao formato em que costumas ver esses valores.

Consultando dados

As seguintes classes de entidade podem ser usadas para mapear para tabelas na base de dados de exemplo 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; }
}

No LINQ, os métodos NTS e as propriedades disponíveis como funções de banco de dados serão traduzidos para SQL. Por exemplo, os métodos Distance e Contains são traduzidos nas consultas a seguir. Consulte a documentação do seu provedor para quais métodos são suportados.

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

Engenharia inversa

Os pacotes NuGet espaciais também permitem modelos de engenharia reversa com propriedades espaciais, mas você precisa instalar o pacote antes de executar Scaffold-DbContext ou dotnet ef dbcontext scaffold. Se não o fizer, receberá alertas por não encontrar mapeamentos de tipo para as colunas e as colunas serão ignoradas.

SRID ignorado durante as operações do cliente

O NTS ignora os valores SRID durante as operações. Pressupõe um sistema de coordenadas planas. Isso significa que, se você especificar coordenadas em termos de longitude e latitude, alguns valores avaliados pelo cliente, como distância, comprimento e área, serão em graus, não em metros. Para valores mais significativos, primeiro você precisa projetar as coordenadas para outro sistema de coordenadas usando uma biblioteca como ProjNet (para GeoAPI).

Observação

Use o pacote ProjNet NuGet mais recente, não o pacote mais antigo chamado ProjNet4GeoAPI.

Se uma operação for avaliada pelo servidor pelo EF Core via SQL, a unidade do resultado será determinada pelo banco de dados.

Aqui está um exemplo de como usar o ProjNet para calcular a distância entre duas cidades.

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

Observação

4326 refere-se ao WGS 84, um padrão usado em GPS e outros sistemas geográficos.

Recursos adicionais

Informações específicas da base de dados

Certifique-se de ler a documentação do seu provedor para obter informações adicionais sobre como trabalhar com dados espaciais.

Outros recursos