使用 EF Core 和 NetTopologySuite 使用半径按区域搜索
Searching by area using radius with EF Core and NetTopologySuite
我正在构建一个应用程序,我需要它能够根据他们的 'delivery area'
搜索商家
例如,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 }
所以,我认为我的问题在于距离
我显然误解了距离是什么/与什么有关。
它们相对正确 - 伦敦商业距离远小于南安普顿商业距离
有没有按米查询的方法?
参考: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.
这是因为 NetTopologySuite 是 JTS 拓扑套件的 .NET 端口。参考: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."
演示使用 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();
}
}
}
我正在构建一个应用程序,我需要它能够根据他们的 'delivery area'
搜索商家例如,London Business
提供距 lat/lon 最远 10000 米的服务
Southampton Business
在距 lat/lon
我参与其中,使用 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 }
所以,我认为我的问题在于距离 我显然误解了距离是什么/与什么有关。 它们相对正确 - 伦敦商业距离远小于南安普顿商业距离
有没有按米查询的方法?
参考: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.
这是因为 NetTopologySuite 是 JTS 拓扑套件的 .NET 端口。参考: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."
演示使用 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();
}
}
}