确定点是否在SQL Server 2014内部形状

时间:2017-12-25 10:50:11

标签: sql sql-server sql-server-2014

我正在开发一个GPS项目,我允许用户绘制(使用谷歌)一个形状(矩形,多边形,圆形,线条) 并将其保存在数据库中

SQL Fiddle

现在我的要求是确定表中是否有任何一个给定点 并返回id返回0。

技巧是保存多个形状(类型列确定形状类型2是矩形 - 4是多边形)

我尝试了什么

Example

注意:我将从C#中使用它并将点作为参数传递

1 个答案:

答案 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();

修改

创建多边形类型时会出现一个常见问题 - 点必须按特定顺序排列,否则你会有一个多边形覆盖整个地球,除了所需的多边形 - 要克服你可以使用以下内容 -

LINK

    #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;

    }