空間データとNetTopologySuite

Oracleデータベースに、物理的な場所とオブジェクトの形状を表す空間データを格納できます。ODP.NETにより、Oracle空間データをネイティブに操作できます。

EF Coreでは、NetTopologySuite (NTS)空間ライブラリを使用して空間データ型にマップします。Oracle EF Core 10以降、OracleデータベースのSDO_GEOMETRY空間データ型は、Oracle EF Core NTSライブラリを使用してアクセスおよびマップできます。

このライブラリを使用すると、Oracle空間列を.NET形状タイプ(点やポリゴンなど)にマップできます。OracleデータベースのLINQ文に、空間ロジック(距離や交差など)を含めることができます。EF Coreアプリケーションでは、Oracleデータベースの空間データの問合せ、挿入、更新および削除に標準の方法を使用できるようになりました。

設定

このOracle空間ライブラリは、NuGetギャラリにあるOracle.EntityFrameworkCore.NetTopologySuiteパッケージで入手できます。これは現在プレビュー中です。NuGetパッケージ・マネージャでは、Oracle EF Core空間型のサポートに必要な他のNTSパッケージが自動的に取得されます。

Oracle空間データをEF Coreで使用するアプリケーションを設定するには、次のステップに従います:

  1. Oracle.EntityFrameworkCore.NetTopologySuiteパッケージを.NETプロジェクトに追加します。

  2. DbContextを使用してそのライブラリを有効にします。拡張メソッドUseNetTopologySuite()により、空間プロバイダ設定を構成します(許可できる幾何学的計算許容誤差を制御する、空間許容レベルなど)。たとえば:

    services.AddDbContext<MyContext>(options =>
        options.UseOracle("<CONNECTION STRING>",
            o => o.UseNetTopologySuite(tolerance: 0.01)));
  3. エンティティ・モデルにおいて、空間クラスを定義します。たとえば:

    public class MySpatialEntity
    {
        public int Id { get; set; }
        
       // NetTopologySuite.Geometries.Point
        public Point Location { get; set; } 
    }
  4. 次のような、空間データのLINQを記述します

    Point myPoint = new Point(10, 20);
    var nearby = context.MySpatialEntities
        .Where(e => e.Location.IsWithinDistance(myPoint, 1000))
        .ToList();

機能

Oracle EF Coreでは、次の機能がサポートされています:

  • OracleのSDO_GEOMETRY列データを次のようなNTS .NETデータ型に自動的にマップする

    • Point

    • LineString

    • Polygon

    • MultiPoint

    • MultiLineString

    • MultiPolygon

    • GeometryCollection

  • NTS .NET型に対してDML操作を実行する。

  • LINQの空間操作をOracle SQL空間ファンクションに変換する。

  • EF Coreの移行と、Oracle空間列を使用したデータベース・スキャフォールディング

  • UseNetTopologySuite()拡張メソッドでの許容範囲構成。

    • 許容範囲が指定されていない場合は、0.005がデフォルト値として設定されます。

ノート:

Oracle Spatialでは、NTSにおける使用できない機能や制限付き機能のスーパーセットがサポートされています(曲線形状など)。EF Core空間サポートはNTS機能に限定されています。

Oracle NetTopologySuiteのメンバー

表5-6 NetTopologySuiteメソッドとOracle Spatialファンクションのマッピング

NetTopologySuiteのメソッド Oracle Spatialのファンクション

EnvelopeCombiner.CombineAsGeometry(geom[])

SDO_AGGR_MBR

Geometry.AsBinary()

SDO_UTIL.TO_WKBGEOMETRY

Geometry.AsText()

SDO_UTIL.TO_WKTGEOMETRY

Geometry.Buffer(distance)

SDO_GEOM.SDO_BUFFER

Geometry.Contains(geom)

SDO_CONTAINS

Geometry.ConvexHull()

SDO_GEOM.SDO_CONVEXHULL

Geometry.CoveredBy(geom)

SDO_RELATE

Geometry.Covers(geom)

SDO_RELATE

Geometry.Crosses(geom)

SDO_RELATE

Geometry.Disjoint(geom)

SDO_RELATE

Geometry.Difference(geom)

SDO_GEOM.DIFFERENCE

Geometry.Distance(geom)

SDO_GEOM.SDO_DISTANCE

Geometry.EqualsExact(geom)

SDO_RELATE

Geometry.EqualsTopologically(geom)

SDO_RELATE

Geometry.GetGeometryN(n)

SDO_UTIL.EXTRACT

Geometry.Intersection(geom)

SDO_GEOM.SDO_INTERSECTION

Geometry.Intersects(geom)

SDO_RELATE

Geometry.IsWithinDistance(geom, dist)

SDO_WITHIN_DISTANCE

Geometry.Overlaps(geom)

SDO_RELATE

Geometry.Relate(geom, pattern). Instead of the DE-9IM pattern, Oracle uses relevant mask words

SDO_RELATE

Geometry.Reverse() for LineString only and requires Oracle Database 23ai or higher.

SDO_UTIL.REVERSE_LINESTRING

Geometry.SymmetricDifference(geom)

SDO_GEOM.SDO_XOR

Geometry.ToBinary()

SDO_UTIL.TO_WKBGEOMETRY

Geometry.ToText()

SDO_UTIL.TO_WKTGEOMETRY

Geometry.Touches(geom)

SDO_RELATE

Geometry.Within(geom)

SDO_CONTAINS

Geometry.Union()

SDO_GEOM.SDO_SELF_UNION

Geometry.Union(geom)

SDO_GEOM.SDO_UNION

GeometryCombiner.Combine(geom[])

SDO_AGGR_UNION

ConvexHull.Create(geom[])

SDO_AGGR_CONVEXHULL

UnaryUnionOp.Union(geom[])

SDO_AGGR_UNION

表5-7 NetTopologySuiteプロパティとOracle Spatialファンクションのマッピング

NetTopologySuiteのプロパティ Oracle Spatialのファンクション

Geometry.Area

SDO_GEOM.SDO_AREA

Geometry.Centroid

SDO_GEOM.SDO_CENTROID

Geometry.Envelope

SDO_GEOM.SDO_MBR

Geometry.GeometryType

CASE on SDO_GTYPE

Geometry.InteriorPoint

SDO_GEOM.SDO_POINTONSURFACE

Geometry.IsEmpty

Composite null checks on SDO_GEOMETRY fields

Geometry.IsSimple

SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT

Geometry.IsValid

SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT

Geometry.Length

SDO_GEOM.SDO_LENGTH

Geometry.NumGeometries

SDO_UTIL.GETNUMELEM

Geometry.NumPoints

SDO_UTIL.GETNUMVERTICES

Geometry.OgcGeometryType

CASE on SDO_GTYPE

Geometry.PointOnSurface

SDO_GEOM.SDO_POINTONSURFACE

Geometry.SRID

SDO_SRID(field)

GeometryCollection.Count

SDO_UTIL.GETNUMELEM

LineString.Count

SDO_UTIL.GETNUMVERTICES

LineString.EndPoint

SDO_LRS.GEOM_SEGMENT_END_PT

LineString.IsClosed

SDO_EQUAL(SDO_LRS.GEOM_SEGMENT_START_PT and SDO_LRS.GEOM_SEGMENT_END_PT)

LineString.IsRing

SDO_EQUAL(SDO_LRS.GEOM_SEGMENT_START_PT, SDO_LRS.GEOM_SEGMENT_END_PT), and SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT

LineString.StartPoint

SDO_LRS.GEOM_SEGMENT_START_PT

Point.X

SDO_POINT.X

Point.Y

SDO_POINT.Y

Point.Z

SDO_POINT.Z

Polygon.ExteriorRing

SDO_UTIL.POLYGONTOLINE and SDO_UTIL.EXTRACT(geom, 1, 0)

Polygon.NumInteriorRings

SDO_UTIL.GETNUMRINGS - 1

   
   

制限事項と回避策

  • M値(メジャー座標): NTSを使用するOracle EF Coreでは、M値(メジャー)座標はサポートされていません。M座標やZM座標があるジオメトリでは、シリアライズの間にM値が削除されます。それは、生成されたSDO_GEOMETRYには含まれていません。Oracle Spatialからジオメトリを読み取るときに、M値は、対応するNTSジオメトリ・オブジェクトに実体化されません。

    回避策:

    Mを認識した操作やLRS (線形参照システム)の操作の場合は、直接Oracle SQLまたはストアド・プロシージャを使用します:

    例1: M値ジオメトリの読取り

    次のM値があるジオメトリを含む、Oracle Database内のSDO_GEOMETRYオブジェクト:
    SDO_GEOMETRY(
        3303,        -- LineString with Z and M
        NULL,
        NULL,
        SDO_ELEM_INFO_ARRAY(1,2,1),
        SDO_ORDINATE_ARRAY(10,20,5,100, 30,40,10,200)
    )
    

    Oracle Database内のSDO_GEOMETRYオブジェクトを読み取るOracle EF Coreコードとその出力:

    
    // C#
    var segment = context.RoadSegments.First();
    var coords = segment.Geometry.Coordinates;
    
    // output:
    Coordinate[0] = (10, 20, 5)
    Coordinate[1] = (30, 40, 10)
    

    M値(100、200)は削除されます。それらは、結果となるオブジェクトには出現しません。

    例2: M値ジオメトリの書込み

    M値でジオメトリを更新するOracle EF Coreコード:
    // C#
    // EF Model
    public class RoadSegment
    {
        public int Id { get; set; }
        public LineString Geometry { get; set; }
    }
    
    // SaveChanges Code:
    var segment = new RoadSegment
    {
        Geometry = new LineString(new[]
        {
            new CoordinateZM(10, 20, 5, 100),
            new CoordinateZM(30, 40, 10, 200)
        })
    };
    
    context.RoadSegments.Add(segment);
    context.SaveChanges();
    
    Oracle EF Coreコードを実行してジオメトリをM値で更新すると、Oracle Database内に次のSDO_GEOMETRYオブジェクトが生成されます:
    SDO_GEOMETRY(
        2003,        -- 2D LineString type (M is ignored)
        NULL,
        NULL,
        SDO_ELEM_INFO_ARRAY(1,2,1),
        SDO_ORDINATE_ARRAY(10,20,5, 30,40,10)
    )
    
  • Relate()メソッド: NTSのRelate()メソッドでは、Dimensionally Extended Nine-Intersection Model (DE-9IM)マトリックスを使用してジオメトリ間の空間関係が確認されます。Oracle Spatialでは、他の空間ライブラリで使用されているDE-9IMパターンではなく、位相関係マスクと呼ばれる別の手法が使用されます。Entity Framework CoreのLINQ問合せでDE-9IMパターンを使用してRelate()メソッドを使用した場合(標準のNTS使用方法)、これらのパターンはOracle SQLに正しく変換されません。DE-9IMパターンを使用した問合せは失敗するか、正しくない結果を返す可能性があります。

    回避策

    DE-9IMマトリックスのかわりに、Relate()メソッドに対する2番目のパラメータとしてOracle Spatialの関係マスク文字列を指定します。Oracle EF CoreのNTSライブラリでは、SQL変換の間に、SDO_RELATEマスク・パラメータに空間関係マスクとしてこの文字列が挿入されます。

    .Relate()とOracleマスクを使用したLINQ問合せ。

    次の問合せでは、Relate()メソッドを使用して、各行のジオメトリとターゲット・ジオメトリの間の空間関係を確認しており、DE-9IMパターンではなくOracle空間関係マスクTOUCHを指定しています。
    // C#
    var targetGeometry = factory.CreatePolygon(new[]
    {
        new Coordinate(0, 0),
        new Coordinate(10, 0),
        new Coordinate(10, 10),
        new Coordinate(0, 10),
        new Coordinate(0, 0)
    });
    
    var result = context.GeometryEntities
        .Where(e => e.Geometry.Relate(targetGeometry, "TOUCH"))
        .Select(e => new { e.Id, e.Geometry })
        .ToList();
    
    Oracle EF Coreでは、このLINQ式(.Relate()とOracleマスクを使用したLINQ問合せ)は次のOracle SQLに変換されます:
    SELECT e."Id", e."Geometry"
    FROM "GeometryEntities" e
    WHERE SDO_RELATE(e."Geometry", :targetGeometry, 'mask=TOUCH') = 'TRUE'
    

    Oracleでは、論理OR演算子(+)を使用して複数の関係マスクを組み合せることができます。