我正在开发一个GPS项目,我允许用户绘制(使用谷歌)一个形状(矩形,多边形,圆形,线条) 并将其保存在数据库中
现在我的要求是确定表中是否有任何一个给定点 并返回id返回0。
技巧是保存多个形状(类型列确定形状类型2是矩形 - 4是多边形)
我尝试了什么
注意:我将从C#中使用它并将点作为参数传递
答案 0 :(得分:1)
首先,我建议您将多边形保存为空间数据类型
在JavaScript中,您可以轻松地将绘制的多边形转换为Lat / Lng的List / Array
对于圆,您需要半径和圆的中心纬度/ lng
类似的东西:
var circleCenter = null;
var circlePolygon = null;
// set draw options on google map
mapObject.setOptions({ draggableCursor: 'crosshair' });
var drawGeofenceEvent = google.maps.event.addListener(mapObject, 'click', event => {
var myLatLng = event.latLng;
// create a marker for center
circleCenter = new google.maps.Marker({
map: mapObject,
position: myLatLng,
draggable: true
});
// create a temp circle polygon to plot on map
circlePolygon = new google.maps.Circle({
radius: 150,
editable: true,
draggable: true
});
circlePolygon.setMap(mapObject);
circlePolygon.bindTo('center', circleCenter, 'position');
google.maps.event.removeListener(drawGeofenceEvent);
});
// after drawing the geofence you want to get the lat/longs along the circumference
var circleCoordinates = this.googleCircleToPolylineArray(this.circleCenter.position, this.circlePolygon.getRadius() * 0.000621371, 1);
// copied from Stackoverflow - will post the link when I find it
googleCircleToPolylineArray(point, radius, dir) {
var lat = point.lat();
var lng = point.lng();
var d2r = Math.PI / 180; // degrees to radians
var r2d = 180 / Math.PI; // radians to degrees
var earthsradius = 3963; // 3963 is the radius of the earth in miles
// ReSharper disable once AssignedValueIsNeverUsed
var points = 32;
//var radius = 1; // radius in miles
// find the raidus in lat/lon
var rlat = (radius / earthsradius) * r2d;
var rlng = rlat / Math.cos(lat * d2r);
var extp = new Array();
for (var i = 0; i < points + 1; i++) {
var theta = Math.PI * (i / (points / 2));
var ex = lng + (rlng * Math.cos(theta)); // center a + radius x * cos(theta)
var ey = lat + (rlat * Math.sin(theta)); // center b + radius y * sin(theta)
extp.push({ Latitude: ey, Longitude: ex }); // new google.maps.LatLng(ey, ex)
}
return extp;
};
// then you would POST the circleCoordinates to the Server to save as a spatial data type
然后在服务器上你将坐标转换为一个众所周知的文本(WKT)Wikipedia - 对于Polygon使用以下,当我说Polygon时我的意思是圆形,矩形,三角形,六边形等任何具有相同起点和终点的东西(已关闭)。
对于您要使用LINESTRING
WKT
public static DbGeography CreatePolygon(Coordinate[] latLongs)
{
//POLYGON ((73.232821 34.191819,73.233755 34.191942,73.233653 34.192358,73.232843 34.192246,73.23269 34.191969,73.232821 34.191819))
var polyString = "";
foreach (var point in latLongs)
{
polyString += point.Longitude + " " + point.Latitude + ",";
}
polyString = polyString.TrimEnd(',');
polyString = string.Format("POLYGON(({0}))", polyString);
var polygonFromText = DbGeography.PolygonFromText(polyString, DbGeography.DefaultCoordinateSystemId);
return polygonFromText;
}
从DbGeography反向到坐标
public static List<Coordinate> PolygonToGeoPoints(DbGeography sptGeofenceArea)
{
var points = new List<Coordinate>();
string polygonText = sptGeofenceArea.ProviderValue.ToString();
polygonText = polygonText.Replace("POLYGON", "");
polygonText = polygonText.Replace("(", "").Replace(")", "").Trim();
var polPoints = polygonText.Split(',');
foreach (var point in polPoints)
{
var latlong = point.Trim().Split(' ');
points.Add(new Coordinate { Latitude = double.Parse(latlong[1]), Longitude = double.Parse(latlong[0]) });
}
return points;
}
我使用Entity Framework和DbGeography类型,我将多边形保存为数据库中的空间数据类型。
您可以编辑上面的代码以返回Well-Known-Text(WKT)而不是DbGeography数据类型。
然后,一旦空间数据类型存储在数据库中,您只需要将要检查的点转换为空间数据类型或WKT
WKT - SQL VERSION
DECLARE @point GEOGRAPHY;
SET @point = geography::Point(47.653, -122.358, 4326)
Select
*
From Polygons
where POLYGON.STIntersects(@point) = 1
空间类型 - 实体框架
DbGeography point;
dbCOntext.Polygons.Where(s => point.Intersects(s.Polygon)).ToList();
修改强>
创建多边形类型时会出现一个常见问题 - 点必须按特定顺序排列,否则你会有一个多边形覆盖整个地球,除了所需的多边形 - 要克服你可以使用以下内容 -
#region
//https://www.exceptionnotfound.net/fixing-sql-server-spatial-not-a-valid-instance-of-geography-errors-in-c-sharp/
private static DbGeography CreatePolygon(string wellKnownText)
{
//First, get the area defined by the well-known text using left-hand rule
var sqlGeography = SqlGeography.STGeomFromText(new SqlChars(wellKnownText), DbGeography.DefaultCoordinateSystemId);
if(!sqlGeography.STIsValid())
throw new Exception("Invalid polygon, please draw the polygon again.");
sqlGeography = sqlGeography.MakeValid();
//Now get the inversion of the above area
var invertedSqlGeography = sqlGeography.ReorientObject();
//Whichever of these is smaller is the enclosed polygon, so we use that one.
if (sqlGeography.STArea() > invertedSqlGeography.STArea())
{
sqlGeography = invertedSqlGeography;
}
return DbSpatialServices.Default.GeographyFromProviderValue(sqlGeography);
}
#endregion
public static DbGeography CreatePolygon(Coordinate[] latLongs)
{
//POLYGON ((73.232821 34.191819,73.233755 34.191942,73.233653 34.192358,73.232843 34.192246,73.23269 34.191969,73.232821 34.191819))
var polyString = "";
foreach (var point in latLongs)
{
polyString += point.Longitude + " " + point.Latitude + ",";
}
polyString = polyString.TrimEnd(',');
polyString = string.Format("POLYGON(({0}))", polyString);
var dbGeographyPolygon = CreatePolygon(polyString);
return dbGeographyPolygon;
//var polygonFromText = DbGeography.PolygonFromText(polyString, DbGeography.DefaultCoordinateSystemId);
//return polygonFromText;
}