我有一个描述地理坐标的大型XML文档(KML,准确无误);下面的代码段可以让您了解它的外观。这里的问题是坐标是双精度(小数点后16位),这会在进一步处理中引起很多问题(此外,最后小数位实际上是十分之一纳米 - 我们的GPS并不精确)。
我一直在寻找将精度降低到给定值的任何方法,例如:小数点后5位给我们一个仪表精度。我尝试在Python中解析XML(使用lxml),更改值并保存新文档,但在流程文档中,格式更改很多,并以某种方式打破了进一步的处理。
因此,我正在寻找一种降低原位精度的方法,以便在原始文件中更改值。我认为AWK应该做到这一点,但遗憾的是我的尝试无济于事。
以下是my XML的示例。
<Document xmlns="http://www.opengis.net/kml/2.2">
<Folder><name>Export_Output02</name>
<Placemark>
<Style><LineStyle><color>ff0000ff</color></LineStyle><PolyStyle><fill>0</fill></PolyStyle></Style>
<ExtendedData><SchemaData schemaUrl="#Export_Output02">
<SimpleData name="species">1312</SimpleData>
<SimpleData name="area">7848012</SimpleData>
<SimpleData name="irrep_area">0.00000012742</SimpleData>
<SimpleData name="groupID">2</SimpleData>
</SchemaData></ExtendedData>
<MultiGeometry>
<Polygon>
<outerBoundaryIs>
<LinearRing>
<coordinates>-57.843052746056827,-33.032934004012787 -57.825312079170494,-33.089724736921667 -57.888494029914156,-33.073777852969904 -57.843052746056827,-33.032934004012787</coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
<Polygon>
<outerBoundaryIs>
<LinearRing>
<coordinates>-57.635769389832561,-33.032934004012787 -57.618028722946228,-33.089724736921667 -57.681210673689904,-33.073777852969904 -57.635769389832561,-33.032934004012787</coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
</MultiGeometry>
</Placemark>
</Folder>
</Document>
[编辑]
我的Python代码:
import glob
import argparse
from pykml import parser
from pykml.helpers import set_max_decimal_places
arg_parser = argparse.ArgumentParser(description='Script for batch reduction of precision of KML files', prog='KML precision reducer')
arg_parser.add_argument('-p', '--precision', type=int, default=5, help='Desired precision')
arg_parser.add_argument('-d', '--directory', default='./', help='Path to KML files')
args = arg_parser.parse_args()
path_to_kml = glob.glob(args.directory + '*.kml')
precision = args.precision
for kml_file in path_to_kml:
print 'Processing ' + kml_file
with open(kml_file) as file_read:
doc = parser.parse(file_read)
max_decimals={'longitude': precision, 'latitude': precision,}
for element in doc.iter("*"):
set_max_decimal_places(element, max_decimals)
out_filename = kml_file.replace('.kml', '_out.kml')
with open(out_filename, 'w') as file_write:
doc.write(file_write, pretty_print=True, with_tail=True)
答案 0 :(得分:2)
您可以使用XSLT。下面的样式表使用XSLT 2.0。这也可以使用XSLT 1.0,但它没有我在这里使用的tokenize()
函数:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:gis="http://www.opengis.net/kml/2.2"
version="2.0">
<!-- This is an identity transform template - it copies all the nodes -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- this template has precedence over the identity template for the `coordinates` nodes -->
<xsl:template match="gis:coordinates">
<xsl:copy> <!-- it copies the element -->
<xsl:variable name="coords" select="tokenize(.,' ')"/> <!-- saves coordinate pairs in variable -->
<xsl:for-each select="$coords"> <!-- for each coordinate pair, formats the values before and after the comma -->
<xsl:value-of select="round(number(substring-before(.,','))*100000) div 100000"/>
<xsl:text>,</xsl:text> <!-- puts the comma back between the coords -->
<xsl:value-of select="round(number(substring-after(.,','))*100000) div 100000"/>
<xsl:if test="position() != last()">
<xsl:text> </xsl:text> <!-- puts the space back if it's not the last coord -->
</xsl:if>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
我在上面添加了一些评论,说明它是如何运作的。
如果将其应用于样本文档,则会将坐标截断为五位小数。以下示例显示了转换后的coordinates
元素:
<LinearRing>
<coordinates>-57.84305,-33.03293 -57.82531,-33.08972 -57.88849,-33.07378 -57.84305,-33.03293</coordinates>
</LinearRing>
这是一个带有工作结果的 XSLT Fiddle 。
我在上面的XML Playground中粘贴了完整XML 并且它有效。我只是无法将你的文件保存在这里,因为文件太大,你可以尝试将其粘贴在那里。转换完整文件大约需要40秒。
我不了解Python中的XSLT 2.0支持,但您可以使用命令行工具(如Saxon)运行转换,或者调用Java或其他支持XSLT 2.0的语言的程序(或者,如果您正在为这个特定问题寻找解决方案,可以使用在线工具解决它。)
答案 1 :(得分:1)
这是可以混合XML和正则表达式并侥幸逃脱的实例之一:
import re
coords = re.compile("([-+]?[0-9]+\.[0-9]{6,}),([-+]?[0-9]+\.[0-9]{6,})")
def five_digits(match):
return "%.5f,%.5f" % tuple(float(g) for g in match.groups())
with open("source.xml") as source, open("target.xml", "w") as target:
source_xml = source.read()
target_xml = re.sub(coords, five_digits, source_xml)
target.write(target_xml)
模式coords
匹配坐标对,每个坐标有六个或更多小数位,函数five_digits
返回重新格式化为五个位置的坐标,re.sub
调用使用那两个人要做替换。