From fa3f2fbc3a201999de0d8d2a359c023dc949de69 Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 4 Jan 2018 17:58:46 +0100 Subject: [PATCH] Modified SQL files --- server/extension/sql/100_routing_helper.sql | 67 +++++++++ .../sql/105_route_between_points.sql | 10 +- server/extension/sql/20_geocode_street.sql | 66 ++++++++- server/extension/sql/50_namedplaces.sql | 61 +++++++- server/extension/sql/80_isolines_helper.sql | 136 ++++++++++++++++++ server/extension/sql/85_isodistance.sql | 17 +++ server/extension/sql/90_isochrone.sql | 16 +++ 7 files changed, 361 insertions(+), 12 deletions(-) diff --git a/server/extension/sql/100_routing_helper.sql b/server/extension/sql/100_routing_helper.sql index 9fd865d..f17ca62 100644 --- a/server/extension/sql/100_routing_helper.sql +++ b/server/extension/sql/100_routing_helper.sql @@ -4,6 +4,73 @@ CREATE TYPE cdb_dataservices_server.simple_route AS ( duration integer ); +CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapbox_route_with_waypoints( + username TEXT, + orgname TEXT, + waypoints geometry(Point, 4326)[], + mode TEXT) +RETURNS cdb_dataservices_server.simple_route AS $$ + import json + from cartodb_services.mapbox import MapboxRouting, MapboxRoutingResponse + from cartodb_services.mapbox.types import MODE_TO_MAPBOX_PROFILE + from cartodb_services.metrics import QuotaService + from cartodb_services.tools import Coordinate + from cartodb_services.tools import Logger,LoggerConfig + from cartodb_services.tools.polyline import polyline_to_linestring + + redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection'] + user_routing_config = GD["user_routing_config_{0}".format(username)] + + plpy.execute("SELECT cdb_dataservices_server._get_logger_config()") + logger_config = GD["logger_config"] + logger = Logger(logger_config) + + quota_service = QuotaService(user_routing_config, redis_conn) + if not quota_service.check_user_quota(): + raise Exception('You have reached the limit of your quota') + + try: + client = MapboxRouting(user_routing_config.mapbox_api_key, logger, user_routing_config.mapbox_service_params) + + if not waypoints or len(waypoints) < 2: + logger.info("Empty origin or destination") + quota_service.increment_empty_service_use() + return [None, None, None] + + if len(waypoints) > 25: + logger.info("Too many waypoints (max 25)") + quota_service.increment_empty_service_use() + return [None, None, None] + + waypoint_coords = [] + for waypoint in waypoints: + lat = plpy.execute("SELECT ST_Y('%s') AS lat" % waypoint)[0]['lat'] + lon = plpy.execute("SELECT ST_X('%s') AS lon" % waypoint)[0]['lon'] + waypoint_coords.append(Coordinate(lon,lat)) + + profile = MODE_TO_MAPBOX_PROFILE.get(mode) + + resp = client.directions(waypoint_coords, profile) + if resp and resp.shape: + shape_linestring = polyline_to_linestring(resp.shape) + if shape_linestring: + quota_service.increment_success_service_use() + return [shape_linestring, resp.length, int(round(resp.duration))] + else: + quota_service.increment_empty_service_use() + return [None, None, None] + else: + quota_service.increment_empty_service_use() + return [None, None, None] + except BaseException as e: + import sys + quota_service.increment_failed_service_use() + logger.error('Error trying to calculate Mapbox routing', sys.exc_info(), data={"username": username, "orgname": orgname}) + raise Exception('Error trying to calculate Mapbox routing') + finally: + quota_service.increment_total_service_use() +$$ LANGUAGE plpythonu SECURITY DEFINER STABLE PARALLEL RESTRICTED; + CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapzen_route_with_waypoints( username TEXT, orgname TEXT, diff --git a/server/extension/sql/105_route_between_points.sql b/server/extension/sql/105_route_between_points.sql index 45a0c2e..728c943 100644 --- a/server/extension/sql/105_route_between_points.sql +++ b/server/extension/sql/105_route_between_points.sql @@ -9,6 +9,7 @@ CREATE OR REPLACE FUNCTION cdb_dataservices_server.cdb_route_point_to_point( RETURNS cdb_dataservices_server.simple_route AS $$ from cartodb_services.metrics import metrics from cartodb_services.tools import Logger + plpy.execute("SELECT cdb_dataservices_server._connect_to_redis('{0}')".format(username)) redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection'] plpy.execute("SELECT cdb_dataservices_server._get_routing_config({0}, {1})".format(plpy.quote_nullable(username), plpy.quote_nullable(orgname))) @@ -19,8 +20,8 @@ RETURNS cdb_dataservices_server.simple_route AS $$ with metrics('cdb_route_with_point', user_routing_config, logger): waypoints = [origin, destination] - mapzen_plan = plpy.prepare("SELECT * FROM cdb_dataservices_server._cdb_mapzen_route_with_waypoints($1, $2, $3, $4, $5, $6) as route;", ["text", "text", "geometry(Point, 4326)[]", "text", "text[]", "text"]) - result = plpy.execute(mapzen_plan, [username, orgname, waypoints, mode, options, units]) + mapbox_plan = plpy.prepare("SELECT * FROM cdb_dataservices_server._cdb_mapbox_route_with_waypoints($1, $2, $3, $4) as route;", ["text", "text", "geometry(Point, 4326)[]", "text"]) + result = plpy.execute(mapbox_plan, [username, orgname, waypoints, mode]) return [result[0]['shape'],result[0]['length'], result[0]['duration']] $$ LANGUAGE plpythonu STABLE PARALLEL RESTRICTED; @@ -35,6 +36,7 @@ CREATE OR REPLACE FUNCTION cdb_dataservices_server.cdb_route_with_waypoints( RETURNS cdb_dataservices_server.simple_route AS $$ from cartodb_services.metrics import metrics from cartodb_services.tools import Logger + plpy.execute("SELECT cdb_dataservices_server._connect_to_redis('{0}')".format(username)) redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection'] plpy.execute("SELECT cdb_dataservices_server._get_routing_config({0}, {1})".format(plpy.quote_nullable(username), plpy.quote_nullable(orgname))) @@ -44,7 +46,7 @@ RETURNS cdb_dataservices_server.simple_route AS $$ logger = Logger(logger_config) with metrics('cdb_route_with_waypoints', user_routing_config, logger): - mapzen_plan = plpy.prepare("SELECT * FROM cdb_dataservices_server._cdb_mapzen_route_with_waypoints($1, $2, $3, $4, $5, $6) as route;", ["text", "text", "geometry(Point, 4326)[]", "text", "text[]", "text"]) - result = plpy.execute(mapzen_plan, [username, orgname, waypoints, mode, options, units]) + mapbox_plan = plpy.prepare("SELECT * FROM cdb_dataservices_server._cdb_mapbox_route_with_waypoints($1, $2, $3, $4) as route;", ["text", "text", "geometry(Point, 4326)[]", "text"]) + result = plpy.execute(mapbox_plan, [username, orgname, waypoints, mode]) return [result[0]['shape'],result[0]['length'], result[0]['duration']] $$ LANGUAGE plpythonu STABLE PARALLEL RESTRICTED; diff --git a/server/extension/sql/20_geocode_street.sql b/server/extension/sql/20_geocode_street.sql index 388bac9..370e495 100644 --- a/server/extension/sql/20_geocode_street.sql +++ b/server/extension/sql/20_geocode_street.sql @@ -1,4 +1,5 @@ -- Geocodes a street address given a searchtext and a state and/or country + CREATE OR REPLACE FUNCTION cdb_dataservices_server.cdb_geocode_street_point(username TEXT, orgname TEXT, searchtext TEXT, city TEXT DEFAULT NULL, state_province TEXT DEFAULT NULL, country TEXT DEFAULT NULL) RETURNS Geometry AS $$ from cartodb_services.metrics import metrics @@ -21,6 +22,9 @@ RETURNS Geometry AS $$ elif user_geocoder_config.mapzen_geocoder: mapzen_plan = plpy.prepare("SELECT cdb_dataservices_server._cdb_mapzen_geocode_street_point($1, $2, $3, $4, $5, $6) as point; ", ["text", "text", "text", "text", "text", "text"]) return plpy.execute(mapzen_plan, [username, orgname, searchtext, city, state_province, country], 1)[0]['point'] + elif user_geocoder_config.mapbox_geocoder: + mapbox_plan = plpy.prepare("SELECT cdb_dataservices_server._cdb_mapbox_geocode_street_point($1, $2, $3, $4, $5, $6) as point; ", ["text", "text", "text", "text", "text", "text"]) + return plpy.execute(mapbox_plan, [username, orgname, searchtext, city, state_province, country], 1)[0]['point'] else: raise Exception('Requested geocoder is not available') @@ -70,6 +74,19 @@ RETURNS Geometry AS $$ $$ LANGUAGE plpythonu STABLE PARALLEL RESTRICTED; +CREATE OR REPLACE FUNCTION cdb_dataservices_server.cdb_mapbox_geocode_street_point(username TEXT, orgname TEXT, searchtext TEXT, city TEXT DEFAULT NULL, state_province TEXT DEFAULT NULL, country TEXT DEFAULT NULL) +RETURNS Geometry AS $$ + # The configuration is retrieved but no checks are performed on it + plpy.execute("SELECT cdb_dataservices_server._connect_to_redis('{0}')".format(username)) + redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection'] + plpy.execute("SELECT cdb_dataservices_server._get_geocoder_config({0}, {1})".format(plpy.quote_nullable(username), plpy.quote_nullable(orgname))) + user_geocoder_config = GD["user_geocoder_config_{0}".format(username)] + + mapzen_plan = plpy.prepare("SELECT cdb_dataservices_server._cdb_mapbox_geocode_street_point($1, $2, $3, $4, $5, $6) as point; ", ["text", "text", "text", "text", "text", "text"]) + return plpy.execute(mapzen_plan, [username, orgname, searchtext, city, state_province, country], 1)[0]['point'] + +$$ LANGUAGE plpythonu STABLE PARALLEL RESTRICTED; + CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_here_geocode_street_point(username TEXT, orgname TEXT, searchtext TEXT, city TEXT DEFAULT NULL, state_province TEXT DEFAULT NULL, country TEXT DEFAULT NULL) RETURNS Geometry AS $$ from cartodb_services.tools import LegacyServiceManager @@ -132,7 +149,7 @@ CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapzen_geocode_street_po RETURNS Geometry AS $$ from cartodb_services.tools import ServiceManager from cartodb_services.mapzen import MapzenGeocoder - from cartodb_services.mapzen.types import country_to_iso3 + from cartodb_services.tools.country import country_to_iso3 from cartodb_services.refactor.service.mapzen_geocoder_config import MapzenGeocoderConfigBuilder import cartodb_services @@ -165,3 +182,50 @@ RETURNS Geometry AS $$ finally: service_manager.quota_service.increment_total_service_use() $$ LANGUAGE plpythonu STABLE PARALLEL RESTRICTED; + +CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapbox_geocode_street_point(username TEXT, orgname TEXT, searchtext TEXT, city TEXT DEFAULT NULL, state_province TEXT DEFAULT NULL, country TEXT DEFAULT NULL) +RETURNS Geometry AS $$ + from cartodb_services.mapbox import MapboxGeocoder + from cartodb_services.metrics import QuotaService, metrics + from cartodb_services.tools import Logger,LoggerConfig + from cartodb_services.tools.country import country_to_iso3 + + plpy.execute("SELECT cdb_dataservices_server._connect_to_redis('{0}')".format(username)) + redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection'] + plpy.execute("SELECT cdb_dataservices_server._get_geocoder_config({0}, {1}, {2})".format(plpy.quote_nullable(username), plpy.quote_nullable(orgname), plpy.quote_nullable('mapbox'))) + user_geocoder_config = GD["user_geocoder_config_{0}".format(username)] + + plpy.execute("SELECT cdb_dataservices_server._get_logger_config()") + logger_config = GD["logger_config"] + logger = Logger(logger_config) + quota_service = QuotaService(user_geocoder_config, redis_conn) + if not quota_service.check_user_quota(): + raise Exception('You have reached the limit of your quota') + + with metrics('cdb_mapbox_geocode_street_point', user_geocoder_config, logger): + try: + geocoder = MapboxGeocoder(user_geocoder_config.mapbox_api_key, logger, user_geocoder_config.mapbox_service_params) + + country_iso3 = None + if country: + country_iso3 = country_to_iso3(country) + + coordinates = geocoder.geocode(searchtext=searchtext, city=city, + state_province=state_province, + country=country_iso3) + if coordinates: + quota_service.increment_success_service_use() + plan = plpy.prepare("SELECT ST_SetSRID(ST_MakePoint($1, $2), 4326); ", ["double precision", "double precision"]) + point = plpy.execute(plan, [coordinates[0], coordinates[1]], 1)[0] + return point['st_setsrid'] + else: + quota_service.increment_empty_service_use() + return None + except BaseException as e: + import sys + quota_service.increment_failed_service_use() + logger.error('Error trying to geocode street point using mapbox', sys.exc_info(), data={"username": username, "orgname": orgname}) + raise Exception('Error trying to geocode street point using mapbox') + finally: + quota_service.increment_total_service_use() +$$ LANGUAGE plpythonu STABLE PARALLEL RESTRICTED; diff --git a/server/extension/sql/50_namedplaces.sql b/server/extension/sql/50_namedplaces.sql index e876bdd..c6a93b9 100644 --- a/server/extension/sql/50_namedplaces.sql +++ b/server/extension/sql/50_namedplaces.sql @@ -3,8 +3,8 @@ CREATE OR REPLACE FUNCTION cdb_dataservices_server.cdb_geocode_namedplace_point( RETURNS Geometry AS $$ import spiexceptions try: - mapzen_plan = plpy.prepare("SELECT cdb_dataservices_server._cdb_mapzen_geocode_namedplace($1, $2, $3) as point;", ["text", "text", "text"]) - return plpy.execute(mapzen_plan, [username, orgname, city_name])[0]['point'] + mapbox_plan = plpy.prepare("SELECT cdb_dataservices_server._cdb_mapbox_geocode_namedplace($1, $2, $3) as point;", ["text", "text", "text"]) + return plpy.execute(mapbox_plan, [username, orgname, city_name])[0]['point'] except spiexceptions.ExternalRoutineException as e: internal_plan = plpy.prepare("SELECT cdb_dataservices_server._cdb_internal_geocode_namedplace($1, $2, $3) as point;", ["text", "text", "text"]) return plpy.execute(internal_plan, [username, orgname, city_name])[0]['point'] @@ -15,8 +15,8 @@ CREATE OR REPLACE FUNCTION cdb_dataservices_server.cdb_geocode_namedplace_point( RETURNS Geometry AS $$ import spiexceptions try: - mapzen_plan = plpy.prepare("SELECT cdb_dataservices_server._cdb_mapzen_geocode_namedplace($1, $2, $3, NULL, $4) as point;", ["text", "text", "text", "text"]) - return plpy.execute(mapzen_plan, [username, orgname, city_name, country_name])[0]['point'] + mapbox_plan = plpy.prepare("SELECT cdb_dataservices_server._cdb_mapbox_geocode_namedplace($1, $2, $3, NULL, $4) as point;", ["text", "text", "text", "text"]) + return plpy.execute(mapbox_plan, [username, orgname, city_name, country_name])[0]['point'] except spiexceptions.ExternalRoutineException as e: internal_plan = plpy.prepare("SELECT cdb_dataservices_server._cdb_internal_geocode_namedplace($1, $2, $3, NULL, $4) as point;", ["text", "text", "text", "text"]) return plpy.execute(internal_plan, [username, orgname, city_name, country_name])[0]['point'] @@ -27,17 +27,64 @@ CREATE OR REPLACE FUNCTION cdb_dataservices_server.cdb_geocode_namedplace_point( RETURNS Geometry AS $$ import spiexceptions try: - mapzen_plan = plpy.prepare("SELECT cdb_dataservices_server._cdb_mapzen_geocode_namedplace($1, $2, $3, $4, $5) as point;", ["text", "text", "text", "text", "text"]) - return plpy.execute(mapzen_plan, [username, orgname, city_name, admin1_name, country_name])[0]['point'] + mapbox_plan = plpy.prepare("SELECT cdb_dataservices_server._cdb_mapbox_geocode_namedplace($1, $2, $3, $4, $5) as point;", ["text", "text", "text", "text", "text"]) + return plpy.execute(mapbox_plan, [username, orgname, city_name, admin1_name, country_name])[0]['point'] except spiexceptions.ExternalRoutineException as e: internal_plan = plpy.prepare("SELECT cdb_dataservices_server._cdb_internal_geocode_namedplace($1, $2, $3, $4, $5) as point;", ["text", "text", "text", "text", "text"]) return plpy.execute(internal_plan, [username, orgname, city_name, admin1_name, country_name])[0]['point'] $$ LANGUAGE plpythonu STABLE PARALLEL RESTRICTED; +CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapbox_geocode_namedplace(username text, orgname text, city_name text, admin1_name text DEFAULT NULL, country_name text DEFAULT NULL) +RETURNS Geometry AS $$ + from cartodb_services.mapbox import MapboxGeocoder + from cartodb_services.metrics import QuotaService, metrics + from cartodb_services.tools import Logger,LoggerConfig + from cartodb_services.tools.country import country_to_iso3 + + plpy.execute("SELECT cdb_dataservices_server._connect_to_redis('{0}')".format(username)) + redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection'] + plpy.execute("SELECT cdb_dataservices_server._get_geocoder_config({0}, {1}, {2})".format(plpy.quote_nullable(username), plpy.quote_nullable(orgname), plpy.quote_nullable('mapbox'))) + user_geocoder_config = GD["user_geocoder_config_{0}".format(username)] + + plpy.execute("SELECT cdb_dataservices_server._get_logger_config()") + logger_config = GD["logger_config"] + logger = Logger(logger_config) + quota_service = QuotaService(user_geocoder_config, redis_conn) + if not quota_service.check_user_quota(): + raise Exception('You have reached the limit of your quota') + + with metrics('cdb_geocode_namedplace_point', user_geocoder_config, logger): + try: + geocoder = MapboxGeocoder(user_geocoder_config.mapbox_api_key, logger) + + country_iso3 = None + if country_name: + country_iso3 = country_to_iso3(country_name) + + coordinates = geocoder.geocode(searchtext=city_name, city=None, + state_province=admin1_name, + country=country_iso3) + if coordinates: + quota_service.increment_success_service_use() + plan = plpy.prepare("SELECT ST_SetSRID(ST_MakePoint($1, $2), 4326); ", ["double precision", "double precision"]) + point = plpy.execute(plan, [coordinates[0], coordinates[1]], 1)[0] + return point['st_setsrid'] + else: + quota_service.increment_empty_service_use() + return None + except BaseException as e: + import sys + quota_service.increment_failed_service_use() + logger.error('Error trying to geocode city point using mapbox', sys.exc_info(), data={"username": username, "orgname": orgname}) + raise Exception('Error trying to geocode city point using mapbox') + finally: + quota_service.increment_total_service_use() +$$ LANGUAGE plpythonu STABLE PARALLEL RESTRICTED; + CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapzen_geocode_namedplace(username text, orgname text, city_name text, admin1_name text DEFAULT NULL, country_name text DEFAULT NULL) RETURNS Geometry AS $$ from cartodb_services.mapzen import MapzenGeocoder - from cartodb_services.mapzen.types import country_to_iso3 + from cartodb_services.tools.country import country_to_iso3 from cartodb_services.metrics import QuotaService, metrics from cartodb_services.tools import Logger,LoggerConfig diff --git a/server/extension/sql/80_isolines_helper.sql b/server/extension/sql/80_isolines_helper.sql index 1694936..f7dc7e3 100644 --- a/server/extension/sql/80_isolines_helper.sql +++ b/server/extension/sql/80_isolines_helper.sql @@ -123,6 +123,75 @@ RETURNS SETOF cdb_dataservices_server.isoline AS $$ quota_service.increment_total_service_use() $$ LANGUAGE plpythonu SECURITY DEFINER STABLE PARALLEL RESTRICTED; +CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapbox_isodistance( + username TEXT, + orgname TEXT, + source geometry(Geometry, 4326), + mode TEXT, + data_range integer[], + options text[]) +RETURNS SETOF cdb_dataservices_server.isoline AS $$ + import json + from cartodb_services.mapbox import MapboxMatrixClient, MapboxIsolines + from cartodb_services.mapbox.types import MODE_TO_MAPBOX_PROFILE + from cartodb_services.tools import Coordinate + from cartodb_services.metrics import QuotaService + from cartodb_services.tools import Logger,LoggerConfig + + redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection'] + user_isolines_routing_config = GD["user_isolines_routing_config_{0}".format(username)] + + plpy.execute("SELECT cdb_dataservices_server._get_logger_config()") + logger_config = GD["logger_config"] + logger = Logger(logger_config) + quota_service = QuotaService(user_isolines_routing_config, redis_conn) + if not quota_service.check_user_quota(): + raise Exception('You have reached the limit of your quota') + + try: + client = MapboxMatrixClient(user_isolines_routing_config.mapbox_matrix_api_key, logger, user_isolines_routing_config.mapbox_matrix_service_params) + mapbox_isolines = MapboxIsolines(client, logger) + + if source: + lat = plpy.execute("SELECT ST_Y('%s') AS lat" % source)[0]['lat'] + lon = plpy.execute("SELECT ST_X('%s') AS lon" % source)[0]['lon'] + origin = Coordinate(lon,lat) + else: + raise Exception('source is NULL') + + profile = MODE_TO_MAPBOX_PROFILE.get(mode) + + # -- TODO Support options properly + isolines = {} + for r in data_range: + isoline = mapbox_isolines.calculate_isodistance(origin, r, profile) + isolines[r] = isoline + + result = [] + for r in data_range: + + if len(isolines[r]) >= 3: + # -- TODO encapsulate this block into a func/method + locations = isolines[r] + [ isolines[r][0] ] # close the polygon repeating the first point + wkt_coordinates = ','.join(["%f %f" % (l.longitude, l.latitude) for l in locations]) + sql = "SELECT ST_MPolyFromText('MULTIPOLYGON((({0})))', 4326) as geom".format(wkt_coordinates) + multipolygon = plpy.execute(sql, 1)[0]['geom'] + else: + multipolygon = None + + result.append([source, r, multipolygon]) + + quota_service.increment_success_service_use() + quota_service.increment_isolines_service_use(len(isolines)) + return result + except BaseException as e: + import sys + quota_service.increment_failed_service_use() + logger.error('Error trying to get Mapbox isolines', sys.exc_info(), data={"username": username, "orgname": orgname}) + raise Exception('Error trying to get Mapbox isolines') + finally: + quota_service.increment_total_service_use() +$$ LANGUAGE plpythonu SECURITY DEFINER STABLE PARALLEL RESTRICTED; CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapzen_isochrones( username TEXT, @@ -186,3 +255,70 @@ RETURNS SETOF cdb_dataservices_server.isoline AS $$ finally: quota_service.increment_total_service_use() $$ LANGUAGE plpythonu SECURITY DEFINER STABLE PARALLEL RESTRICTED; + +CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapbox_isochrones( + username TEXT, + orgname TEXT, + source geometry(Geometry, 4326), + mode TEXT, + data_range integer[], + options text[]) +RETURNS SETOF cdb_dataservices_server.isoline AS $$ + import json + from cartodb_services.mapbox import MapboxMatrixClient, MapboxIsolines + from cartodb_services.mapbox.types import MODE_TO_MAPBOX_PROFILE + from cartodb_services.tools import Coordinate + from cartodb_services.tools.coordinates import coordinates_to_polygon + from cartodb_services.metrics import QuotaService + from cartodb_services.tools import Logger,LoggerConfig + + redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection'] + user_isolines_routing_config = GD["user_isolines_routing_config_{0}".format(username)] + + plpy.execute("SELECT cdb_dataservices_server._get_logger_config()") + logger_config = GD["logger_config"] + logger = Logger(logger_config) + # -- Check the quota + quota_service = QuotaService(user_isolines_routing_config, redis_conn) + if not quota_service.check_user_quota(): + raise Exception('You have reached the limit of your quota') + + try: + client = MapboxMatrixClient(user_isolines_routing_config.mapbox_matrix_api_key, logger, user_isolines_routing_config.mapbox_matrix_service_params) + mapbox_isolines = MapboxIsolines(client, logger) + + if source: + lat = plpy.execute("SELECT ST_Y('%s') AS lat" % source)[0]['lat'] + lon = plpy.execute("SELECT ST_X('%s') AS lon" % source)[0]['lon'] + origin = Coordinate(lon,lat) + else: + raise Exception('source is NULL') + + profile = MODE_TO_MAPBOX_PROFILE.get(mode) + + resp = mapbox_isolines.calculate_isochrone(origin, data_range, profile) + + if resp: + result = [] + for isochrone in resp: + result_polygon = coordinates_to_polygon(isochrone.coordinates) + if result_polygon: + quota_service.increment_success_service_use() + result.append([source, isochrone.duration, result_polygon]) + else: + quota_service.increment_empty_service_use() + result.append([source, isochrone.duration, None]) + quota_service.increment_success_service_use() + quota_service.increment_isolines_service_use(len(result)) + return result + else: + quota_service.increment_empty_service_use() + return [] + except BaseException as e: + import sys + quota_service.increment_failed_service_use() + logger.error('Error trying to get Mapbox isochrones', sys.exc_info(), data={"username": username, "orgname": orgname}) + raise Exception('Error trying to get Mapbox isochrones') + finally: + quota_service.increment_total_service_use() +$$ LANGUAGE plpythonu SECURITY DEFINER STABLE PARALLEL RESTRICTED; diff --git a/server/extension/sql/85_isodistance.sql b/server/extension/sql/85_isodistance.sql index fe602c4..fc73199 100644 --- a/server/extension/sql/85_isodistance.sql +++ b/server/extension/sql/85_isodistance.sql @@ -21,6 +21,9 @@ RETURNS SETOF cdb_dataservices_server.isoline AS $$ elif user_isolines_config.mapzen_provider: mapzen_plan = plpy.prepare("SELECT * FROM cdb_dataservices_server.cdb_mapzen_isodistance($1, $2, $3, $4, $5, $6) as isoline; ", ["text", "text", "geometry(geometry, 4326)", "text", "integer[]", "text[]"]) return plpy.execute(mapzen_plan, [username, orgname, source, mode, range, options]) + elif user_isolines_config.mapbox_provider: + mapbox_plan = plpy.prepare("SELECT * FROM cdb_dataservices_server.cdb_mapbox_isodistance($1, $2, $3, $4, $5, $6) as isoline; ", ["text", "text", "geometry(geometry, 4326)", "text", "integer[]", "text[]"]) + return plpy.execute(mapbox_plan, [username, orgname, source, mode, range, options]) else: raise Exception('Requested isolines provider is not available') $$ LANGUAGE plpythonu STABLE PARALLEL RESTRICTED; @@ -53,3 +56,17 @@ RETURNS SETOF cdb_dataservices_server.isoline AS $$ return result $$ LANGUAGE plpythonu STABLE PARALLEL RESTRICTED; + +-- mapbox isodistance +CREATE OR REPLACE FUNCTION cdb_dataservices_server.cdb_mapbox_isodistance(username TEXT, orgname TEXT, source geometry(Geometry, 4326), mode TEXT, range integer[], options text[] DEFAULT array[]::text[]) +RETURNS SETOF cdb_dataservices_server.isoline AS $$ + plpy.execute("SELECT cdb_dataservices_server._connect_to_redis('{0}')".format(username)) + redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection'] + plpy.execute("SELECT cdb_dataservices_server._get_isolines_routing_config({0}, {1})".format(plpy.quote_nullable(username), plpy.quote_nullable(orgname))) + user_isolines_config = GD["user_isolines_routing_config_{0}".format(username)] + + mapbox_plan = plpy.prepare("SELECT * FROM cdb_dataservices_server._cdb_mapbox_isodistance($1, $2, $3, $4, $5, $6) as isoline; ", ["text", "text", "geometry(geometry, 4326)", "text", "integer[]", "text[]"]) + result = plpy.execute(mapbox_plan, [username, orgname, source, mode, range, options]) + + return result +$$ LANGUAGE plpythonu STABLE PARALLEL RESTRICTED; diff --git a/server/extension/sql/90_isochrone.sql b/server/extension/sql/90_isochrone.sql index 6c88bca..1f3e798 100644 --- a/server/extension/sql/90_isochrone.sql +++ b/server/extension/sql/90_isochrone.sql @@ -21,6 +21,9 @@ RETURNS SETOF cdb_dataservices_server.isoline AS $$ elif user_isolines_config.mapzen_provider: mapzen_plan = plpy.prepare("SELECT * FROM cdb_dataservices_server.cdb_mapzen_isochrone($1, $2, $3, $4, $5, $6) as isoline; ", ["text", "text", "geometry(geometry, 4326)", "text", "integer[]", "text[]"]) return plpy.execute(mapzen_plan, [username, orgname, source, mode, range, options]) + elif user_isolines_config.mapbox_provider: + mapbox_plan = plpy.prepare("SELECT * FROM cdb_dataservices_server.cdb_mapbox_isochrone($1, $2, $3, $4, $5, $6) as isoline; ", ["text", "text", "geometry(geometry, 4326)", "text", "integer[]", "text[]"]) + return plpy.execute(mapbox_plan, [username, orgname, source, mode, range, options]) else: raise Exception('Requested isolines provider is not available') $$ LANGUAGE plpythonu STABLE PARALLEL RESTRICTED; @@ -52,3 +55,16 @@ RETURNS SETOF cdb_dataservices_server.isoline AS $$ result = plpy.execute(mapzen_plan, [username, orgname, source, mode, range, options]) return result $$ LANGUAGE plpythonu STABLE PARALLEL RESTRICTED; + +-- mapbox isochrone +CREATE OR REPLACE FUNCTION cdb_dataservices_server.cdb_mapbox_isochrone(username TEXT, orgname TEXT, source geometry(Geometry, 4326), mode TEXT, range integer[], options text[] DEFAULT array[]::text[]) +RETURNS SETOF cdb_dataservices_server.isoline AS $$ + plpy.execute("SELECT cdb_dataservices_server._connect_to_redis('{0}')".format(username)) + redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection'] + plpy.execute("SELECT cdb_dataservices_server._get_isolines_routing_config({0}, {1})".format(plpy.quote_nullable(username), plpy.quote_nullable(orgname))) + user_isolines_config = GD["user_isolines_routing_config_{0}".format(username)] + + mapbox_plan = plpy.prepare("SELECT * FROM cdb_dataservices_server._cdb_mapbox_isochrones($1, $2, $3, $4, $5, $6) as isoline; ", ["text", "text", "geometry(geometry, 4326)", "text", "integer[]", "text[]"]) + result = plpy.execute(mapbox_plan, [username, orgname, source, mode, range, options]) + return result +$$ LANGUAGE plpythonu STABLE PARALLEL RESTRICTED;