Python 的 Geodaisy 库转换带负号坐标的WKT时的Bug
Geodaisy是一个Python库,用于创建由类型和坐标表示的地理对象,并在各种标准和表示之间进行转换,包括GeoJSON、Well - Known Text和Python的__geo_interface__协议,它被其他地理库使用。
源码地址:https://gitee.com/mzfly/geodaisy.git
在使用其 converters.wkt_to_geo_interface()方法时转换带符号坐标的wkt字符串时,发现其转换的结果跟预期的不一样:
例如:
> wkt = "MULTILINESTRING ((3146.2134801800566 2556399.9823166174, -2611.53319329879 2564044.0076796883))" > converters.wkt_to_geo_interface(wkt) # {'type': 'MultiLineString', 'coordinates': (3146.2134801800566, 2556399.9823166174, -2611.53319329879, 2564044.0076796883)}
我们看到 coordinates 的值并不是我们想要的结果:
{'type': 'MultiLineString', 'coordinates': [(3146.2134801800566, 2556399.9823166174), (-2611.53319329879, 2564044.0076796883)]}
原因是 wkt_to_geo_interface 方法源码采用正则匹配,并没有考虑带负数坐标的情况(或者根本就不考虑),但是在地方坐标系处理偏移的过程中是肯定有出现负值坐标的情况的。
解决方法:
修改wkt_to_geo_interface方法源码的正则匹配(匹配”-“号和数字):
1 def wkt_to_geo_interface(wkt): 2 # type: (str) -> dict 3 """Converts a WKT string to a geo_interface dictionary.""" 4 try: 5 wkt_type, coords = re.split(r'(?<=[A-Z])\s', wkt) 6 7 geo_type = type_translations[wkt_type] 8 9 # Clean up the strings so they'll covert correctly 10 if geo_type in {'Polygon', 'MultiLineString', 'MultiPolygon'}: 11 # coords = re.sub(r'(?<=\d)\), \((?=\d)', ')), ((', coords) 12 coords = re.sub(r'(?<=[-\d])\), \((?=[-\d])', ')), ((', coords) # modif by 举个栗子 13 14 # Pairs of coordinates must be enclosed in parentheses 15 # coords = re.sub(r'(?<=\d), (?=\d)', '), (', coords) 16 coords = re.sub(r'(?<=[-\d]), (?=[-\d])', '), (', coords) # modif by 举个栗子 17 18 # Coordinates within parentheses must be separated by commas 19 # coords = re.sub(r'(?<=\d) (?=\d)', ', ', coords) 20 coords = re.sub(r'(?<=[-\d]) (?=[-\d])', ', ', coords) # # modif by 举个栗子 21 22 # Now we can turn the string into a tuple or a tuple of tuples 23 coords = literal_eval(coords) 24 25 coords = reformat_coordinates(coords, 'geo_interface') # type: ignore # noqa: E501 26 27 # If we only have a simple polygon (no hole), the coordinate array 28 # won't be deep enough to satisfy the GeoJSON/geo_interface spec, so 29 # we need to enclose it in a list. 30 numbers = {float, int} 31 if geo_type == 'Polygon' and type(coords[0][0]) in numbers: 32 coords = [coords] # type: ignore 33 elif geo_type == 'MultiPolygon' and type(coords[0][0][0]) in numbers: 34 coords = [coords] # type: ignore 35 36 except Exception: 37 raise ValueError('{} is not a WKT string'.format(wkt)) 38 39 return {'type': geo_type, 'coordinates': coords}