【问题标题】:Reformat SQLGeography polygons to JSON将 SQLGeography 多边形重新格式化为 JSON
【发布时间】:2011-06-28 12:53:13
【问题描述】:

我正在构建一个以 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
    }

这实际上工作得很好,并将数据库输出动态转换为 JSON 以响应操作调用。

但是,我不是正则表达式大师,而且对 String.Replace() 的调用对我来说似乎也效率低下。

有没有人对此性能有任何建议/cmets?

【问题讨论】:

  • 注意:地理定义仅适用于 GB,这反映在正则表达式中。
  • 不要将数据作为文本返回。使用SqlGeometryDbGeometry 直接返回。或者直接从数据库返回为 GeoJSON。

标签: c# json geojson sqlgeography wkt


【解决方案1】:

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

此方法从 MS SQL Geography Type 上的 ToString() 调用中获取输出。 如果返回的字符串包含由 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;
    }
}

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

【讨论】:

    【解决方案2】:

    要从 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"
    }
    

    【讨论】:

    • 吉普车,太棒了。谢谢!这应该是顶部。
    • 这几天可能是更好的答案,因为我最初在 10 多年前回答了我自己的问题!
    【解决方案3】:

    为了回答您关于效率的问题,对于这种特殊情况,我认为 ReplaceRegEx 的区别不会那么大。我们真正改变的只是一些括号和逗号。就个人而言,我更喜欢在 TSQL 中为 Web 应用程序做事,因为我可以将计算工作卸载到 SQL Server 而不是 Web Server。就我而言,我有很多数据正在为地图生成,因此不想让网络服务器陷入大量数据转换的困境。此外,为了性能,我通常在 SQL 服务器上比在 Web 服务器上投入更多的马力,所以即使这两个函数之间存在一些差异,如果 Replace 效率较低,至少可以处理由具有更多资源的服务器。一般来说,我希望我的网络服务器处理与客户端的连接,而我的 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
    

    接下来,在SELECT 语句中使用GetGeoJSON 函数,例如:

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

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

    【讨论】:

    • 我得到这个错误几何与地理不兼容
    • 你从哪里得到这个错误的?什么时候调用函数?如果是这样,它将地理作为参数,而不是几何。
    【解决方案4】:

    詹姆斯的回答中概述的方法效果很好。但是我最近在转换经度值超过 99 的 WKT 时发现了一个错误。

    我改了正则表达式:

    @"(-?\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】:

      字符串在 .net 中是不可变的,因此当您替换一些字符串时,您会创建先前字符串的编辑副本。这对于性能来说并不那么重要,就像内存使用一样。

      JSON.net

      或者使用StringBuilder正确生成。

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

      【讨论】:

      • 我知道这一点,而且我已经在使用 JSON.Net。因为字符串没有分隔符,所以我不能吐出它们然后将它们添加到 StringBuilder 或 JSON 对象。我可以检测到 Lat/Long 对,但字符串中的括号也很重要,因为它表示数组的深度,否则我可以只使用我的匹配项并构建一个 JSON 对象而不关心括号。本质上,这些字符串几乎是 JSON 对象,它们只需要重新格式化为 JSON 语法。
      【解决方案6】:

      用于将空间单元格格式化为 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
      

      【讨论】:

        猜你喜欢
        • 2014-10-06
        • 2019-07-15
        • 1970-01-01
        • 2019-08-05
        • 1970-01-01
        • 2017-09-14
        • 2016-06-27
        • 1970-01-01
        • 2016-11-10
        相关资源
        最近更新 更多