@@ -1,2 +1,5 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
*.pyc
|
*.pyc
|
||||||
|
cartodb_services.egg-info/
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
from polyline.codec import PolylineCodec
|
|
||||||
|
|
||||||
from exceptions import WrongParams
|
from exceptions import WrongParams
|
||||||
from cartodb_services.tools import Coordinate
|
from cartodb_services.tools import Coordinate, PolyLine
|
||||||
|
|
||||||
|
|
||||||
class MapzenRouting:
|
class MapzenRouting:
|
||||||
@@ -74,7 +73,7 @@ class MapzenRouting:
|
|||||||
try:
|
try:
|
||||||
parsed_json_response = json.loads(response)
|
parsed_json_response = json.loads(response)
|
||||||
legs = parsed_json_response['trip']['legs'][0]
|
legs = parsed_json_response['trip']['legs'][0]
|
||||||
shape = PolylineCodec().decode(legs['shape'])
|
shape = PolyLine().decode(legs['shape'])
|
||||||
length = legs['summary']['length']
|
length = legs['summary']['length']
|
||||||
duration = legs['summary']['time']
|
duration = legs['summary']['time']
|
||||||
routing_response = MapzenRoutingResponse(shape, length, duration)
|
routing_response = MapzenRoutingResponse(shape, length, duration)
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
from redis_tools import RedisConnection
|
from redis_tools import RedisConnection
|
||||||
from coordinates import Coordinate
|
from coordinates import Coordinate
|
||||||
|
from polyline import PolyLine
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -5,7 +5,6 @@ python-dateutil==2.2
|
|||||||
googlemaps==2.4.2
|
googlemaps==2.4.2
|
||||||
# Dependency for googlemaps package
|
# Dependency for googlemaps package
|
||||||
requests<=2.9.1
|
requests<=2.9.1
|
||||||
polyline==1.1
|
|
||||||
|
|
||||||
# Test
|
# Test
|
||||||
mock==1.3.0
|
mock==1.3.0
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from setuptools import setup, find_packages
|
|||||||
setup(
|
setup(
|
||||||
name='cartodb_services',
|
name='cartodb_services',
|
||||||
|
|
||||||
version='0.3.0',
|
version='0.3.1',
|
||||||
|
|
||||||
description='CartoDB Services API Python Library',
|
description='CartoDB Services API Python Library',
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
@@ -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")
|
||||||
Reference in New Issue
Block a user