将SQLGeography多边形重新格式化为JSON

时间:2011-06-28 12:53:13

标签: c# json geojson sqlgeography wkt

我正在构建一个以JSON格式提供地理边界数据的Web服务。

使用表中的地理类型将地理数据存储在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)))

地理定义可以采用定义多边形的纬度/长度对数组的形式,也可以采用多个定义,数组或多边形(多边形)的形式。

我有以下正则表达式,它根据输出将输出转换为包含在多维数组中的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()的调用对我来说似乎效率低下。

有没有人对此表现有任何建议/意见?

6 个答案:

答案 0 :(得分:10)

再次只是为了关闭这个,我将使用解决方案回答我自己的问题。

此方法从MS SQL ToString()上的Geography Type调用获取输出。 如果返回的字符串包含由GPS点构成的多边形数据,则此方法将解析并将其重新格式化为JSON sting。

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

这特定于我的应用,但可能对某人有用,甚至可能创建更好的实现。

答案 1 :(得分:6)

要从WKT转换为GeoJson,您可以使用nuget的NetTopologySuite。添加NetTopologySuiteNetTopologySuite.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"
}

答案 2 :(得分:5)

要回答有关效率的问题,对于这种特殊情况,我认为替换 RegEx 不会有太大差别。我们真正改变的是一些括号和逗号。就个人而言,我更喜欢在TSQL中为Web应用程序做事,因为我可以将计算工作卸载到SQL Server而不是Web Server上。在我的情况下,我有大量的数据,我为地图生成,因此不希望陷入网络服务器与大量的数据转换。另外,为了提高性能,我通常会在SQL服务器上放置比运行Web服务器更多的功能,因此即使两个功能之间存在一些差异,如果替换效率较低,则至少要处理由具有更多资源的服务器。一般来说,我希望我的Web服务器处理与客户端的连接以及处理数据计算的SQL服务器。这也使我的Web服务器脚本保持清洁和高效。所以我的建议如下:

在数据库中编写标量TSQL函数。这使用SQL REPLACE 函数,有点蛮力,但它表现得非常好。如果您真的想简化Web服务器代码,可以直接在SELECT语句中使用此函数,或在表中创建计算列。目前,此示例仅支持POINT,POLYGON和MULTIPOLYGON,并为geoJSON格式提供“几何”JSON元素。

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]

我希望这能提供一些见解并帮助其他人寻找方法,祝你好运!

答案 3 :(得分:1)

字符串在.net中是不可变的,因此当您替换某些字符串时,您将创建以前字符串的编辑副本。这对于性能和内存使用并不是那么重要。

查看JSON.net

或使用StringBuilder正确生成它。

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

答案 4 :(得分:1)

詹姆斯答案中概述的方法效果很好。但是我最近在转换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.

答案 5 :(得分:1)

用于将空间单元格式化为 GeoJSON 的实用函数如下所示。

DROP FUNCTION IF EXISTS dbo.geometry2json
GO

CREATE FUNCTION dbo.geometry2json( @geo geometry)
RETURNS nvarchar(MAX) AS
BEGIN
RETURN (

'{' +
(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 ',''),'(','['),')',']'),'], ',']],['),', ','],['),' ',',') + ']'
WHEN 'MULTIPOINT' THEN
'"type": "MultiPoint","coordinates":' +
'[' + REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@geo.ToString(),'MULTIPOINT ',''),'(','['),')',']'),'], ',']],['),', ','],['),' ',',') + ']'
WHEN 'LINESTRING' THEN
'"type": "LineString","coordinates":' +
'[' + REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@geo.ToString(),'LINESTRING ',''),'(','['),')',']'),'], ',']],['),', ','],['),' ',',') + ']'
ELSE NULL
END)
+'}')

END