Merge pull request #103 from CartoDB/polyline_decoder

Polyline decoder
This commit is contained in:
Mario de Frutos
2016-03-02 11:10:59 +01:00
8 changed files with 132 additions and 6 deletions

3
.gitignore vendored
View File

@@ -1,2 +1,5 @@
.DS_Store
*.pyc
cartodb_services.egg-info/
build/
dist/

View File

@@ -1,10 +1,9 @@
import requests
import json
import re
from polyline.codec import PolylineCodec
from exceptions import WrongParams
from cartodb_services.tools import Coordinate
from cartodb_services.tools import Coordinate, PolyLine
class MapzenRouting:
@@ -74,7 +73,7 @@ class MapzenRouting:
try:
parsed_json_response = json.loads(response)
legs = parsed_json_response['trip']['legs'][0]
shape = PolylineCodec().decode(legs['shape'])
shape = PolyLine().decode(legs['shape'])
length = legs['summary']['length']
duration = legs['summary']['time']
routing_response = MapzenRoutingResponse(shape, length, duration)

View File

@@ -1,2 +1,3 @@
from redis_tools import RedisConnection
from coordinates import Coordinate
from coordinates import Coordinate
from polyline import PolyLine

View File

@@ -0,0 +1,51 @@
from itertools import tee, izip
from math import trunc
class PolyLine:
""" Polyline decoder https://developers.google.com/maps/documentation/utilities/polylinealgorithm?csw=1 """
def decode(self, data):
coordinates = []
chunks = self._extract_chunks(data)
for chunk in chunks:
coordinate = self._process_chunk(chunk)
coordinate /= 1e5
print coordinate
if len(coordinates) > 1:
# We have to sum the previous with the offset in this chunk
coordinate += coordinates[-2]
coordinates.append(round(coordinate, 5))
print coordinates
return zip(coordinates, coordinates[1:])[::2]
def _extract_chunks(self, data):
chunks, chunk = [], []
for character in data:
byte = ord(character) - 63
if byte & 0x20 > 0:
byte &= 0x1F
chunk.append(byte)
else:
chunk.append(byte)
chunks.append(chunk)
chunk = []
return chunks
def _process_chunk(self, chunk):
coordinate = self._get_coordinate(chunk)
# Check if the coordinate is negative
if coordinate & 0x1:
return ~(coordinate >> 1)
else:
return coordinate >> 1
def _get_coordinate(self, chunk):
coordinate = 0
for i, c in enumerate(chunk):
coordinate |= c << (i * 5)
return coordinate

View File

@@ -5,7 +5,6 @@ python-dateutil==2.2
googlemaps==2.4.2
# Dependency for googlemaps package
requests<=2.9.1
polyline==1.1
# Test
mock==1.3.0

View File

@@ -10,7 +10,7 @@ from setuptools import setup, find_packages
setup(
name='cartodb_services',
version='0.3.0',
version='0.3.1',
description='CartoDB Services API Python Library',

View File

@@ -0,0 +1,39 @@
from cartodb_services.tools import PolyLine
from unittest import TestCase
class TestPolyline(TestCase):
def setUp(self):
self.polyline = PolyLine()
def test_should_decode_a_chunk_correctly(self):
decoded_polyline = self.polyline.decode('`~oia@`~oia@')
original_value = [(-179.98321, -179.98321)]
assert decoded_polyline == original_value
def test_should_decode_polyline_correctly(self):
original_polyline_1 = [(38.5, -120.2),
(40.7, -120.95),
(43.252, -126.453)]
decoded_polyline_1 = self.polyline.decode('_p~iF~ps|U_ulLnnqC_mqNvxq`@')
assert decoded_polyline_1 == original_polyline_1
original_polyline_2 = [(17.95783,-5.58105),
(15.79225,2.90039),
(7.60211,-10.76660)]
decoded_polyline_2 = self.polyline.decode('mkrlBp`aa@z}eL_pwr@js~p@tilrA')
assert decoded_polyline_2 == original_polyline_2
original_polyline_3 = [(62.75473,-157.14844),
(65.07213,169.80469) ,
(48.92250,158.55469),
(44.33957,-150.46875)]
decoded_polyline_3 = self.polyline.decode('ax_~Jv`d~\wrcMa`qj}@dfqaBngtcAhb~Zncc}y@')
assert decoded_polyline_3 == original_polyline_3

View File

@@ -0,0 +1,34 @@
from unittest import TestCase
from nose.tools import assert_raises
from nose.tools import assert_not_equal, assert_equal
from ..helpers.integration_test_helper import IntegrationTestHelper
class TestRoutingFunctions(TestCase):
def setUp(self):
self.env_variables = IntegrationTestHelper.get_environment_variables()
self.sql_api_url = "https://{0}.{1}/api/v2/sql".format(
self.env_variables['username'],
self.env_variables['host'],
self.env_variables['api_key']
)
def test_if_select_with_routing_point_to_point_is_ok(self):
query = "SELECT duration, length, shape as the_geom " \
"FROM cdb_route_point_to_point('POINT(-3.70237112 40.41706163)'::geometry, " \
"'POINT(-3.69909883 40.41236875)'::geometry, 'car', " \
"ARRAY['mode_type=shortest']::text[])&api_key={0}".format(
self.env_variables['api_key'])
routing = IntegrationTestHelper.execute_query(self.sql_api_url, query)
assert_not_equal(routing['the_geom'], None)
def test_if_select_with_routing_point_to_point_without_api_key_raise_error(self):
query = "SELECT duration, length, shape as the_geom " \
"FROM cdb_route_point_to_point('POINT(-3.70237112 40.41706163)'::geometry, " \
"'POINT(-3.69909883 40.41236875)'::geometry, 'car', " \
"ARRAY['mode_type=shortest']::text[])"
try:
IntegrationTestHelper.execute_query(self.sql_api_url, query)
except Exception as e:
assert_equal(e.message[0], "The api_key must be provided")