空间数据Spatial Data

备注

此功能是在 EF Core 2.2 中添加的。

空间数据表示对象的物理位置和形状。 许多数据库提供对此类数据的支持,以便能够与其他数据一起进行索引和查询。 常见方案包括从位置在给定距离内查询对象,或选择其边框包含给定位置的对象。 EF Core 支持使用NetTopologySuite空间库映射到空间数据类型。

安装Installing

若要在 EF Core 中使用空间数据,需要安装相应的支持 NuGet 包。 需要安装哪个包取决于所使用的提供程序。

EF Core 提供程序空间 NuGet 包
Microsoft.EntityFrameworkCore.SqlServerMicrosoft.entityframeworkcore. NetTopologySuite
Microsoft.EntityFrameworkCore.SqliteMicrosoft.entityframeworkcore. NetTopologySuite
Microsoft.EntityFrameworkCore.InMemoryNetTopologySuite
Npgsql.EntityFrameworkCore.PostgreSQLNpgsql. Microsoft.entityframeworkcore. PostgreSQL. NetTopologySuite

反向工程Reverse engineering

空间 NuGet 包还启用具有空间属性的反向工程模型,但需要在运行或之前安装包 Scaffold-DbContext dotnet ef dbcontext scaffold 。 否则,你将收到有关找不到列的类型映射的警告,将跳过这些列。

NetTopologySuite (NTS)NetTopologySuite (NTS)

NetTopologySuite 是用于 .NET 的空间库。 EF Core 使用模型中的 NTS 类型启用映射到数据库中的空间数据类型。

若要通过 NTS 启用到空间类型的映射,请在提供程序的 DbContext 选项生成器上调用 UseNetTopologySuite 方法。 例如,对于 SQL Server,你应将其称为。

  1. optionsBuilder.UseSqlServer(
  2. @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=WideWorldImporters",
  3. x => x.UseNetTopologySuite());

有几种空间数据类型。 使用哪种类型取决于您想要允许的形状的类型。 下面是可用于模型中的属性的 NTS 类型的层次结构。 它们位于 NetTopologySuite.Geometries 命名空间内。

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

警告

NTS 不支持 CircularString、CompoundCurve 和 CurePolygon。

使用基本几何图形类型允许属性指定任意类型的形状。

以下实体类可用于映射到广角导入示例数据库中的表。

  1. [Table("Cities", Schema = "Application"))]
  2. class City
  3. {
  4. public int CityID { get; set; }
  5. public string CityName { get; set; }
  6. public Point Location { get; set; }
  7. }
  8. [Table("Countries", Schema = "Application"))]
  9. class Country
  10. {
  11. public int CountryID { get; set; }
  12. public string CountryName { get; set; }
  13. // Database includes both Polygon and MultiPolygon values
  14. public Geometry Border { get; set; }
  15. }

创建值Creating values

您可以使用构造函数来创建 geometry 对象;但是,NTS 建议改为使用几何工厂。 这允许您指定默认的 SRID (坐标使用的空间引用系统),并使您能够控制更高级的任务(例如,在计算过程中使用)和坐标序列(确定可用的坐标-维度和度量值)。

  1. var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
  2. var currentLocation = geometryFactory.CreatePoint(new Coordinate(-122.121512, 47.6739882));

备注

4326指的是 WGS 84,是 GPS 和其他地理系统中使用的标准。

经度和纬度Longitude and Latitude

NTS 中的坐标采用 X 和 Y 值。 若要表示经度和纬度,请将 X 用于经度,将 Y 用于纬度。 请注意,这backwards是从 latitude, longitude 通常会看到这些值的格式反向进行的。

在客户端操作过程中忽略 SRIDSRID Ignored during client operations

NTS 在操作过程中忽略 SRID 值。 它假定为平面坐标系统。 这意味着,如果在经度和纬度方面指定了坐标,则某些客户端计算的值(例如,距离、长度和区域)将为度数,而不是计量。 若要获得更有意义的值,首先需要使用库(如ProjNet4GeoAPI )在计算这些值之前投影到另一个坐标系统的坐标。

如果通过 EF Core 通过 SQL 对操作进行服务器计算,则该结果的单元将由数据库确定。

下面是一个示例,说明如何使用 ProjNet4GeoAPI 来计算两个城市之间的距离。

  1. static class GeometryExtensions
  2. {
  3. static readonly CoordinateSystemServices _coordinateSystemServices
  4. = new CoordinateSystemServices(
  5. new CoordinateSystemFactory(),
  6. new CoordinateTransformationFactory(),
  7. new Dictionary<int, string>
  8. {
  9. // Coordinate systems:
  10. [4326] = GeographicCoordinateSystem.WGS84.WKT,
  11. // This coordinate system covers the area of our data.
  12. // Different data requires a different coordinate system.
  13. [2855] =
  14. @"
  15. PROJCS[""NAD83(HARN) / Washington North"",
  16. GEOGCS[""NAD83(HARN)"",
  17. DATUM[""NAD83_High_Accuracy_Regional_Network"",
  18. SPHEROID[""GRS 1980"",6378137,298.257222101,
  19. AUTHORITY[""EPSG"",""7019""]],
  20. AUTHORITY[""EPSG"",""6152""]],
  21. PRIMEM[""Greenwich"",0,
  22. AUTHORITY[""EPSG"",""8901""]],
  23. UNIT[""degree"",0.01745329251994328,
  24. AUTHORITY[""EPSG"",""9122""]],
  25. AUTHORITY[""EPSG"",""4152""]],
  26. PROJECTION[""Lambert_Conformal_Conic_2SP""],
  27. PARAMETER[""standard_parallel_1"",48.73333333333333],
  28. PARAMETER[""standard_parallel_2"",47.5],
  29. PARAMETER[""latitude_of_origin"",47],
  30. PARAMETER[""central_meridian"",-120.8333333333333],
  31. PARAMETER[""false_easting"",500000],
  32. PARAMETER[""false_northing"",0],
  33. UNIT[""metre"",1,
  34. AUTHORITY[""EPSG"",""9001""]],
  35. AUTHORITY[""EPSG"",""2855""]]
  36. "
  37. });
  38. public static Geometry ProjectTo(this Geometry geometry, int srid)
  39. {
  40. var transformation = _coordinateSystemServices.CreateTransformation(geometry.SRID, srid);
  41. var result = geometry.Copy();
  42. result.Apply(new MathTransformFilter(transformation.MathTransform));
  43. return result;
  44. }
  45. class MathTransformFilter : ICoordinateSequenceFilter
  46. {
  47. readonly MathTransform _transform;
  48. public MathTransformFilter(MathTransform transform)
  49. => _transform = transform;
  50. public bool Done => false;
  51. public bool GeometryChanged => true;
  52. public void Filter(CoordinateSequence seq, int i)
  53. {
  54. var result = _transform.Transform(
  55. new[]
  56. {
  57. seq.GetOrdinate(i, Ordinate.X),
  58. seq.GetOrdinate(i, Ordinate.Y)
  59. });
  60. seq.SetOrdinate(i, Ordinate.X, result[0]);
  61. seq.SetOrdinate(i, Ordinate.Y, result[1]);
  62. }
  63. }
  64. }
  1. var seattle = new Point(-122.333056, 47.609722) { SRID = 4326 };
  2. var redmond = new Point(-122.123889, 47.669444) { SRID = 4326 };
  3. var distance = seattle.ProjectTo(2855).Distance(redmond.ProjectTo(2855));

查询数据Querying Data

在 LINQ 中,可用作数据库函数的 NTS 方法和属性将转换为 SQL。 例如,在以下查询中转换距离和包含方法。 本文末尾的表格显示了不同 EF Core 提供商支持哪些成员。

  1. var nearestCity = db.Cities
  2. .OrderBy(c => c.Location.Distance(currentLocation))
  3. .FirstOrDefault();
  4. var currentCountry = db.Countries
  5. .FirstOrDefault(c => c.Border.Contains(currentLocation));

SQL ServerSQL Server

如果你正在使用 SQL Server,你还应该注意一些其他问题。

地理或几何图形Geography or geometry

默认情况下,空间属性映射到 geography SQL Server 中的列。 若要使用 geometry ,请在模型中配置列类型

地理多边形环Geography polygon rings

当使用 geography 列类型时,SQL Server 会对外部环(或外壳)和内部环(或孔)施加附加要求。 外部环必须逆时针旋转,并顺时针旋转内部环。 NTS 在将值发送到数据库之前对其进行验证。

FullGlobeFullGlobe

在使用列类型时,SQL Server 具有非标准几何类型来表示全地球 geography 。 它还提供了一种方法,用于根据全地球(无外部环)来表示多边形。 NTS 不支持这两种方法。

警告

NTS 不支持基于 FullGlobe 和多边形。

SQLiteSQLite

下面是使用 SQLite 的一些其他信息。

安装 SpatiaLiteInstalling SpatiaLite

在 Windows 上,本机 mod_spatialite 库以 NuGet 包的依赖项的形式分发。 其他平台需要单独安装它。 通常使用软件程序包管理器完成此操作。 例如,可以在 Ubuntu 上使用 APT 和 MacOS 上的 Homebrew。

  1. # Ubuntu
  2. apt-get install libsqlite3-mod-spatialite
  3. # macOS
  4. brew install libspatialite

遗憾的是,较新版本的 PROJ (SpatiaLite 的依赖项)与 EF 的默认SQLitePCLRaw 绑定不兼容。 若要解决此情况,可以创建使用系统 SQLite 库的自定义SQLitePCLRaw 提供程序,也可以安装 SPATIALITE 禁用 PROJ 支持的自定义生成。

  1. curl https://www.gaia-gis.it/gaia-sins/libspatialite-4.3.0a.tar.gz | tar -xz
  2. cd libspatialite-4.3.0a
  3. if [[ `uname -s` == Darwin* ]]; then
  4. # Mac OS requires some minor patching
  5. sed -i "" "s/shrext_cmds='\`test \\.\$module = .yes && echo .so \\|\\| echo \\.dylib\`'/shrext_cmds='.dylib'/g" configure
  6. fi
  7. ./configure --disable-proj
  8. make
  9. make install

配置 SRIDConfiguring SRID

在 SpatiaLite 中,列需要为每个列指定一个 SRID。 默认的 SRID 为 0 。 使用 ForSqliteHasSrid 方法指定其他 SRID。

  1. modelBuilder.Entity<City>().Property(c => c.Location)
  2. .ForSqliteHasSrid(4326);

维度Dimension

类似于 SRID,列的维度(或坐标)也被指定为列的一部分。 默认坐标为 X 和 Y。使用 ForSqliteHasDimension 方法启用其他坐标(Z 和 M)。

  1. modelBuilder.Entity<City>().Property(c => c.Location)
  2. .ForSqliteHasDimension(Ordinates.XYZ);

转换的操作Translated Operations

此表显示每个 EF Core 提供程序将哪些 NTS 成员转换为 SQL。

NetTopologySuiteSQL Server (geometry)SQL Server (geography)SQLiteNpgsql
Geometry
AsBinary ()
AsText ()
Geometry
Geometry (双精度型)
Geometry (double,int)
质心
Geometry。 Contains (Geometry)
ConvexHull ()
CoveredBy (Geometry)
Geometry (Geometry)
几何。交叉(几何)
几何差(几何)
Geometry。维度
不连续(Geometry)
Geometry (Geometry)
Geometry 信封
EqualsExact (Geometry)
EqualsTopologically (Geometry)
GeometryType
GetGeometryN (int)
InteriorPoint
几何交集(Geometry)
几何和交集(Geometry)
IsEmpty
IsSimple
Geometry
IsWithinDistance (Geometry,double)
Geometry。长度
NumGeometries
X.numpoints
OgcGeometryType
Geometry 重叠(Geometry)
PointOnSurface
Geometry (Geometry,string)
Geometry 反向()
SRID
SymmetricDifference (Geometry)
ToBinary ()
ToText ()
几何图形(几何)
Geometry ()
Geometry (Geometry)
几何图形。内(Geometry)
GeometryCollection
GeometryCollection [int]
LineString
LineString 终结点
LineString. GetPointN (int)
LineString. IsClosed
LineString. IsRing
LineString. StartPoint
MultiLineString. IsClosed
点 M
点 X
Point。 Y
点 Z
多边形。 ExteriorRing
GetInteriorRingN (int)
多边形。 NumInteriorRings

其他资源Additional resources