Переформатировать полигоны SQLGeography в JSON - PullRequest
7 голосов
/ 28 июня 2011

Я создаю веб-службу, которая обслуживает данные о географических границах в формате JSON.

Географические данные хранятся в базе данных SQL Server 2008 R2 с использованием типа географического объекта в таблице.Я использую метод [ColumnName].ToString() для возврата данных многоугольника в виде текста.

Пример вывода:

POLYGON ((-6.1646509904325884 56.435153006374627, ... -6.1606079906751 56.4338050060666))

MULTIPOLYGON (((-6.1646509904325884 56.435153006374627 0 0, ... -6.1606079906751 56.4338050060666 0 0)))

Географические определения могут принимать форму либо массива длинных / длинных пар, определяющих многоугольникили в случае нескольких определений - массив или многоугольники (multipolygon).

У меня есть следующее регулярное выражение, которое преобразует выходные данные в объекты JSON, содержащиеся в многомерных массивах в зависимости от выходных данных.

Regex latlngMatch = new Regex(@"(-?[0-9]{1}\.\d*)\s(\d{2}.\d*)(?:\s0\s0,?)?", RegexOptions.Compiled);

    private string ConvertPolysToJson(string polysIn)
    {
        return this.latlngMatch.Replace(polysIn.Remove(0, polysIn.IndexOf("(")) // remove POLYGON or MULTIPOLYGON
                                               .Replace("(", "[")  // convert to JSON array syntax
                                               .Replace(")", "]"), // same as above
                                               "{lng:$1,lat:$2},"); // reformat lat/lng pairs to JSON objects
    }

Это на самом деле работает довольно хорошо и преобразует вывод БД в JSON на лету в ответ на вызов операции.

Однако я не являюсь мастером регулярных выражений, и звонки на String.Replace() также кажутся мне неэффективными.

У кого-нибудь есть какие-либо предложения / комментарии по поводу эффективности этого?

Ответы [ 5 ]

10 голосов
/ 11 августа 2011

Опять же, просто чтобы закрыть это, я отвечу на свой собственный вопрос с помощью решения, которое я использую.

Этот метод берет вывод из вызова ToString() в MS SQL Geography Type.Если возвращаемая строка содержит данные многоугольника, созданные из точек GPS, этот метод будет анализировать и переформатировать его в строку JSON.

public static class PolyConverter
{
    static Regex latlngMatch = new Regex(@"(-?\d{1,2}\.\dE-\d+|-?\d{1,2}\.?\d*)\s(-?\d{1,2}\.\dE-\d+|-?\d{1,2}\.?\d*)\s?0?\s?0?,?", RegexOptions.Compiled);
    static Regex reformat = new Regex(@"\[,", RegexOptions.Compiled);

    public static string ConvertPolysToJson(string polysIn)
    {
        var formatted = reformat.Replace(
                        latlngMatch.Replace(
                        polysIn.Remove(0, polysIn.IndexOf("(")), ",{lng:$1,lat:$2}")
                        .Replace("(", "[")
                        .Replace(")", "]"), "[");

        if (polysIn.Contains("MULTIPOLYGON"))
        {
            formatted = formatted.Replace("[[", "[")
                                 .Replace("]]", "]")
                                 .Replace("[[[", "[[")
                                 .Replace("]]]", "]]");
        }

        return formatted;
    }
}

Это относится к моей заявке, но может быть полезно для кого-то и даже может создатьлучшая реализация.

6 голосов
/ 14 февраля 2015

Для преобразования из WKT в GeoJson вы можете использовать NetTopologySuite из nuget.Добавить NetTopologySuite и NetTopologySuite.IO.GeoJSON

var wkt = "POLYGON ((10 20, 30 40, 50 60, 10 20))";
var wktReader = new NetTopologySuite.IO.WKTReader();
var geom = wktReader.Read(wkt);
var feature = new NetTopologySuite.Features.Feature(geom, new NetTopologySuite.Features.AttributesTable());
var featureCollection = new NetTopologySuite.Features.FeatureCollection();
featureCollection.Add(feature);
var sb = new StringBuilder();
var serializer = new NetTopologySuite.IO.GeoJsonSerializer();
serializer.Formatting = Newtonsoft.Json.Formatting.Indented;
using (var sw = new StringWriter(sb))
{
    serializer.Serialize(sw, featureCollection);
}
var result = sb.ToString();

Вывод:

{
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [
              10.0,
              20.0
            ],
            [
              30.0,
              40.0
            ],
            [
              50.0,
              60.0
            ],
            [
              10.0,
              20.0
            ]
          ]
        ]
      },
      "properties": {}
    }
  ],
  "type": "FeatureCollection"
}
5 голосов
/ 20 августа 2014

Чтобы ответить на ваш вопрос об эффективности, в данном конкретном случае я не думаю, что Заменить против RegEx будет такой большой разницей. Все, что мы действительно меняем, - это некоторые скобки и запятые. Лично я предпочитаю делать вещи в TSQL для веб-приложений, потому что я могу перенести вычислительную работу на SQL Server вместо веб-сервера. В моем случае у меня есть много данных, которые я генерирую для карты, и поэтому не хочу перегружать веб-сервер большим количеством преобразований данных. Кроме того, для повышения производительности я обычно отдаю больше мощности SQL-серверу, чем веб-серверу, поэтому, даже если между этими двумя функциями есть некоторая разница, если Заменить менее эффективен, он по крайней мере обрабатывается сервер с гораздо большим количеством ресурсов. В общем, я хочу, чтобы мой веб-сервер обрабатывал соединения с клиентами, а мой SQL-сервер обрабатывал вычисления данных. Это также сохраняет мои скрипты веб-сервера чистыми и эффективными. Поэтому я предлагаю следующее:

Написать функцию Scalar TSQL в вашей базе данных. Он использует функцию SQL REPLACE и является довольно грубой силой, но он работает действительно хорошо. Эта функция может использоваться непосредственно в операторе SELECT или для создания вычисляемых столбцов в таблице, если вы действительно хотите упростить код веб-сервера. В настоящее время этот пример поддерживает только POINT, POLYGON и MULTIPOLYGON и предоставляет элемент JSON "geometry" для формата geoJSON.

Скалярная функция GetGeoJSON

CREATE FUNCTION GetGeoJSON (@geo geography) /*this is your geography shape*/
RETURNS varchar(max)
WITH SCHEMABINDING /*this tells SQL SERVER that it is deterministic (helpful if you use it in a calculated column)*/
AS
BEGIN
/* Declare the return variable here*/
DECLARE @Result varchar(max)

/*Build JSON "geometry" element for geoJSON*/

SELECT  @Result = '"geometry":{' +
    CASE @geo.STGeometryType()
        WHEN 'POINT' THEN
            '"type": "Point","coordinates":' +
            REPLACE(REPLACE(REPLACE(REPLACE(@geo.ToString(),'POINT ',''),'(','['),')',']'),' ',',')
        WHEN 'POLYGON' THEN 
            '"type": "Polygon","coordinates":' +
            '[' + REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@geo.ToString(),'POLYGON ',''),'(','['),')',']'),'], ',']],['),', ','],['),' ',',') + ']'
        WHEN 'MULTIPOLYGON' THEN 
            '"type": "MultiPolygon","coordinates":' +
            '[' + REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@geo.ToString(),'MULTIPOLYGON ',''),'(','['),')',']'),'], ',']],['),', ','],['),' ',',') + ']'
    ELSE NULL
    END
    +'}'

    /* Return the result of the function*/
    RETURN @Result

END

Далее, используйте вашу GetGeoJSON функцию в вашем выражении SELECT, например:

SELECT dbo.GetGeoJSON([COLUMN]) as Geometry From [TABLE]

Я надеюсь, что это даст некоторое понимание и поможет другим, ищущим методологию, удачи!

1 голос
/ 28 февраля 2016

Метод, изложенный в ответе Джеймса, прекрасно работает.Но недавно я обнаружил ошибку при преобразовании WKT, где Долгота имела значение более 99.

Я изменил регулярное выражение:

@"(-?\d{1,2}\.\dE-\d+|-?\d{1,3}\.?\d*)\s(-?\d{1,2}\.\dE-\d+|-?\d{1,2}\.?\d*)\s?0?\s?0?,?"

Обратите внимание, что второе «2» было изменено на«3» для увеличения долготы до 180.

1 голос
/ 28 июня 2011

Строки неизменны в .net, поэтому при замене некоторых вы создаете отредактированную копию предыдущей строки. Это не так критично для производительности, как для использования памяти.

Посмотрите на JSON.net

Или используйте StringBuilder для правильной генерации.

StringBuilder sb = new StringBuilder();
sb.AppendFormat();
...