From 5e4346635fadcffe2fd1812495a59e32811973ac Mon Sep 17 00:00:00 2001 From: Aaron Seelye Date: Tue, 28 May 2019 14:25:43 -0700 Subject: [PATCH 1/2] Add support for lon-lat/GeoJSON tuples --- README.rst | 4 ++++ polyline/__init__.py | 10 ++++++---- polyline/codec.py | 10 ++++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index 6648c53..9cdcaf9 100644 --- a/README.rst +++ b/README.rst @@ -35,6 +35,8 @@ This should return ``_p~iF~ps|U_ulL~ugC_hgN~eq`@``. You can set the required precision with the optional ``precision`` parameter. The default value is 5. +You can encode lon-lat tuples by setting ``geojson=True``. + Decoding -------- @@ -44,3 +46,5 @@ To get a set of coordinates represented by a given encoded polyline string:: polyline.decode('u{~vFvyys@fS]') This should return ``[(40.63179, -8.65708), (40.62855, -8.65693)]``. + +You can decode into lon-lat tuples by setting ``geojson=True``. diff --git a/polyline/__init__.py b/polyline/__init__.py index b2cf024..ab0b038 100644 --- a/polyline/__init__.py +++ b/polyline/__init__.py @@ -3,28 +3,30 @@ __version__ = '1.3.2' -def decode(expression, precision=5): +def decode(expression, precision=5, geojson=False): """ Decode a polyline string into a set of coordinates. :param expression: Polyline string, e.g. 'u{~vFvyys@fS]'. :param precision: Precision of the encoded coordinates. Google Maps uses 5, OpenStreetMap uses 6. The default value is 5. + :param geojson: Set output of tuples to (lon, lat), as per https://tools.ietf.org/html/rfc7946#section-3.1.1 :return: List of coordinate tuples """ - return PolylineCodec().decode(expression, precision) + return PolylineCodec().decode(expression, precision, geojson) -def encode(coordinates, precision=5): +def encode(coordinates, precision=5, geojson=False): """ Encode a set of coordinates in a polyline string. :param coordinates: List of coordinate tuples, e.g. [(0, 0), (1, 0)]. :param precision: Precision of the coordinates to encode. Google Maps uses 5, OpenStreetMap uses 6. The default value is 5. + :param geojson: Set to True in order to encode lon-lat tuples. :return: The encoded polyline string. """ - return PolylineCodec().encode(coordinates, precision) + return PolylineCodec().encode(coordinates, precision, geojson) __all__ = ['decode', 'encode'] diff --git a/polyline/codec.py b/polyline/codec.py index 415b00d..b5eacff 100644 --- a/polyline/codec.py +++ b/polyline/codec.py @@ -36,7 +36,7 @@ def _trans(self, value, index): return ~(result >> 1) if comp else (result >> 1), index - def decode(self, expression, precision=5): + def decode(self, expression, precision=5, geojson=False): coordinates, index, lat, lng, length, factor = [], 0, 0, 0, len(expression), float(10 ** precision) while index < length: @@ -46,9 +46,15 @@ def decode(self, expression, precision=5): lng += lng_change coordinates.append((lat / factor, lng / factor)) + if geojson is True: + coordinates = [t[::-1] for t in coordinates] + return coordinates - def encode(self, coordinates, precision=5): + def encode(self, coordinates, precision=5, geojson=False): + if geojson is True: + coordinates = [t[::-1] for t in coordinates] + output, factor = six.StringIO(), int(10 ** precision) self._write(output, coordinates[0][0], 0, factor) From 59495b81032bd09ff061c944c6895d06c999a719 Mon Sep 17 00:00:00 2001 From: Frederick Jansen Date: Wed, 26 Jun 2019 18:40:28 -0400 Subject: [PATCH 2/2] Add unit tests --- requirements/test.txt | 3 ++- test/test_codec.py | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/requirements/test.txt b/requirements/test.txt index 9ce02c5..e2fef78 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -1,2 +1,3 @@ flake8==2.2.4 -nose==1.3.4 +nose==1.3.7 +coverage==4.5.3 diff --git a/test/test_codec.py b/test/test_codec.py index 720afcb..a9c5852 100644 --- a/test/test_codec.py +++ b/test/test_codec.py @@ -49,6 +49,14 @@ def test_decode_official_example(self): (43.252, -126.453) ]) + def test_decode_geojson(self): + d = polyline.decode('_p~iF~ps|U_ulLnnqC_mqNvxq`@', geojson=True) + self.assertEqual(d, [ + (-120.200, 38.500), + (-120.950, 40.700), + (-126.453, 43.252) + ]) + def test_decode_official_example_precision(self): d = polyline.decode('_izlhA~rlgdF_{geC~ywl@_kwzCn`{nI', 6) self.assertEqual(d, [ @@ -111,6 +119,14 @@ def test_encode_official_example(self): ]) self.assertEqual(e, '_p~iF~ps|U_ulLnnqC_mqNvxq`@') + def test_encode_geojson(self): + e = polyline.encode([ + (-120.200, 38.500), + (-120.950, 40.700), + (-126.453, 43.252) + ], geojson=True) + self.assertEqual(e, '_p~iF~ps|U_ulLnnqC_mqNvxq`@') + def test_encode_official_example_precision(self): e = polyline.encode([ (38.500, -120.200), @@ -178,7 +194,7 @@ def generator(): else: okays += 1 - assert okays == waypoints + self.assertEqual(okays, waypoints) print("encoded and decoded {0:.2f}% correctly for {1} waypoints @ {2} wp/sec".format( 100 * okays / float(waypoints), waypoints,