空間データ

空間データは、オブジェクトの物理的な場所と形状を表現します。 この種類のデータは多くのデータベースでサポートされているため、他のデータと共にインデックスを作成し、クエリを実行できます。 一般的なシナリオとしては、ある場所から特定の距離内にあるオブジェクトに対するクエリを実行する場合や、境界線に特定の場所が含まれるオブジェクトを選択する場合などがあります。 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

NetTopologySuite

NetTopologySuite (NTS) は .NET 用の空間ライブラリです。 EF Core を使用すると、モデルで NTS 型を使用してデータベースの空間データ型にマッピングできます。

NTS を使用して空間型にマッピングできるようにするには、プロバイダーの DbContext オプション ビルダーで UseNetTopologySuite メソッドを呼び出します。 たとえば、SQL Server では次のように呼び出します。

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

空間データ型は複数あります。 使用する型は、使用できるようにする形状の種類によって異なります。 モデルのプロパティに使用できる NTS 型の階層を次に示します。 これらは NetTopologySuite.Geometries 名前空間内にあります。

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

警告

NTS では CircularString、CompoundCurve、CircularPolygon はサポートされません。

基本の Geometry 型を使用すると、プロパティによって任意の型の図形を指定できます。

経度と緯度

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 = db.Cities
    .OrderBy(c => c.Location.Distance(currentLocation))
    .FirstOrDefault();
// Find the containing country
var currentCountry = db.Countries
    .FirstOrDefault(c => c.Border.Contains(currentLocation));

リバース エンジニアリング

また、空間 NuGet パッケージでは、空間プロパティを使用するリバース エンジニアリング モデルも可能になりますが、Scaffold-DbContext または dotnet ef dbcontext scaffold を実行する "" にこのパッケージをインストールする必要があります。 そうしない場合、列の型マッピングが見つからないという警告が表示され、列がスキップされます。

クライアントの操作中に SRID が無視される

NTS では、SRID 値は操作中に無視されます。 これは平面座標系が前提になっています。 つまり、経度と緯度の座標を指定する場合、距離、長さ、面積などのクライアントによって評価される一部の値は、メートル数ではなく度数になります。 より有用な値を得るには、まず、ProjNet (GeoAPI の場合) のようなライブラリを使用して、座標を別の座標系に投影する必要があります。

Note

ProjNet4GeoAPI という古いパッケージではなく、より新しい ProjNet NuGet パッケージをご使用ください。

操作が SQL を介した EF Core によるサーバー評価の対象である場合、結果の単位はデータベースによって決められます。

ProjNet を使用して 2 つの都市間の距離を計算する例を次に示します。

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

Note

4326 は WGS 84 (GPS や他の地理システムで使用されている標準) を表します。

その他のリソース

データベース固有の情報

空間データの操作に関する追加情報については、プロバイダーのドキュメントを参照してください。

その他のリソース