c# - 通过 EF Core 和 NetTopologySuite 使用半径按区域搜索

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

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

例如,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 }

所以,我认为我的问题在于距离 我显然误解了距离是什么/与什么相关。 他们是相对正确的 - 伦敦商业距离比南安普顿商业距离小得多

有没有办法按米查询?

最佳答案

演示使用 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();
        }
    }
}

关于c# - 通过 EF Core 和 NetTopologySuite 使用半径按区域搜索,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64732110/

相关文章:

c# - EF Code First - 如何使用 Include 扩展方法过滤相关对象

c# - 带有 XML 的 DataGridView - "Merging"子记录

sql - 什么是索引以及如何使用它们来优化数据库中的查询?

entity-framework - 不能确定 child /受抚养人之间的一对一关系

C# 使用预先创建的对象调用 Powershell

c# - UWP 从字符串写入(和读取)文件夹中的文件

mysql - 从 SQL Server(ODBC 连接)使用 MySQL 作为链接服务器时查询缓慢

c# - 查询结果超过1000条时Dapper抛出System.Data.SqlClient.SqlException

c# - EF Core 在迁移 (UP) 时将列数据从表移动到另一个表

.net - 完整的 .Net 中的 Entity Framework 核心?