Introduction

The encpoly package is a Python 3 library for encoding/decoding geographic coordinates using Google’s Encoded Polyline Algorithm. The package is developed on GitHub and contributions are welcome.

Table of contents

Installation and usage

https://img.shields.io/pypi/v/encpoly.svg

The latest encpoly release is available on PyPI, and you can install it using Pip:

pip install encpoly

Tip

When installing Python dependencies, it’s a good idea to use a virtual environment.

If you prefer, you can also install encpoly using a higher-level dependency-management tool like Poetry:

poetry add encpoly

Or Pipenv:

pipenv install encpoly

System requirements

The encpoly package requires Python 3.4+. It runs well under PyPy. It has no dependencies of its own. It’s multi-platform and works on Linux, MacOS, and Windows.

Usage

After you’ve installed the package you can import the encode() and decode() functions from the encpoly package:

>>> from encpoly import encode, decode

To encode a series of coordinates, pass them to the encode() function:

>>> coords = ((38.5, -120.2), (40.7, -120.95), (43.252, -126.453))
>>> encode(coords)
'_p~iF~ps|U_ulLnnqC_mqNvxq`@'

To decode a polyline, pass it to the decode() function:

>>> for (lat, lon) in decode("_p~iF~ps|U_ulLnnqC_mqNvxq`@"):
...     print(lat, lon)
...
38.5 -120.2
40.7 -120.95
43.252 -126.453

Tip

The decode() function returns a generator. If you want to get a list or tuple of coordinates rather than a generator, wrap the function in list or tuple:

>>> tuple(decode("_p~iF~ps|U_ulLnnqC_mqNvxq`@"))
([38.5, -120.2], [40.7, -120.95], [43.252, -126.453])

You’ll probably not need anything other than those two functions, but the full contents of the package are documented in the API reference.

Advanced usage

By default the Encoded Polyline Algorithm is lossy. When coordinates are encoded they are truncated to five decimal places. For example, the latitude and longitude coordinates 64.12345678, -21.987654321 would be encoded as 64.12345, -21.98765:

>>> list(decode(encode(((64.12345678, -21.987654321),))))
[[64.12346, -21.98765]]

You can, however, choose the precision at which coordinates are encoded using the precision argument:

>>> coords = ((64.12345678, -21.987654321),)
>>> encode(coords)
'sbkfKxmeeC'
>>> encode(coords, precision=8)
'{soqe}Jnv~x`bC'
>>> list(decode("{soqe}Jnv~x`bC", precision=8))
[[64.12345678, -21.98765432]]

Note

You must use the same value for precision when encoding and decoding. You can only set the precision for polylines as a whole, not individual coordinates within a polyline.

Package development

Encpoly is an open-source project and all contributions are welcome. Encpoly is developed on GitHub and the latest source code is always available from there. To get a copy of the code you can clone the public repository:

$ git clone https://github.com/JaGallup/encpoly.git

To install the package in “editable mode” you will need Poetry, a tool for dependency management and packaging in Python. Once you have both Poetry and a copy of the source you can install encpoly and its development dependencies into a virtual environment:

$ cd encpoly
$ poetry install
Test suite

Encpoly uses Pytest and Tox for testing. To run the tests locally, use:

tox -e py37

The example above runs tests against Python 3.7. You can also use other versions like py36 and pypy3.

The test suite is run automatically when the master branch is pushed to GitHub. The tests are run using Travis CI (Linux) and Appveyor (Windows) for Python 3.4+ and PyPy 3.5. Code coverage is tracked using Codecov.

Build status on Travis CI Build status on Appveyor https://codecov.io/gh/JaGallup/encpoly/branch/master/graph/badge.svg

API reference

Encode and decode coordinates using Google’s Encoded Polyline Algorithm.

The Encoded Polyline Algorithm is a lossy compression algorithm that allows you to store a series of geographical coordinates as a single string. It was developed by Google.

The encoding process converts a longitude or latitude coordinate from a decimal value to a binary value, and then converts that into a series of ASCII characters. To further compress the data, values stored for longitude and latitude are offset from the previous longitude or latitude values. The algorithm is lossy because it uses a default maximum precision of five decimal places for longitude and latitude.

Encoding and decoding

encpoly.encode(locations, precision=None)

Encode the given coordinates as an encoded polyline.

>>> coords = ((38.5, -120.2), (40.7, -120.95), (43.252, -126.453))
>>> encode(coords)
'_p~iF~ps|U_ulLnnqC_mqNvxq`@'

To produce an encoded polyline, follow these steps for each individual longitude and latitude value.

  1. Take a signed longitude or latitude decimal value and multiply it by 1e5 (or other decimal precision if specified)
  2. Round the result using Python 2’s default rounding behaviour
  3. Convert the decimal value to binary (a negative value must be calculated using its two’s complement)
  4. Shift the binary value left by one bit
  5. If the original decimal value is negative, invert the encoding
  6. Break the binary value out into 5-bit chunks from right to left
  7. Place the 5-bit chunks into reverse order
  8. OR each value with 0x20 if another bit chunk follows
  9. Convert each value to decimal
  10. Add 63 to each value (this is to avoid, e.g., unprintable ASCII characters)
  11. Convert each value to its ASCII equivalent

Note

After the first latitude/longitude pair, the steps should be followed using offsets from the previous point.

If coordinates are provided with a greater decimal precision than is used during step 1, the resulting output will be lossy.

Parameters:
  • locations (iterable) – ordered latitude and longitude pairs (in y, x order) as a tuple, list, or other iterable
  • precision (int) – maximum decimal precision to use for longitude and latitude coordinates (defaults to 5)
Returns:

an encoded polyline

Return type:

str

encpoly.decode(polyline, precision=None)

Yield the given encoded polyline as an ordered series of points.

This essentially reverses the algorithm’s steps as outlined in the encode() documentation.

>>> for coord in decode("_p~iF~ps|U_ulLnnqC_mqNvxq`@"):
...     print(coord)
...
[38.5, -120.2]
[40.7, -120.95]
[43.252, -126.453]
Parameters:
  • polyline (str) – encoded polyline to decode
  • precision (int) – maximum decimal precision used when the polyline was originally encoded (defaults to 5)
Yields:

iterable of lists of lat/lon coordinate pairs

Supporting functions and attributes

The following functions are attributes are used to support encoding and decoding and are not expected to be used directly. They’re documented here for completeness.

encpoly.codec.DEFAULT_PRECISION = 100000.0

When encoding values of longitude and latitude, use a default precision of five decimal places. This gives, very roughly, accuracy of one metre.

encpoly.codec.polyline_round(number)

Return the given number rounded to nearest integer.

>>> polyline_round(0.5)
1
>>> polyline_round(-0.5)
-1

Note

The rounding behaviour is “always round up 0.5”. This is not generally considered the standard way of rounding numbers now (Python 3 does it differently, for example). But it is what Python 2 does by default and it is, not coincidentally, what the Encoded Polyline Algorithm requires.

Parameters:number (float) – value to round to nearest integer
Returns:int
encpoly.codec.encode_coord(coord, precision)

Encode a given longitude or latitude value as an encoded polyline.

The encodes an individual value from a lat/lon pair into an ASCII value that can be used in an encoded polyline.

Tip

You probably want to use encpoly.encode() instead, as that function will encode a complete line (that is, a set of lat/lon pairs).

>>> encode_coord(-179.9832104, 1e5)
'`~oia@'
Parameters:
  • coord (float) – a single latitude or longitude coordinate
  • precision (float) – maximum decimal precision used to store the coordinate value
Returns:

the coordinate value as an encoded polyline

Return type:

str

encpoly.codec.decode_coords(polyline)

Yield alternating latitude and longitude values from a polyline.

Each coordinate is an offset from the last of its type, and has yet to be converted from its stored integer value to a floating point coordinate (see steps 1 and 2 of the encoding algorithm).

Tip

You probably want to use encpoly.decode() instead, as that function returns lat/lon pairs with offset and precision handled correctly.

>>> tuple(decode_coords("_p~iF~ps|U_ulLnnqC_mqNvxq`@"))
(3850000, -12020000, 220000, -75000, 255200, -550300)
Parameters:polyline (str) – encoded polyline to decode
Yields:iterable of alternating lat/lon coordinates

Quick start

>>> from encpoly import encode, decode
>>> coords = ((38.5, -120.2), (40.7, -120.95), (43.252, -126.453))
>>> encode(coords)
'_p~iF~ps|U_ulLnnqC_mqNvxq`@'
>>> tuple(decode("_p~iF~ps|U_ulLnnqC_mqNvxq`@"))
([38.5, -120.2], [40.7, -120.95], [43.252, -126.453])