Added cdb_dataservices_server functions
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
from routing import MapboxRouting, MapboxRoutingResponse
|
||||
from geocoder import MapboxGeocoder
|
||||
from isolines import MapboxIsolines
|
||||
from isolines import MapboxIsolines, MapboxIsochronesResponse
|
||||
from matrix_client import MapboxMatrixClient
|
||||
from exceptions import ServiceException
|
||||
|
||||
@@ -54,8 +54,15 @@ class MapboxGeocoder(Traceable):
|
||||
return [longitude, latitude]
|
||||
|
||||
@qps_retry(qps=10)
|
||||
def geocode(self, address, country=None):
|
||||
response = self._geocoder.forward(address=address,
|
||||
def geocode(self, searchtext, city=None, state_province=None,
|
||||
country=None):
|
||||
address = [searchtext]
|
||||
if city:
|
||||
address.append(city)
|
||||
if state_province:
|
||||
address.append(state_province)
|
||||
|
||||
response = self._geocoder.forward(address=', '.join(address),
|
||||
country=country,
|
||||
limit=1)
|
||||
|
||||
|
||||
@@ -60,22 +60,28 @@ class MapboxIsolines():
|
||||
|
||||
return costs
|
||||
|
||||
def calculate_isochrone(self, origin, time_range,
|
||||
def calculate_isochrone(self, origin, time_ranges,
|
||||
profile=DEFAULT_PROFILE):
|
||||
validate_profile(profile)
|
||||
|
||||
max_speed = MAX_SPEEDS[profile]
|
||||
upper_rmax = max_speed * time_range # an upper bound for the radius
|
||||
|
||||
return self.calculate_isoline(origin=origin,
|
||||
isorange=time_range,
|
||||
upper_rmax=upper_rmax,
|
||||
cost_method=self._calculate_matrix_cost,
|
||||
profile=profile,
|
||||
unit_factor=UNIT_FACTOR_ISOCHRONE,
|
||||
number_of_angles=MATRIX_NUM_ANGLES,
|
||||
max_iterations=MATRIX_MAX_ITERS,
|
||||
tolerance=MATRIX_TOLERANCE)
|
||||
isochrones = []
|
||||
for time_range in time_ranges:
|
||||
upper_rmax = max_speed * time_range # an upper bound for the radius
|
||||
|
||||
coordinates = self.calculate_isoline(origin=origin,
|
||||
isorange=time_range,
|
||||
upper_rmax=upper_rmax,
|
||||
cost_method=self._calculate_matrix_cost,
|
||||
profile=profile,
|
||||
unit_factor=UNIT_FACTOR_ISOCHRONE,
|
||||
number_of_angles=MATRIX_NUM_ANGLES,
|
||||
max_iterations=MATRIX_MAX_ITERS,
|
||||
tolerance=MATRIX_TOLERANCE)
|
||||
isochrones.append(MapboxIsochronesResponse(coordinates,
|
||||
time_range))
|
||||
return isochrones
|
||||
|
||||
def calculate_isodistance(self, origin, distance_range,
|
||||
profile=DEFAULT_PROFILE):
|
||||
@@ -85,7 +91,7 @@ class MapboxIsolines():
|
||||
time_range = distance_range / max_speed
|
||||
|
||||
return self.calculate_isochrone(origin=origin,
|
||||
time_range=time_range,
|
||||
time_ranges=[time_range],
|
||||
profile=profile)
|
||||
|
||||
def calculate_isoline(self, origin, isorange, upper_rmax,
|
||||
@@ -145,3 +151,18 @@ class MapboxIsolines():
|
||||
location_estimates_filtered.append(location_estimates[i])
|
||||
|
||||
return location_estimates_filtered
|
||||
|
||||
|
||||
class MapboxIsochronesResponse:
|
||||
|
||||
def __init__(self, coordinates, duration):
|
||||
self._coordinates = coordinates
|
||||
self._duration = duration
|
||||
|
||||
@property
|
||||
def coordinates(self):
|
||||
return self._coordinates
|
||||
|
||||
@property
|
||||
def duration(self):
|
||||
return self._duration
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import plpy
|
||||
|
||||
|
||||
class Coordinate:
|
||||
"""Class that represents a generic form of coordinates to be used
|
||||
by the services """
|
||||
@@ -34,6 +37,26 @@ def validate_coordinates(coordinates,
|
||||
min=num_coordinates_min,
|
||||
max=num_coordinates_max))
|
||||
|
||||
|
||||
def marshall_coordinates(coordinates):
|
||||
return ';'.join([str(coordinate).replace(' ', '')
|
||||
for coordinate in coordinates])
|
||||
|
||||
|
||||
def coordinates_to_polygon(coordinates):
|
||||
"""Convert a Coordinate array coordinates to a PostGIS polygon"""
|
||||
coordinates.append(coordinates[0]) # Close the ring
|
||||
result_coordinates = []
|
||||
for coordinate in coordinates:
|
||||
result_coordinates.append("%s %s" % (coordinate.longitude,
|
||||
coordinate.latitude))
|
||||
wkt_coordinates = ','.join(result_coordinates)
|
||||
|
||||
try:
|
||||
sql = "SELECT ST_CollectionExtract(ST_MakeValid(ST_MakePolygon(ST_GeomFromText('LINESTRING({0})', 4326))),3) as geom".format(wkt_coordinates)
|
||||
geometry = plpy.execute(sql, 1)[0]['geom']
|
||||
except BaseException as e:
|
||||
plpy.warning("Can't generate POLYGON from coordinates: {0}".format(e))
|
||||
geometry = None
|
||||
|
||||
return geometry
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from itertools import tee, izip
|
||||
from math import trunc
|
||||
import plpy
|
||||
|
||||
|
||||
class PolyLine:
|
||||
@@ -46,3 +45,22 @@ class PolyLine:
|
||||
coordinate |= c << (i * 5)
|
||||
|
||||
return coordinate
|
||||
|
||||
|
||||
def polyline_to_linestring(polyline):
|
||||
"""Convert a Mapzen polyline shape to a PostGIS multipolygon"""
|
||||
coordinates = []
|
||||
for point in polyline:
|
||||
# Divide by 10 because mapzen uses one more decimal than the
|
||||
# google standard (https://mapzen.com/documentation/turn-by-turn/decoding/)
|
||||
coordinates.append("%s %s" % (point[1]/10, point[0]/10))
|
||||
wkt_coordinates = ','.join(coordinates)
|
||||
|
||||
try:
|
||||
sql = "SELECT ST_GeomFromText('LINESTRING({0})', 4326) as geom".format(wkt_coordinates)
|
||||
geometry = plpy.execute(sql, 1)[0]['geom']
|
||||
except BaseException as e:
|
||||
plpy.warning("Can't generate LINESTRING from polyline: {0}".format(e))
|
||||
geometry = None
|
||||
|
||||
return geometry
|
||||
|
||||
@@ -19,11 +19,11 @@ class MapboxIsolinesTestCase(unittest.TestCase):
|
||||
self.mapbox_isolines = MapboxIsolines(matrix_client, logger=Mock())
|
||||
|
||||
def test_calculate_isochrone(self):
|
||||
time_range = 10 * 60 # 10 minutes
|
||||
time_ranges = [300, 900]
|
||||
solution = self.mapbox_isolines.calculate_isochrone(
|
||||
origin=VALID_ORIGIN,
|
||||
profile=DEFAULT_PROFILE,
|
||||
time_range=time_range)
|
||||
time_ranges=time_ranges)
|
||||
|
||||
assert solution
|
||||
|
||||
|
||||
Reference in New Issue
Block a user