web-dev-qa-db-ja.com

Entity Framework Core 3.1 with NetTopologySuite.Geometries.Point:SqlException:指定された値はデータタイプgeographyの有効なインスタンスではありません

次のようなモデルがあります。

public class Facility
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public NetTopologySuite.Geometries.Point Location { get; set; }
}

ポイントを追加するためのテストコード:

var testFacility = new Facility();
testFacility.Location = new NetTopologySuite.Geometries.Point(13.003725d, 55.604870d) { SRID = 3857 };

//Other values tested with the same error error

//testFacility.Location = new NetTopologySuite.Geometries.Point(13.003725d, 55.604870d);

//testFacility.Location = new NetTopologySuite.Geometries.Point(55.604870d, 13.003725d);

//var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 3857);
//var currentLocation = geometryFactory.CreatePoint(new Coordinate(13.003725d, 55.604870d));
//testFacility.Location = currentLocation;

db.Facilities.Add(testFacility);
//Exception on Save
db.SaveChanges();

次のNuGet、バージョン3.1.0を使用しています

Microsoft.AspNetCore.Identity.EntityFrameworkCore
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools
Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite

保存時に取得する例外は次のとおりです。

SqlException:着信表形式データストリーム(TDS)リモートプロシージャコール(RPC)プロトコルストリームが正しくありません。パラメーター7( "@ p6"):指定された値は、データタイプgeographyの有効なインスタンスではありません。ソースデータで無効な値を確認してください。無効な値の例は、位取りが精度よりも大きい数値型のデータです。

すべてのドキュメントによると、経度はX、緯度はYである必要があるため、問題はないと思います。念のために座標を逆にしてみましたが、試した例でわかるように同じエラーが発生しました。

https://docs.Microsoft.com/en-us/ef/core/modeling/spatial

緯度= Y長い= X

https://gis.stackexchange.com/a/68856/71364

私は間違っているように見える明らかなものを見つけることができません。 Optionsbuilderがセットアップされ、Entity Framework 6のgeographyで非常にうまく機能するデータタイプDbGeographyでテーブルが作成されます。

var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=TestDb;Trusted_Connection=True;MultipleActiveResultSets=true",
    x => x.UseNetTopologySuite());

var db = new ApplicationDbContext(optionsBuilder.Options);

単一のPointを処理する特定のケースはなく、SQLサーバーのドキュメントで確認できます。

https://docs.Microsoft.com/en-us/ef/core/modeling/spatial#sql-server

保存している座標はGoogleマップからのものなので、EPSG 3857が使用されます。

https://gis.stackexchange.com/questions/48949/epsg-3857-or-4326-for-googlemaps-openstreetmap-and-leaflet

何が欠けていますか?

3
Ogglas

[〜#〜] tldr [〜#〜]

SRIDはSQL Serverに存在しませんsys.spatial_reference_systems

enter image description here

4326のように存在するものに変更すると、機能します。

enter image description here

select *
from sys.spatial_reference_systems
where spatial_reference_id = '4326'

長い答え:

Google Maps APIはEPSG 3857を使用しますが、Google Maps WebアプリケーションはEPSG 4326を使用します

https://developers.google.com/maps/documentation/javascript/markers

https://www.google.com/maps/@55.604933,13.003662,14z

enter image description here

したがって、Google Maps Webアプリケーションからのポイントは、次のように作成して保存する必要があります。

var testFacility = new Facility();
testFacility.Location = new NetTopologySuite.Geometries.Point(13.003725d, 55.604870d) { SRID = 4326 };
db.Facilities.Add(testFacility);
db.SaveChanges();

ただし、EPSG 4326座標をEPSG 3857座標系に投影するのは少し難しい作業でした。マイクロソフトではProjNet4GeoAPIの使用を推奨しているので、それを使用することにしました。

https://docs.Microsoft.com/en-us/ef/core/modeling/spatial#srid-ignored-during-client-operations

私はそれがここで機能することを確認しました:

http://epsg.io/transform#s_srs=4326&t_srs=3857&x=13.003725&y=55.60487

変換の例:

var x = 13.003725d;
var y = 55.604870d;

var epsg3857ProjectedCoordinateSystem = ProjNet.CoordinateSystems.ProjectedCoordinateSystem.WebMercator;
var epsg4326GeographicCoordinateSystem = ProjNet.CoordinateSystems.GeographicCoordinateSystem.WGS84;

var coordinateTransformationFactory = new ProjNet.CoordinateSystems.Transformations.CoordinateTransformationFactory();
var coordinateTransformation = coordinateTransformationFactory.CreateFromCoordinateSystems(epsg4326GeographicCoordinateSystem, epsg3857ProjectedCoordinateSystem);

var epsg4326Coordinate = new GeoAPI.Geometries.Coordinate(x, y);

var epsg3857Coordinate = coordinateTransformation.MathTransform.Transform(epsg4326Coordinate);

完全なサンプルプログラム:

実行するには:

  • NuGets をインストールします
    • 次のNuGetはバージョン3.1です:
      • Microsoft.EntityFrameworkCore
      • Microsoft.EntityFrameworkCore.SqlServer
      • Microsoft.EntityFrameworkCore.Tools
      • Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite
    • ProjNET4GeoAPI
  • Add-Migration InitialCreate
  • データベースを更新する

コード:

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using NetTopologySuite;
using NetTopologySuite.Geometries;
using ProjNet.CoordinateSystems;
using ProjNet.CoordinateSystems.Transformations;
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace TestConsoleAppEFGeo
{
    public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
    {
        public ApplicationDbContext CreateDbContext(string[] args)
        {
            var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
            optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=TestApp;Trusted_Connection=True;MultipleActiveResultSets=true",
                x => x.UseNetTopologySuite());

            return new ApplicationDbContext(optionsBuilder.Options);
        }
    }

    public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }

        public virtual DbSet<Facility> Facilities { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
        }
    }

    public class Facility
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int Id { get; set; }

        public NetTopologySuite.Geometries.Point Location { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var applicationDbContextFactory = new ApplicationDbContextFactory();
            var db = applicationDbContextFactory.CreateDbContext(null);

            var x = 13.003725d;
            var y = 55.604870d;
            var srid = 4326;

            if (!db.Facilities.AnyAsync(x => x.Id == 1).Result)
            {
                var testFacility = new Facility();
                var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid);
                var currentLocation = geometryFactory.CreatePoint(new NetTopologySuite.Geometries.Coordinate(x, y));
                testFacility.Id = 1;
                testFacility.Location = currentLocation;

                var testFacility2 = new Facility();
                testFacility2.Id = 2;
                testFacility2.Location = new Point(x, y) { SRID = srid };
                db.Facilities.Add(testFacility);
                db.Facilities.Add(testFacility2);

                //Will throw an exception
                //var testFacility3 = new Facility();
                //testFacility3.Id = 3;
                //testFacility3.Location = new Point(1447568.0454157612d, 7480155.2276327936d) { SRID = 3857 };
                //db.Facilities.Add(testFacility3);

                db.SaveChanges();
            }

            var facility1 = db.Facilities.FirstAsync(x => x.Id == 1).Result;
            var facility2 = db.Facilities.FirstAsync(x => x.Id == 2).Result;

            if(facility1.Location == facility2.Location)
            {
                Console.WriteLine("facility1.Location is equal to facility2.Location");
            }
            else
            {
                Console.WriteLine("facility1.Location is NOT equal to facility2.Location");
            }

            //Test conversion
            //Show coordinate: http://epsg.io/map#srs=4326&x=13.003725&y=55.604870&z=14&layer=streets
            //Conversion: http://epsg.io/transform#s_srs=4326&t_srs=3857&x=13.0037250&y=55.6048700
            //Google Maps - https://www.google.se/maps shows EPSG:4326 when viewing a location
            //https://epsg.io/3857 - Google Maps API is EPSG:3857 however
            //Example: https://developers.google.com/maps/documentation/javascript/markers

            var epsg3857ProjectedCoordinateSystem = ProjectedCoordinateSystem.WebMercator;
            var epsg4326GeographicCoordinateSystem = GeographicCoordinateSystem.WGS84;

            var coordinateTransformationFactory = new CoordinateTransformationFactory();
            var coordinateTransformation = coordinateTransformationFactory.CreateFromCoordinateSystems(epsg4326GeographicCoordinateSystem, epsg3857ProjectedCoordinateSystem);

            var epsg4326Coordinate = new GeoAPI.Geometries.Coordinate(facility1.Location.Coordinate.X, facility1.Location.Coordinate.Y);

            var epsg3857Coordinate = coordinateTransformation.MathTransform.Transform(epsg4326Coordinate);

        }
    }
}

詳細はこちら:

https://github.com/dotnet/efcore/issues/19416

1
Ogglas

LatとLongが正しい位置にあり、有効なSRIDを提供していることを確認してください。

0
Ross