代码之家  ›  专栏  ›  技术社区  ›  Alex

使用EF Core和NetTopologySuite的半径按区域搜索

  •  0
  • Alex  · 技术社区  · 4 年前

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

    例如 London Business 提供距离lat/lon高达10000米的服务 Southampton Business 提供距离lat/lon 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 }
    

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

    有没有办法按米查询?

    0 回复  |  直到 4 年前
        1
  •  1
  •   Yehor Androsov    4 年前

    使用 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();
            }
        }
    }
    
        2
  •  0
  •   AlwaysLearning    4 年前

    参考: SRID Ignored during client operations :

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

    这是因为NetTopologySuite是一个。JTS拓扑套件的网络端口。参考: NetTopologySuite :

    A.NET地理信息系统解决方案,该解决方案快速可靠。NET平台。NetTopologySuite是JTS拓扑套件提供的所有功能的直接端口:NTS将JTS暴露在“一个”中。NET方式,例如使用属性、索引器等。。。

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