1

我有一些代码(来自 GeoNetwork)需要将地理标记语言(在 XML 中)转换为 GeoJSON。我目前正在尝试添加功能以读取从 posList 形成的多边形,但我很难概念化/起草我需要做的事情。

“输入”基本上是一个由一堆坐标组成的字符串。所以它可能看起来像这样

<gml:LinearRing gml:id="p21" srsName="http://www.opengis.net/def/crs/EPSG/0/4326">
    <gml:posList srsDimension="2">45.67 88.56 55.56 88.56 55.56 89.44 45.67 89.44</gml:posList>
 </gml:LinearRing >

(借自维基百科的样本)。我可以使用类似的东西在 XSLT 中将其分块

<xsl:variable name="temp" as="xs:string*" select="tokenize(gml:LinearRing/gml:posList))" '\s'/>

这应该给我Temp =

('45.67', '88.56', '55.56', '88.56', '55.56', '89.44', '45.67', '89.44')

问题 1:GeoJSON 想要 WGS 84 (EPSG 4326) 和 (long, lat) 顺序中的所有内容 - 但严格遵守 WGS 84 规则(我希望 gml 遵循)意味着坐标是 (lat, long) 顺序 - 所以该列表需要重新排序。(我认为 - 这仍然让我很困惑)

问题 2:GeoJSON 想要坐标对,但我只有一个坐标列表。

我目前的想法是做这样的事情:

<geom>
<xsl:text>{"type": "Polygon",</xsl:text>
<xsl:text>"coordinates": [
[</xsl:text>

<xsl:variable name="temp" as="xs:string*" select="tokenize(gml:LinearRing/gml:posList))" '\s'/>
<xsl:for-each select="$temp">
  <xsl:if test="position() mod 2 = 0">
    <xsl:value-of select="concat('[', $saved, ', ', ., ']')" separator=","/>
  </xsl:if>
  <xsl:variable name="saved" value="."/>
</xsl:for-each>
<xsl:text>]
] 
}</xsl:text>
</geom>

但我不确定 XSL 是否会让我不断地编写这样的变量,以及是否有更好/更有效的解决方案来解决这个问题。(我在 MATLAB 方面有很多经验,我会使用 for 循环快速解决这个问题,如果不是有效的话)

理想情况下,我会得到类似于

<geom>
{"type": "Polygon",
"coordinates": [
  [
  [88.56, 45.67],
  [88.56, 55.56],
  [89.44, 55.56],
  [89.44, 45.67]
  ]
]
}
</geom>

(我认为,要弄清楚多边形是右手还是左手,还有一整套其他的蠕虫罐头)

4

3 回答 3

2

此样式表带有任何输入(未使用)

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
                xmlns:my="dummy"
                exclude-result-prefixes="my">
   <xsl:template match="/">
      <xsl:sequence select="
            my:reverseByTuple(
                  ('45.67', '88.56', '55.56', '88.56', '55.56', '89.44', '45.67', '89.44')
            )"/>
   </xsl:template> 
   <xsl:function name="my:reverseByTuple">
        <xsl:param name="items"/>
        <xsl:sequence 
            select="if (empty($items))
                    then ()
                    else ($items[2], $items[1], my:reverseByTuple($items[position()>2]))"
                    />
    </xsl:function>
</xsl:stylesheet>

输出

88.56 45.67 88.56 55.56 89.44 55.56 89.44 45.67

我真的不明白你为什么要序列化 ​​JSON 而不是使用像 XSLT 3.0 中的函数这样有据可查的库......但只是为了好玩,这个样式表

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
                xmlns:my="dummy"
                exclude-result-prefixes="my">
   <xsl:template match="/">
      <xsl:value-of 
        select="
          my:encloseWithBracket(
            my:reverseByTupleEncloseWithBracket(
              ('45.67', '88.56', '55.56', '88.56', '55.56', '89.44', '45.67', '89.44')
            )
          )"/>
   </xsl:template> 
   <xsl:function name="my:reverseByTupleEncloseWithBracket">
        <xsl:param name="items"/>
        <xsl:sequence 
            select="if (empty($items))
                    then ()
                    else (my:encloseWithBracket(($items[2],$items[1])),
                          my:reverseByTupleEncloseWithBracket($items[position()>2]) )"
                    />
    </xsl:function>
   <xsl:function name="my:encloseWithBracket">
        <xsl:param name="items"/>
        <xsl:value-of select="concat('[',string-join($items,','),']')"/>
    </xsl:function>
</xsl:stylesheet>

输出

[[88.56,45.67],[88.56,55.56],[89.44,55.56],[89.44,45.67]]
于 2019-04-11T23:19:54.823 回答
1

支持 XPath 3.1 的 XSLT 3 可以将 JSON 表示为映射/数组并将它们序列化为 JSON,以便您可以从坐标序列计算 XPath 映射:

serialize(
    map { 
      'type' : 'polygon', 
      'coordinates' : array { 
          let $seq := tokenize(gml:LinearRing/gml:posList, '\s+') 
          return $seq[position() mod 2 = 0]![., let $p := position() return $seq[($p - 1) * 2 + 1]] 
         }
    },
    map { 'method' : 'json', 'indent' : true() }
)

https://xsltfiddle.liberty-development.net/gWvjQfu/1

要获取数组中的 JSON 数字,请使用let $seq := tokenize(., '\s+')!number()而不是let $seq := tokenize(gml:LinearRing/gml:posList, '\s+').

如果您可以访问支持高阶函数的 Saxon PE 或 EE 或 Altova 等 XSLT 3 处理器,则可以将其减少到

        serialize(
          map {
            'type': 'polygon',
            'coordinates': array {
                let $seq := tokenize(gml:LinearRing/gml:posList, '\s+'),
                    $odd := $seq[position() mod 2 = 1],
                    $even := $seq[position() mod 2 = 0]
                return
                    for-each-pair($odd, $even, function ($c1, $c2) {
                        [$c2, $c1]
                    })
            }
          }, 
          map {
            'method': 'json',
            'indent': true()
          }
        )
于 2019-04-12T12:03:23.633 回答
0

您可以使用以下 XSLT-2.0 样式表来获得您想要的结果。它利用xsl:analyze-string函数来分隔 2 元组中的值。该模板包括错误处理并gml使用exclude-result-prefixes="gml". 您可能需要调整模板和xsl:analyze-string表达式的 XML 路径。但我想你可以处理这个。

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:gml="http://www.opengis.net/def/crs/EPSG/0/4326" exclude-result-prefixes="gml">
    <xsl:output method="xml" omit-xml-declaration="yes" />

    <xsl:template match="/">
<geom><xsl:text>
{"type": "Polygon",
"coordinates": [
  [
</xsl:text>
        <xsl:analyze-string select="gml:LinearRing/gml:posList" 
        regex="\s*(\d\d)\.(\d\d)\s+(\d\d)\.(\d\d)\s*"> 
            <xsl:matching-substring>
                <xsl:value-of select="concat('    [',regex-group(3),'.', regex-group(4),', ',regex-group(1),'.', regex-group(2),']&#xa;')"/>
            </xsl:matching-substring>
            <xsl:non-matching-substring>
                <xsl:message terminate="yes">=============================&#xA;=== ERROR: Invalid input! ===&#xA;=============================</xsl:message>
            </xsl:non-matching-substring>
        </xsl:analyze-string>
<xsl:text>  ]
]
}
</xsl:text>
</geom>
    </xsl:template>

</xsl:stylesheet>

它的输出是:

<geom>
{"type": "Polygon",
"coordinates": [
  [
    [88.56, 45.67]
    [88.56, 55.56]
    [89.44, 55.56]
    [89.44, 45.67]
  ]
]
}
</geom>% 
于 2019-04-11T23:09:41.200 回答