【问题标题】:Searching by area using radius with EF Core and NetTopologySuite通过 EF Core 和 NetTopologySuite 使用半径按区域搜索
【发布时间】:2021-02-20 05:16:09
【问题描述】:

我正在构建一个应用程序,我需要它能够根据他们的“送货区域”搜索企业

例如,London Business 提供距离纬度/经度最远 10000 米的服务 Southampton Business 提供距离纬度/经度 1000 米的服务

我是其中的一部分,使用 EF Core 和 NetTopologySuite。

我正在使用以下简化代码:

var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);

var londonBusiness = new Business
{
    Name = "London Business",
    Area = geometryFactory.CreatePoint(new Coordinate(-0.127758, 51.507351)),
    AreaRadius = 10000
};

var southamptonBusiness = new Business
{
    Name = "Southampton Business",
    Area = geometryFactory.CreatePoint(new Coordinate(1.4044, 50.9097)),
    AreaRadius = 1000
};

await _dbContext.Businesses.AddAsync(londonBusiness);
await _dbContext.Businesses.AddAsync(southamptonBusiness);
await _dbContext.SaveChangesAsync();


// QUERY

// this is very clsoe to the londonBusiness (a couple of km)
var searchLocation = _geometryFactory.CreatePoint(new Coordinate(-0.142500, 51.539188));

var query = _dbContext
    .Businesses
    .Where(x => x.AreaLocation.Distance(searchLocation) <= x.AreaRadius)
    .ToList()

    // this projection is for debug purposes
    .Select(x => new
    {
        Distance = x.AreaLocation.Distance(searchLocation),
        radius = x.AreaRadius
    });

这将返回以下结果:

{ Name = "London Business", Distance = 0.035084485645370242, Radius = 10000 }
{ Name = "Southampton Business", Distance = 1.6700762713552944, Radius = 1000 }

所以,我认为我的问题在于距离 我显然误解了距离是/相关的。 他们是相对正确的 - 伦敦商务距离远小于南安普敦商务

有没有按米查询的方法?

【问题讨论】:

    标签: c# sql-server entity-framework-core geospatial nettopologysuite


    【解决方案1】:

    参考:SRID Ignored during client operations

    NTS 在操作期间忽略 SRID 值。它假设一个平面坐标系。这意味着,如果您根据经度和纬度指定坐标,一些客户评估的值(例如距离、长度和面积)将以度为单位,而不是米。对于更有意义的值,您首先需要使用 ProjNet4GeoAPI 等库将坐标投影到另一个坐标系,然后再计算这些值。

    这是因为 NetTopologySuite 是 JTS 拓扑套件的 .NET 端口。参考:NetTopologySuite

    一种 .NET GIS 解决方案,对于 .NET 平台来说是快速且可靠的。 NetTopologySuite 是 JTS 拓扑套件提供的所有功能的直接端口:NTS 以“.NET 方式”公开 JTS,例如使用属性、索引器等...

    JTS 网站的摘录也解释了 NTS 的功能:“JTS 拓扑套件是用于建模和操作二维线性几何的 API。它提供了许多几何谓词和函数。JTS 符合 SQL 的简单特征规范由开放 GIS 联盟发布。”

    【讨论】:

    • 我明白了;但是如何在查询时预测这个距离?
    【解决方案2】:

    演示使用ProjNet4GeoAPI 包将过滤值的点映射到投影坐标系,并使用它们计算距离(以米为单位)。

    using GeoAPI.CoordinateSystems.Transformations;
    using Microsoft.EntityFrameworkCore;
    using NetTopologySuite.Geometries;
    using ProjNet.CoordinateSystems;
    using ProjNet.CoordinateSystems.Transformations;
    using System.Diagnostics;
    using System.Linq;
    
    namespace ConsoleApp7
    {
        public class Location
        {
            public int Id { get; set; }
            public Point Point { get; set; }
        }
        public class Business
        {
            public int Id { get; set; }
            public Point Point { get; set; }
            public double Distance { get; set; }
        }
        public class MyDbContext : DbContext
        {
            public DbSet<Location> Locations { get; set; }
            public DbSet<Business> Businesses { get; set; }
            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {
                optionsBuilder.UseSqlServer("Server=DESKTOP-5PVJ0I5;Database=geog;Integrated Security=true;", 
                    options => options.UseNetTopologySuite());
    
                base.OnConfiguring(optionsBuilder);
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                CoordinateTransformationFactory ctfac = new CoordinateTransformationFactory();
    
                var from = GeographicCoordinateSystem.WGS84;
                var to = ProjectedCoordinateSystem.WGS84_UTM(30, true);
    
                // convert points from one coordinate system to another
                ICoordinateTransformation trans = ctfac.CreateFromCoordinateSystems(from, to);
    
                var businessCoordinate = new GeoAPI.Geometries.Coordinate(-0.127758, 51.507351);
                var searchLocationCoordinate = new GeoAPI.Geometries.Coordinate(-0.142500, 51.539188);
    
                var mathTransform = trans.MathTransform;
                var businessLocation = mathTransform.Transform(businessCoordinate);
                var searchLocation = mathTransform.Transform(searchLocationCoordinate);
    
                // calculate distance in meters
                var dist = businessLocation.Distance(searchLocation); // 3687m
    
    
                // arrange db
                var dbContext = new MyDbContext();
                dbContext.Database.Migrate();
    
                var location = new Location()
                {
                    Point = new Point(searchLocationCoordinate.X, searchLocationCoordinate.Y) {  SRID = 4326, }
                };
    
                // one business has radius to include location point
                var businessWithLocationInRadius = new Business()
                {
                    Distance = 4000,
                    Point = new Point(businessCoordinate.X, businessCoordinate.Y) { SRID = 4326, }
                };
    
                // and this one has too low range
                var businessWithLocationNOTInRadius = new Business()
                {
                    Distance = 3500,
                    Point = new Point(businessCoordinate.X, businessCoordinate.Y) { SRID = 4326, }
                };
    
                dbContext.Add(location);
                dbContext.Add(businessWithLocationInRadius);
                dbContext.Add(businessWithLocationNOTInRadius);
    
                dbContext.SaveChanges();
    
                var query = dbContext
                    .Businesses
                    .Where(x => x.Point.Distance(location.Point) <= x.Distance)
                    .ToList()
    
                    .Select(x => new
                    {
                        Distance = searchLocation
                            .Distance(mathTransform.Transform(new GeoAPI.Geometries.Coordinate(x.Point.X, x.Point.Y))), // 3687m
                        Radius = x.Distance
                    })
                    
                    .ToList();
    
                Debugger.Break();
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2020-04-11
      • 1970-01-01
      • 2021-11-04
      • 2016-09-06
      • 2014-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多