Переформатировать полигоны SQLGeography в JSON

125
11

Я создаю веб-службу, которая обслуживает географические граничные данные в формате 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)))


Географические определения могут принимать форму либо массива пар lat/long, определяющих многоугольник, либо в случае нескольких определений, массива или многоугольников (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
}


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


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


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

спросил(а) 2021-01-25T16:54:29+03:00 5 месяцев назад
1
Решение
132

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


Этот метод выводит результат вызова 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;
}
}


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

ответил(а) 2021-01-25T16:54:29+03:00 5 месяцев назад
98

Чтобы ответить на вопрос об эффективности, для этого конкретного случая я не думаю, что Replace vs RegEx будет большой разницы. Все, что мы действительно меняем, это скобки и запятые. Лично я предпочитаю делать что-то в TSQL для веб-приложений, потому что я могу разгрузить вычислительную работу на SQL Server вместо Web-сервера. В моем случае у меня есть много данных, которые я генерирую для карты, и поэтому не хочу бояться веб-сервера с большим количеством преобразований данных. Кроме того, для производительности я обычно добавляю больше мощности на сервер 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]

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

ответил(а) 2021-01-25T16:54:29+03:00 5 месяцев назад
89

Для преобразования из 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"
}

ответил(а) 2021-01-25T16:54:29+03:00 5 месяцев назад
63

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


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


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


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

ответил(а) 2021-01-25T16:54:29+03:00 5 месяцев назад
63

Метод, описанный в ответе Джеймса, отлично работает. Но я недавно обнаружил ошибку при преобразовании 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.

ответил(а) 2021-01-25T16:54:29+03:00 5 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема