2014-11-28 1 views
2

У меня есть этот код, он принимает геометрию стран, а также множество точек, а затем возвращает только точки в этих странах:Как ускорить преобразование lat-long в SqlGeometry?

public static IEnumerable<Point> RemovePointsOutsideBorders(IEnumerable<Point> points, IEnumerable<Country> countries) 
{ 
    var cc = new List<Point>(); 

    var SPAT_REF_ID = 4326; 
    foreach (var p in points) 
    { 
      var validText = new SqlChars(new SqlString(string.Format("POINT({0} {1})", p.Longitude, p.Latitude))); 
      var geoPoint = SqlGeometry.STPointFromText(validText, SPAT_REF_ID); 

     foreach (var c in countries) 
     { 
      if(c.Polygon.STIntersects(geoPoint)) 
      { 
       cc.Add(p); 
       break; 
      } 
     } 
    } 

    return cc; 
} 

Текущий его довольно медленно, Есть около 4000 точек, с двойные LAT/длинные значений, преобразование, что в SqlGeometry медленно (занимает около 25 секунд, - мне нужно, чтобы это было возможно, вплоть до второго или два):

var s = new SqlChars(new SqlString(string.Format("POINT({0} {1})", p.Longitude, p.Latitude))); 
var pGeo = SqlGeometry.STPointFromText(s, SPAT_REF_ID); 

Это сделано только потому, что SqlGeometry .Point принимает x, y вместо lat, long ... любые подсказки о том, как я могу ускорить это?

Я уже знаю, что SqlGeometry (c.Polygon) может быть уменьшен, чтобы ускорить работу, однако я не могу это контролировать. То, что я получаю, это способ ускорить преобразование с lat/long в точку SqlGeometry.

+0

Вы уверены, что вы хотите иметь дело с 'SqlGeometry' и не' SqlGeorgaphy'? Эти два существенно отличаются в некоторых важных аспектах, особенно при работе с точками, которые находятся далеко (например, две страны). –

+0

@MatthewHaugen Я ограничен Geometry, так как это то, что у нас есть в базе данных, любая операция, чтобы превратить это в SqlGeography в C#, была бы еще одной потерей IMO. – sprocket12

+1

@Muhammad преобразует БД в SqlGeography. Любое другое решение только отложит неизбежное, БД ошибочно и нуждается в изменении. –

ответ

2

Это решение, которое я придумал в конце концов, это все, что в половине второго:

public static IEnumerable<Point> RemovePointsOutsideBorders(IEnumerable<Point> points, IEnumerable<Country> countries) 
{ 
    // join all the country polygons into one (make a stamp) 
    var allCountryPolygon = countries.Select(x => x.Polygon).UnionAll(); 

    // make a single geometry shape from our evenly spaced extent points (cookie dough) 
    var pointsGeo = PointsToGeometry(points); 

    // do an intersect (stamp country shape over extent based geometry) 
    var cookieOfPoints = pointsGeo.STIntersection(allCountryPolygon); 

    // how many points left inside? pick them all back out 
    for (int n = 1; n <= cookieOfPoints.STNumPoints(); n++) 
    { 
     var insidePoint = cookieOfPoints.STPointN(n); 
     yield return new Point 
     { 
      Longitude = insidePoint.STX.Value, 
      Latitude = insidePoint.STY.Value 
     }; 
    } 
} 

public static SqlGeometry PointsToGeometry(IEnumerable<Point> points) 
{ 
    var bld = new SqlGeometryBuilder(); 
    bld.SetSrid(4326); 
    bld.BeginGeometry(OpenGisGeometryType.MultiPoint); 

    foreach (var p in points) 
    { 
     bld.BeginGeometry(OpenGisGeometryType.Point); 
     bld.BeginFigure(p.Longitude, p.Latitude); 
     bld.EndFigure(); 
     bld.EndGeometry(); 
    } 

    bld.EndGeometry(); 
    return bld.ConstructedGeometry; 
} 

public static class ExtensionMethods 
{ 
    /// <summary> 
    /// Joins many geometries into one 
    /// </summary> 
    /// <param name="geometries">geometries to join</param> 
    /// <returns>composite geometry</returns> 
    public static SqlGeometry UnionAll(this IEnumerable<SqlGeometry> geometries) 
    { 
     var compositeGeometry = geometries.First(); 
     foreach (var g in geometries.Skip(1)) 
     { 
      compositeGeometry = compositeGeometry.STUnion(g); 
     } 
     return compositeGeometry; 
    } 
}