Поделиться через


Пространственные данные

Пространственные данные представляют физическое расположение и форму объектов. Многие базы данных поддерживают этот тип данных, поэтому их можно индексировать и запрашивать вместе с другими данными. Распространенные сценарии включают запросы к объектам в пределах заданного расстояния от расположения или выбор объекта, граница которого содержит заданное расположение. EF Core поддерживает сопоставление с пространственными типами данных с помощью пространственной библиотеки NetTopologySuite.

Установка

Чтобы использовать пространственные данные с EF Core, необходимо установить соответствующий вспомогательный пакет NuGet. Какой пакет необходимо установить, зависит от используемого поставщика.

Поставщик EF Core Пространственный пакет NuGet
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
FileBaseContext NetTopologySuite

NetTopologySuite

NetTopologySuite (NTS) — это пространственная библиотека для .NET. EF Core позволяет сопоставлять пространственные типы данных в базе данных с помощью типов NTS в модели.

Чтобы включить сопоставление пространственных типов с помощью NTS, вызовите метод UseNetTopologySuite в построителе параметров DbContext поставщика. Например, при использовании SQL Server вы будете называть его следующим образом.

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

Существует несколько типов пространственных данных. Тип, который вы используете, зависит от видов фигур, которые вы хотите разрешить. Ниже приведена иерархия типов NTS, которые можно использовать для свойств в модели. Они расположены в пространстве имен NetTopologySuite.Geometries.

  • Геометрия
    • Точка
    • Линейная строка (LineString)
    • Многоугольник
    • Коллекция геометрий
      • MultiPoint
      • МультиЛайнСтринг
      • Мультиполигон

Предупреждение

CircularString, CompoundCurve и CurePolygon не поддерживаются NTS.

Использование базового геометрического типа позволяет задать любой тип фигуры свойством.

Долгота и широта

Координаты в NTS относятся к значениям X и Y. Для представления долготы и широты используйте X для долготы и Y для широты. Обратите внимание, что это противоположный от latitude, longitude формата, в котором обычно отображаются эти значения.

Запрос данных

Следующие классы сущностей можно использовать для сопоставления с таблицами в образце базы данных 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; }
}

В LINQ методы и свойства NTS, доступные в качестве функций базы данных, будут переведены в SQL. Например, методы Distance и Contains претворяются в следующие запросы. Ознакомьтесь с документацией поставщика, для которой поддерживаются методы.

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

Обратное проектирование

Пакеты пространственных NuGet также позволяют выполнять обратную разработку моделей с пространственными свойствами, но необходимо установить пакет перед выполнениемScaffold-DbContext или dotnet ef dbcontext scaffold. Если вы этого не сделаете, вы получите предупреждения о том, что не найдены сопоставления типов для столбцов, и они будут пропущены.

SRID игнорируется во время клиентских операций

NTS игнорирует значения SRID во время операций. Предполагается наличие планарной системы координат. Это означает, что если вы указываете координаты с точки зрения долготы и широты, некоторые вычисляемые клиентом значения, такие как расстояние, длина и область будут находиться в градусах, а не в метрах. Для получения более значимых значений сначала необходимо проецировать координаты в другую систему координат, используя библиотеку, например ProjNet (для GeoAPI).

Замечание

Используйте новый пакет NuGet ProjNet, а не старый пакет ProjNet4GeoAPI.

Если операция оценивается сервером EF Core через SQL, единица измерения результата будет определена базой данных.

Ниже приведен пример использования ProjNet для вычисления расстояния между двумя городами.

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

Замечание

4326 относится к WGS 84, стандарту, используемому в GPS и других географических системах.

Дополнительные ресурсы

Сведения о конкретной базе данных

Обязательно ознакомьтесь с документацией поставщика для получения дополнительных сведений о работе с пространственными данными.

Другие ресурсы