Bug in Python's Geodaisy library converting WKT with negative coordinates

Time:2022-8-13
Geodaisy is a Python library for creating geographic objects represented by types and coordinates, and converting between various standards and representations, including GeoJSON, Well-Known Text, and Python's __geo_interface__ protocol, which is used by other geographic libraries use.
When converting a wkt string with signed coordinates when using its converters.wkt_to_geo_interface() method, it turns out that the result of the conversion is not as expected:
E.g:
> 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)}

We see that the value of coordinates is not what we want:

{'type': 'MultiLineString', 'coordinates': [(3146.2134801800566, 2556399.9823166174), (-2611.53319329879, 2564044.0076796883)]}

The reason is that the source code of the wkt_to_geo_interface method adopts regular matching, and does not consider the case with negative coordinates (or does not consider it at all), but there must be negative coordinates in the process of processing offsets in the local coordinate system.

Solution:

Modify the regular matching of the source code of the wkt_to_geo_interface method (matching "-" numbers and numbers):

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}