1

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

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

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

有没有按米查询的方法?

4

2 回答 2

1

演示使用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-11-09T21:00:35.910 回答
0

Reference: SRID Ignored during client operations:

NTS ignores SRID values during operations. It assumes a planar coordinate system. This means that if you specify coordinates in terms of longitude and latitude, some client-evaluated values like distance, length, and area will be in degrees, not meters. For more meaningful values, you first need to project the coordinates to another coordinate system using a library like ProjNet4GeoAPI before calculating these values.

This is because NetTopologySuite is a .NET port of the JTS Topology Suite. Reference: NetTopologySuite:

A .NET GIS solution that is fast and reliable for the .NET platform. NetTopologySuite is a direct-port of all the functionalities offered by JTS Topology Suite: NTS expose JTS in a '.NET way', as example using Properties, Indexers etc...

An excerpt from JTS website explains the capabilities of NTS too: "The JTS Topology Suite is an API for modelling and manipulating 2-dimensional linear geometry. It provides numerous geometric predicates and functions. JTS conforms to the Simple Features Specification for SQL published by the Open GIS Consortium."

于 2020-11-07T23:14:33.180 回答