Compare commits

...

83 Commits

Author SHA1 Message Date
Mario de Frutos
e70aefba1d Merge pull request #491 from CartoDB/development
Release 0.30.5 for server and 0.17.6 for python library
2018-03-28 10:38:06 +02:00
Mario de Frutos
423fe42007 Merge pull request #488 from CartoDB/cc211-Isolines_should_always_return_multipolygon
Cc211 isolines should always return multipolygon
2018-03-28 10:35:43 +02:00
Mario de Frutos
5e141e3a10 Update NEWS.md 2018-03-28 10:34:45 +02:00
Mario de Frutos
50aa537a7a Bump python library version 2018-03-27 16:36:04 +02:00
Mario de Frutos
e25e2c26a0 Remove the PARALLEL modifiers from the version SQL artifact 2018-03-27 16:36:04 +02:00
Juan Ignacio Sánchez Lara
8025f657b8 Upgrade version scripts 2018-03-27 16:36:04 +02:00
Juan Ignacio Sánchez Lara
2a47000f32 coordinates_to_polygon should always return multipolygon to avoid inconsistencies 2018-03-27 16:36:04 +02:00
Mario de Frutos
53e9ad4d2e Merge pull request #490 from CartoDB/development
Release python library 0.17.5
2018-03-27 16:23:28 +02:00
Mario de Frutos
ea5818f08f Update NEWS.md 2018-03-27 16:20:29 +02:00
Mario de Frutos
2f8edbe5ce Merge pull request #476 from CartoDB/Avoid_reaching_provider_for_empty_geocodings
Avoid reaching provider for empty geocodings
2018-03-27 16:12:33 +02:00
Antonio
552a7d4886 Fixing Mapbox geocoding validity checks 2018-03-16 16:55:08 +01:00
Antonio
a34da0adb5 Fixed CR suggestions 2018-03-16 16:37:17 +01:00
Antonio
ab5ef64e83 Fixed CR suggestions 2018-03-16 16:35:28 +01:00
Antonio
f0cef02dfc Fixed CR suggestions 2018-03-16 16:33:45 +01:00
Antonio Carlón
59940ea994 Merge pull request #487 from CartoDB/development
Release server 0.30.4
2018-03-16 12:28:52 +01:00
Antonio Carlón
b9da6c9ec5 Merge pull request #486 from CartoDB/485-Add_CollectionExtract_to_MakeValid
Added ST_CollectionExtract
2018-03-16 12:26:06 +01:00
Antonio
ace7683ffe Added suggestion from CR 2018-03-16 12:14:14 +01:00
Antonio
6017b53ea0 Server version 0.30.4 2018-03-16 12:07:20 +01:00
Antonio
e15c6127d3 Added ST_CollectionExtract 2018-03-16 12:01:12 +01:00
Antonio Carlón
4bd8726720 Merge pull request #484 from CartoDB/development
Release server 0.30.3
2018-03-16 11:19:46 +01:00
Antonio
5d20d0bdf5 Updated NEWS.md 2018-03-16 11:16:57 +01:00
Antonio Carlón
db5ead6f98 Merge pull request #483 from CartoDB/1381MakeValidPolygonsForIsoMapboxDistances
Make valid polygons for iso mapbox distances
2018-03-16 11:13:29 +01:00
Ubuntu
44fae489a3 Update scripts 2018-03-15 15:09:21 +00:00
Alex Martín
71c93fe13a Add ST_MakeValid to sql 2018-03-15 15:58:15 +01:00
Antonio Carlón
d9c569881a Merge pull request #481 from CartoDB/development
Release python 0.17.4
2018-03-14 17:49:33 +01:00
Antonio Carlón
244d579f6f Merge pull request #480 from CartoDB/479-Fix_get_service_quota_info
Fixed error when checking quota
2018-03-14 17:41:22 +01:00
Antonio
c90859e58b Fixed error when checking quota 2018-03-14 16:26:00 +01:00
Antonio Carlón
12eecc271d Merge branch 'development' into Avoid_reaching_provider_for_empty_geocodings 2018-03-14 15:12:58 +01:00
Antonio Carlón
f216b6d922 Merge pull request #478 from CartoDB/development
Release python 0.17.3
2018-03-14 14:21:46 +01:00
Antonio Carlón
573a304bd2 Merge pull request #477 from CartoDB/fix_routing_quota
Fix routing quota
2018-03-14 14:18:00 +01:00
Antonio
f5cbc195cc Fixed tests 2018-03-14 13:31:00 +01:00
Mario de Frutos
e324afd77f Update NEWS.md 2018-03-14 11:20:56 +01:00
Mario de Frutos
0196292093 Bump version 2018-03-14 11:20:12 +01:00
Mario de Frutos
f652a52a8d Use routing quota instead of monthly quota 2018-03-14 11:19:48 +01:00
Antonio
1c38b33501 Updated server side scripts 2018-03-14 09:55:07 +01:00
Antonio
59751d7c9c Avoid reaching provider for empty geocodings 2018-03-13 16:01:05 +01:00
Juan Ignacio Sánchez Lara
b279fafbc5 Merge pull request #474 from CartoDB/development
0.17.2 version of Python library
2018-03-05 16:17:11 +01:00
Juan Ignacio Sánchez Lara
07ed2a4112 Merge pull request #471 from CartoDB/1333-Mapbox_isolines_almost_shoreline
Fix Mapbox isolines near the shoreline
2018-03-05 16:14:45 +01:00
Antonio
35f743164e Keep deleting points that got None 2018-03-02 10:27:37 +01:00
Antonio
4308b5f351 Remove too far away points (filtering by time) 2018-03-02 10:23:58 +01:00
Antonio
9fb04fdc24 Version bumped 2018-02-28 11:49:23 +01:00
Antonio
a2fd0bf142 Merge branch 'master' into 1333-Mapbox_isolines_almost_shoreline 2018-02-28 11:48:00 +01:00
Antonio Carlón
6534b12606 Merge pull request #473 from CartoDB/development
Version 0.17.1 of the python library
2018-02-28 11:37:52 +01:00
Antonio Carlón
8d9c3a4bf7 Merge pull request #472 from CartoDB/fix_mapzen_routing
Fix mapzen routing bug
2018-02-28 11:35:26 +01:00
Mario de Frutos
ad46de1156 Bump version and update NEWS.md 2018-02-27 18:11:50 +01:00
Mario de Frutos
97f1611d62 Only get mapzen/mapbox values in case the provider of routing is one of these 2018-02-27 18:09:21 +01:00
Antonio
bc4c9fea33 Using Mapbox returned destinations as coordinates for isolines 2018-02-26 12:45:08 +01:00
Antonio Carlón
e110ab4cc3 Merge pull request #469 from CartoDB/development
Release 0.17.0
2018-02-22 12:46:34 +01:00
Antonio Carlón
e646000f24 Merge pull request #468 from CartoDB/remove_legacy_mapzen
Remove legacy mapzen
2018-02-22 12:41:48 +01:00
Mario de Frutos
05e2cc981e Change min log level for tests to WARNING 2018-02-22 11:20:16 +01:00
Mario de Frutos
cbc19b869c Fix CR suggestions 2018-02-22 11:14:33 +01:00
Mario de Frutos
199788748b Updated NEWS.md 2018-02-21 19:14:56 +01:00
Mario de Frutos
e3f23adfdd Bump to version 0.17.0 2018-02-21 19:14:47 +01:00
Mario de Frutos
39dabffb85 Now is not mandatory to have mapzen configuration becuase its deprecated as provider 2018-02-21 19:14:29 +01:00
Mario de Frutos
03e1d1ca61 Change default provider to mapbox instead of mapzen 2018-02-21 19:13:59 +01:00
Mario de Frutos
c14fb057d3 Update README.md 2018-02-14 11:03:51 +01:00
Mario de Frutos
9e247685b8 Merge pull request #465 from CartoDB/457-Fix_Mapbox_Python_tests
457 fix mapbox python tests
2018-02-14 10:21:29 +01:00
Juan Ignacio Sánchez Lara
1fdb4d3b3a Pythonic refactor, importing single method 2018-02-13 18:24:21 +01:00
Juan Ignacio Sánchez Lara
029541f298 api_key -> mapbox_api_key rename refactor 2018-02-13 16:16:13 +01:00
Juan Ignacio Sánchez Lara
45d9edbba6 Mapbox test API key is supplied now through a MAPBOX_API_KEY environment variable 2018-02-13 15:58:10 +01:00
Mario de Frutos
39c54f3e0c Merge pull request #464 from CartoDB/development
Release 0.16.7
2018-02-13 13:26:44 +01:00
Mario de Frutos
54e40645fa Update NEWS.md 2018-02-13 11:51:06 +01:00
Mario de Frutos
a86b8e86f9 Merge pull request #462 from CartoDB/Fixing_mapbox_request_errors
Fixed Mapbox requests and responses
2018-02-13 11:46:45 +01:00
Antonio
8674dabeb2 Version bumped 2018-02-13 08:38:54 +01:00
Antonio
080a386b8f Fixed mapbox requests and responses 2018-02-12 18:02:37 +01:00
Antonio Carlón
972aba6cfb Merge pull request #459 from CartoDB/development
Mapbox permanent geocoder. Documentation
2018-02-12 12:54:40 +01:00
Antonio Carlón
9b43e8a92e Merge pull request #456 from CartoDB/455-Use_Mapbox_permanent_geocoder
Use Mapbox permanent geocoder
2018-02-12 12:45:09 +01:00
Antonio
4e311aef47 Updated NEWS.md 2018-02-12 12:40:56 +01:00
Antonio
b65d003742 Using Mapbox permanent geocoder by default 2018-02-09 09:07:31 +01:00
Iñigo Medina
b171951bc7 Merge pull request #421 from CartoDB/docs-1266-replace-content
updating content as per docs issue 1266, ready for review but do not …
2018-02-06 12:32:30 +01:00
Mario de Frutos
462773a138 Merge pull request #453 from CartoDB/development
Release python library version 0.16.5
2018-02-05 09:39:47 +01:00
Mario de Frutos
fad2f25183 Updated NEWS.md 2018-02-05 09:35:40 +01:00
Mario de Frutos
2f54ef7e4e Merge pull request #452 from CartoDB/fix_misplaced_coordinate_polyline
Multiple fixes
2018-02-05 09:30:24 +01:00
Mario de Frutos
ab4d77edf2 Merge pull request #377 from CartoDB/logger_conf_wrong_square_brackets
Removed wrong square brackets
2018-02-02 18:16:36 +01:00
Mario de Frutos
0b12f26f47 Add info about square brackets in server configuration of the README 2018-02-02 18:15:40 +01:00
Mario de Frutos
a116aba660 Merge pull request #379 from CartoDB/missing_grant_usage
Missing GRANT USAGE
2018-02-02 18:11:47 +01:00
Mario de Frutos
29d5a332b6 Change to reflect the current used user for the DS
Check this issue https://github.com/CartoDB/dataservices-api/issues/380
which talks about the renaming for a more proper name
2018-02-02 18:10:02 +01:00
Juan Ignacio Sánchez Lara
b2825f46a4 Missing GRANT USAGE 2018-02-02 18:08:01 +01:00
Mario de Frutos
a791d02dcc 422 errors from mapbox return empty response 2018-02-02 14:11:22 +01:00
Mario de Frutos
e2612645c3 Bump to version 0.16.5 2018-02-02 13:18:47 +01:00
Mario de Frutos
c5fed2cc80 Check for empty coordinates when we transform to polygon 2018-02-02 13:18:32 +01:00
Mario de Frutos
a1f339376e By default the polyline comes with 6 decimals so we avoid to use 5 by default 2018-02-02 13:17:56 +01:00
csobier
7775d2373d updating content as per docs issue 1266, ready for review but do not merge until given notice 2018-01-04 08:29:02 -05:00
43 changed files with 10731 additions and 105 deletions

59
NEWS.md
View File

@@ -1,3 +1,62 @@
March 28th, 2018
================
* Version `0.30.5` of server side and `0.17.6` of the python library
* All the returned polygons are now always MULTIPOLYGON #488
March 27th, 2018
================
* Version `0.17.5` of python library
* Avoid reaching provider for empty geocodings (but still incrementing empty service use) #476
March 16th, 2018
================
* Version `0.30.3` of server side
* Fix problem with invalid Mapbox isolines #483
* Version `0.30.4` of server side
* Added ST_CollectionExtract to ST_MakeValid for Mapbox isolines to avoid non-polygonal geometries #486
March 14th, 2018
================
* Version `0.17.4` of the python library
* Fix bug with previous version when checking quotas #480
* Version `0.17.3` of the python library
* Fix bug with Mapbox routing not using the proper quota value #477
February 22th, 2018
==================
* Version `0.17.2` of the python library
* Fix bug with Mapbox isolines not stopping at the seacoast #471
February 27th, 2018
==================
* Version `0.17.1` of the python library
* Fix bug when the mapzen credentials are not in the db config and we keep getting them
February 22th, 2018
==================
* Version `0.17.0` of the python library
* Change default provider to Mapbox
* Remove the obligatory nature of the Mapzen configuration due to its deprecation as provider
February 13th, 2018
==================
* Version `0.16.7` of the python library
* Pick the first result when Mapbox geocoder returns multiple results #462
* Normalize input for Mapbox geocoder #462
February 12th, 2018
==================
* Version `0.16.6` of the python library
* Using Mapbox permanent geocoder #455
February 5th, 2018
==================
* Version `0.16.5` of the python library
* Fix displaced routing shape #443
* Check for empty coordinates object before converting it to polygon
* 422 errors that come from Mapbox now returns an empty result because is a bad input from the user data
February 2th, 2018
==================
* Version `0.16.4` of the python library

View File

@@ -45,7 +45,7 @@ Steps to deploy a new Data Services API version :
```sql
CREATE DATABASE dataservices_db ENCODING = 'UTF8' LC_COLLATE = 'en_US.UTF-8' LC_CTYPE = 'en_US.UTF-8';
CREATE USER dataservices_user;
CREATE USER geocoder_api;
```
- Install needed extensions in `dataservices_db` database
@@ -90,19 +90,23 @@ Steps to deploy a new Data Services API version :
```
psql -U postgres -d dataservices_db -f src/pg/test/fixtures/load_fixtures.sql
```
- Give permission to execute and select to the `dataservices_user` user:
- Give permission to execute and select to the `geocoder_api` user:
```
psql -U postgres -d dataservices_db -c "BEGIN;CREATE EXTENSION IF NOT EXISTS observatory VERSION 'dev'; COMMIT" -e
psql -U postgres -d dataservices_db -c "BEGIN;GRANT SELECT ON ALL TABLES IN SCHEMA cdb_observatory TO dataservices_user; COMMIT" -e
psql -U postgres -d dataservices_db -c "BEGIN;GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA cdb_observatory TO dataservices_user; COMMIT" -e
psql -U postgres -d dataservices_db -c "BEGIN;GRANT SELECT ON ALL TABLES IN SCHEMA observatory TO dataservices_user; COMMIT" -e
psql -U postgres -d dataservices_db -c "BEGIN;GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA observatory TO dataservices_user; COMMIT" -e
psql -U postgres -d dataservices_db -c "BEGIN;GRANT SELECT ON ALL TABLES IN SCHEMA cdb_observatory TO geocoder_api; COMMIT" -e
psql -U postgres -d dataservices_db -c "BEGIN;GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA cdb_observatory TO geocoder_api; COMMIT" -e
psql -U postgres -d dataservices_db -c "BEGIN;GRANT USAGE ON SCHEMA cdb_observatory TO geocoder_api; COMMIT" -e
psql -U postgres -d dataservices_db -c "BEGIN;GRANT SELECT ON ALL TABLES IN SCHEMA observatory TO geocoder_api; COMMIT" -e
psql -U postgres -d dataservices_db -c "BEGIN;GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA observatory TO geocoder_api; COMMIT" -e
psql -U postgres -d dataservices_db -c "BEGIN;GRANT USAGE ON SCHEMA observatory TO geocoder_api; COMMIT" -e
```
### Server configuration
Configuration for the different services must be stored in the server database using `CDB_Conf_SetConf()`.
**All the configuration inside brackets [] is optional**
#### Redis configuration
If sentinel is used:

View File

@@ -1,3 +1,5 @@
-- Only show warning or error messages in the tests output
SET client_min_messages TO WARNING;
-- Install dependencies
CREATE EXTENSION postgis;
CREATE EXTENSION plpythonu;

View File

@@ -1,3 +1,5 @@
-- Only show warning or error messages in the tests output
SET client_min_messages TO WARNING;
-- Install dependencies
CREATE EXTENSION postgis;
CREATE EXTENSION plpythonu;

View File

@@ -4,7 +4,7 @@ The [geocoder](https://carto.com/data/geocoder-api/) functions allow you to matc
_**This service is subject to quota limitations and extra fees may apply**. View the [Quota Information](https://carto.com/docs/carto-engine/dataservices-api/quota-information/) section for details and recommendations about to quota consumption._
Here is an example of how to geocode a single country:
The following example displays how to geocode a single country:
```bash
https://{username}.carto.com/api/v2/sql?q=SELECT cdb_geocode_admin0_polygon('USA')&api_key={api_key}
@@ -312,7 +312,7 @@ INSERT INTO {tablename} (the_geom) SELECT cdb_geocode_ipaddress_point('102.23.34
## Street-Level Geocoder
This function geocodes your data into a point geometry for a street address. CARTO uses several different service providers for street-level geocoding, depending on your platform. If you access CARTO on a Google Cloud Platform, [Google Maps geocoding](https://developers.google.com/maps/documentation/geocoding/intro) is applied. All other platform users are provided with [HERE geocoding services](https://developer.here.com/rest-apis/documentation/geocoder/topics/quick-start.html). Additional service providers will be implemented in the future.
This function geocodes your data into a point geometry for a street address. CARTO uses several different service providers for street-level geocoding, depending on your platform. If you access CARTO on a Google Cloud Platform, [Google Maps geocoding](https://developers.google.com/maps/documentation/geocoding/intro) is applied. All other platform users are provided with [Mapbox geocoding services](https://www.mapbox.com/). [Contact us](mailto:sales@carto.com) if you have any specific questions or requirements about the location data service provider being used with your account._.
**This service is subject to quota limitations, and extra fees may apply**. View the [Quota information](https://carto.com/docs/carto-engine/dataservices-api/quota-information/) for details and recommendations about quota consumption.

View File

@@ -10,7 +10,7 @@ You can use the isoline functions to retrieve, for example, isochrone lines from
https://{username}.carto.com/api/v2/sql?q=INSERT INTO {table} (the_geom) SELECT the_geom FROM cdb_isodistance('POINT(-3.70568 40.42028)'::geometry, 'car', ARRAY[300, 600, 900]::integer[])&api_key={api_key}
```
The following functions provide an isoline generator service, based on time or distance. This service uses the isolines service defined for your account. The default service limits the usage of displayed polygons represented on top of [HERE](https://developer.here.com/coverage-info) maps.
The following functions provide an isoline generator service, based on time or distance. This service uses the isolines service defined for your account. The default service limits the usage of displayed polygons represented on top of [Mapbox](https://www.mapbox.com/) maps.
## cdb_isodistance(_source geometry, mode text, range integer[], [options text[]]_)

View File

@@ -4,7 +4,7 @@ By using CARTO libraries and the SQL API, you can apply location data services t
**Note:** Based on your account plan, some of these data services are subject to different [quota limitations](https://carto.com/docs/carto-engine/dataservices-api/quota-information/#quota-information).
_The Data Services API is collaborating with [Mapzen](https://mapzen.com/), and several other geospatial service providers, in order to supply the best location data services from within our CARTO Engine._
_In order to supply the best location data services from within our CARTO Engine, the Data Services API collaborates with [Mapbox](https://www.mapbox.com/) and several other geospatial service providers. [Contact us](mailto:sales@carto.com) if you have any specific questions or requirements about the location data service provider being used with your account._
## Data Services Integration

View File

@@ -59,9 +59,9 @@ Result:
```sql
service | monthly_quota | used_quota | soft_limit | provider
----------------+---------------+------------+------------+------------------
isolines | 100 | 0 | f | mapzen
hires_geocoder | 100 | 0 | f | mapzen
routing | 50 | 0 | f | mapzen
isolines | 100 | 0 | f | mapbox
hires_geocoder | 100 | 0 | f | mapbox
routing | 50 | 0 | f | mapbox
observatory | 0 | 0 | f | data observatory
(4 rows)
@@ -100,7 +100,7 @@ Suppose you want to geocode a whole table. In order to check that you have enoug
SELECT COUNT(*) FROM {tablename} WHERE {street_name_column} IS NOT NULL;
```
Result: here's a sample result of 10000 records:
Result: A sample result of 10000 records:
```sql
count

View File

@@ -0,0 +1,68 @@
--DO NOT MODIFY THIS FILE, IT IS GENERATED AUTOMATICALLY FROM SOURCES
-- Complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION cdb_dataservices_server UPDATE TO '0.30.5'" to load this file. \quit
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 $$
from cartodb_services.tools import ServiceManager
from cartodb_services.mapbox import MapboxMatrixClient, MapboxIsolines
from cartodb_services.mapbox.types import TRANSPORT_MODE_TO_MAPBOX
from cartodb_services.tools import Coordinate
from cartodb_services.refactor.service.mapbox_isolines_config import MapboxIsolinesConfigBuilder
import cartodb_services
cartodb_services.init(plpy, GD)
service_manager = ServiceManager('isolines', MapboxIsolinesConfigBuilder, username, orgname, GD)
service_manager.assert_within_limits()
try:
client = MapboxMatrixClient(service_manager.config.mapbox_api_key, service_manager.logger, service_manager.config.service_params)
mapbox_isolines = MapboxIsolines(client, service_manager.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 = TRANSPORT_MODE_TO_MAPBOX.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_multi(ST_CollectionExtract(ST_MakeValid(ST_MPolyFromText('MULTIPOLYGON((({0})))', 4326)),3)) as geom".format(wkt_coordinates)
multipolygon = plpy.execute(sql, 1)[0]['geom']
else:
multipolygon = None
result.append([source, r, multipolygon])
service_manager.quota_service.increment_success_service_use()
service_manager.quota_service.increment_isolines_service_use(len(isolines))
return result
except BaseException as e:
import sys
service_manager.quota_service.increment_failed_service_use()
service_manager.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:
service_manager.quota_service.increment_total_service_use()
$$ LANGUAGE plpythonu SECURITY DEFINER STABLE PARALLEL RESTRICTED;

View File

@@ -0,0 +1,68 @@
--DO NOT MODIFY THIS FILE, IT IS GENERATED AUTOMATICALLY FROM SOURCES
-- Complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION cdb_dataservices_server UPDATE TO '0.30.4'" to load this file. \quit
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 $$
from cartodb_services.tools import ServiceManager
from cartodb_services.mapbox import MapboxMatrixClient, MapboxIsolines
from cartodb_services.mapbox.types import TRANSPORT_MODE_TO_MAPBOX
from cartodb_services.tools import Coordinate
from cartodb_services.refactor.service.mapbox_isolines_config import MapboxIsolinesConfigBuilder
import cartodb_services
cartodb_services.init(plpy, GD)
service_manager = ServiceManager('isolines', MapboxIsolinesConfigBuilder, username, orgname, GD)
service_manager.assert_within_limits()
try:
client = MapboxMatrixClient(service_manager.config.mapbox_api_key, service_manager.logger, service_manager.config.service_params)
mapbox_isolines = MapboxIsolines(client, service_manager.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 = TRANSPORT_MODE_TO_MAPBOX.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_CollectionExtract(ST_MakeValid(ST_MPolyFromText('MULTIPOLYGON((({0})))', 4326)),3) as geom".format(wkt_coordinates)
multipolygon = plpy.execute(sql, 1)[0]['geom']
else:
multipolygon = None
result.append([source, r, multipolygon])
service_manager.quota_service.increment_success_service_use()
service_manager.quota_service.increment_isolines_service_use(len(isolines))
return result
except BaseException as e:
import sys
service_manager.quota_service.increment_failed_service_use()
service_manager.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:
service_manager.quota_service.increment_total_service_use()
$$ LANGUAGE plpythonu SECURITY DEFINER STABLE PARALLEL RESTRICTED;

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
comment = 'CartoDB dataservices server extension'
default_version = '0.30.2'
default_version = '0.30.5'
requires = 'plpythonu, plproxy, postgis, cdb_geocoder'
superuser = true
schema = cdb_dataservices_server

View File

@@ -0,0 +1,70 @@
--DO NOT MODIFY THIS FILE, IT IS GENERATED AUTOMATICALLY FROM SOURCES
-- Complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION cdb_dataservices_server UPDATE TO '0.30.3'" to load this file. \quit
-- HERE goes your code to upgrade/downgrade
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 $$
from cartodb_services.tools import ServiceManager
from cartodb_services.mapbox import MapboxMatrixClient, MapboxIsolines
from cartodb_services.mapbox.types import TRANSPORT_MODE_TO_MAPBOX
from cartodb_services.tools import Coordinate
from cartodb_services.refactor.service.mapbox_isolines_config import MapboxIsolinesConfigBuilder
import cartodb_services
cartodb_services.init(plpy, GD)
service_manager = ServiceManager('isolines', MapboxIsolinesConfigBuilder, username, orgname, GD)
service_manager.assert_within_limits()
try:
client = MapboxMatrixClient(service_manager.config.mapbox_api_key, service_manager.logger, service_manager.config.service_params)
mapbox_isolines = MapboxIsolines(client, service_manager.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 = TRANSPORT_MODE_TO_MAPBOX.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_MakeValid(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])
service_manager.quota_service.increment_success_service_use()
service_manager.quota_service.increment_isolines_service_use(len(isolines))
return result
except BaseException as e:
import sys
service_manager.quota_service.increment_failed_service_use()
service_manager.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:
service_manager.quota_service.increment_total_service_use()
$$ LANGUAGE plpythonu SECURITY DEFINER STABLE PARALLEL RESTRICTED;

View File

@@ -0,0 +1,70 @@
--DO NOT MODIFY THIS FILE, IT IS GENERATED AUTOMATICALLY FROM SOURCES
-- Complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION cdb_dataservices_server UPDATE TO '0.30.2'" to load this file. \quit
-- HERE goes your code to upgrade/downgrade
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 $$
from cartodb_services.tools import ServiceManager
from cartodb_services.mapbox import MapboxMatrixClient, MapboxIsolines
from cartodb_services.mapbox.types import TRANSPORT_MODE_TO_MAPBOX
from cartodb_services.tools import Coordinate
from cartodb_services.refactor.service.mapbox_isolines_config import MapboxIsolinesConfigBuilder
import cartodb_services
cartodb_services.init(plpy, GD)
service_manager = ServiceManager('isolines', MapboxIsolinesConfigBuilder, username, orgname, GD)
service_manager.assert_within_limits()
try:
client = MapboxMatrixClient(service_manager.config.mapbox_api_key, service_manager.logger, service_manager.config.service_params)
mapbox_isolines = MapboxIsolines(client, service_manager.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 = TRANSPORT_MODE_TO_MAPBOX.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])
service_manager.quota_service.increment_success_service_use()
service_manager.quota_service.increment_isolines_service_use(len(isolines))
return result
except BaseException as e:
import sys
service_manager.quota_service.increment_failed_service_use()
service_manager.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:
service_manager.quota_service.increment_total_service_use()
$$ LANGUAGE plpythonu SECURITY DEFINER STABLE PARALLEL RESTRICTED;

View File

@@ -0,0 +1,69 @@
--DO NOT MODIFY THIS FILE, IT IS GENERATED AUTOMATICALLY FROM SOURCES
-- Complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION cdb_dataservices_server UPDATE TO '0.30.4'" to load this file. \quit
-- HERE goes your code to upgrade/downgrade
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 $$
from cartodb_services.tools import ServiceManager
from cartodb_services.mapbox import MapboxMatrixClient, MapboxIsolines
from cartodb_services.mapbox.types import TRANSPORT_MODE_TO_MAPBOX
from cartodb_services.tools import Coordinate
from cartodb_services.refactor.service.mapbox_isolines_config import MapboxIsolinesConfigBuilder
import cartodb_services
cartodb_services.init(plpy, GD)
service_manager = ServiceManager('isolines', MapboxIsolinesConfigBuilder, username, orgname, GD)
service_manager.assert_within_limits()
try:
client = MapboxMatrixClient(service_manager.config.mapbox_api_key, service_manager.logger, service_manager.config.service_params)
mapbox_isolines = MapboxIsolines(client, service_manager.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 = TRANSPORT_MODE_TO_MAPBOX.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_CollectionExtract(ST_MakeValid(ST_MPolyFromText('MULTIPOLYGON((({0})))', 4326)),3) as geom".format(wkt_coordinates)
multipolygon = plpy.execute(sql, 1)[0]['geom']
else:
multipolygon = None
result.append([source, r, multipolygon])
service_manager.quota_service.increment_success_service_use()
service_manager.quota_service.increment_isolines_service_use(len(isolines))
return result
except BaseException as e:
import sys
service_manager.quota_service.increment_failed_service_use()
service_manager.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:
service_manager.quota_service.increment_total_service_use()
$$ LANGUAGE plpythonu SECURITY DEFINER STABLE PARALLEL RESTRICTED;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,69 @@
--DO NOT MODIFY THIS FILE, IT IS GENERATED AUTOMATICALLY FROM SOURCES
-- Complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION cdb_dataservices_server UPDATE TO '0.30.3'" to load this file. \quit
-- HERE goes your code to upgrade/downgrade
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 $$
from cartodb_services.tools import ServiceManager
from cartodb_services.mapbox import MapboxMatrixClient, MapboxIsolines
from cartodb_services.mapbox.types import TRANSPORT_MODE_TO_MAPBOX
from cartodb_services.tools import Coordinate
from cartodb_services.refactor.service.mapbox_isolines_config import MapboxIsolinesConfigBuilder
import cartodb_services
cartodb_services.init(plpy, GD)
service_manager = ServiceManager('isolines', MapboxIsolinesConfigBuilder, username, orgname, GD)
service_manager.assert_within_limits()
try:
client = MapboxMatrixClient(service_manager.config.mapbox_api_key, service_manager.logger, service_manager.config.service_params)
mapbox_isolines = MapboxIsolines(client, service_manager.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 = TRANSPORT_MODE_TO_MAPBOX.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_MakeValid(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])
service_manager.quota_service.increment_success_service_use()
service_manager.quota_service.increment_isolines_service_use(len(isolines))
return result
except BaseException as e:
import sys
service_manager.quota_service.increment_failed_service_use()
service_manager.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:
service_manager.quota_service.increment_total_service_use()
$$ LANGUAGE plpythonu SECURITY DEFINER STABLE PARALLEL RESTRICTED;

File diff suppressed because it is too large Load Diff

View File

@@ -169,7 +169,7 @@ RETURNS SETOF cdb_dataservices_server.isoline AS $$
# -- 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)
sql = "SELECT st_multi(ST_CollectionExtract(ST_MakeValid(ST_MPolyFromText('MULTIPOLYGON((({0})))', 4326)),3)) as geom".format(wkt_coordinates)
multipolygon = plpy.execute(sql, 1)[0]['geom']
else:
multipolygon = None

View File

@@ -1,3 +1,5 @@
-- Only show warning or error messages in the tests output
SET client_min_messages TO WARNING;
-- Install dependencies
CREATE EXTENSION postgis;
CREATE EXTENSION plpythonu;

View File

@@ -1,3 +1,5 @@
-- Only show warning or error messages in the tests output
SET client_min_messages TO WARNING;
-- Install dependencies
CREATE EXTENSION postgis;
CREATE EXTENSION plpythonu;

View File

@@ -24,7 +24,7 @@ NOTE: a system installation is required at present because the library is meant
## Running the unit tests
Just run `nosetests test/`
Just run `MAPBOX_API_KEY=xxx nosetests test/`
```shell
$ nosetests test/
......................................................................................................

View File

@@ -8,11 +8,12 @@ from mapbox import Geocoder
from cartodb_services.metrics import Traceable
from cartodb_services.tools.exceptions import ServiceException
from cartodb_services.tools.qps import qps_retry
from cartodb_services.tools.normalize import normalize
GEOCODER_NAME = 'geocoder_name'
EPHEMERAL_GEOCODER = 'mapbox.places'
PERMANENT_GEOCODER = 'mapbox.places-permanent'
DEFAULT_GEOCODER = EPHEMERAL_GEOCODER
DEFAULT_GEOCODER = PERMANENT_GEOCODER
ENTRY_FEATURES = 'features'
ENTRY_CENTER = 'center'
@@ -32,17 +33,23 @@ class MapboxGeocoder(Traceable):
self._token = token
self._logger = logger
self._geocoder_name = service_params.get(GEOCODER_NAME,
EPHEMERAL_GEOCODER)
DEFAULT_GEOCODER)
self._geocoder = Geocoder(access_token=self._token,
name=self._geocoder_name)
def _parse_geocoder_response(self, response):
json_response = json.loads(response)
if json_response and json_response[ENTRY_FEATURES]:
feature = json_response[ENTRY_FEATURES][0]
# If Mapbox returns more that one result, take the first one
if json_response:
if type(json_response) == list:
json_response = json_response[0]
return self._extract_lng_lat_from_feature(feature)
if json_response[ENTRY_FEATURES]:
feature = json_response[ENTRY_FEATURES][0]
return self._extract_lng_lat_from_feature(feature)
else:
return []
else:
return []
@@ -57,18 +64,31 @@ class MapboxGeocoder(Traceable):
latitude = location[1]
return [longitude, latitude]
def _validate_input(self, searchtext, city=None, state_province=None,
country=None):
if searchtext and searchtext.strip():
return True
elif city:
return True
elif state_province:
return True
return False
@qps_retry(qps=10)
def geocode(self, searchtext, city=None, state_province=None,
country=None):
if searchtext and searchtext.strip():
address = [searchtext]
if city:
address.append(city)
if state_province:
address.append(state_province)
else:
if not self._validate_input(searchtext, city, state_province, country):
return []
address = []
if searchtext and searchtext.strip():
address.append(normalize(searchtext))
if city:
address.append(normalize(city))
if state_province:
address.append(normalize(state_province))
country = [country] if country else None
try:
@@ -80,6 +100,8 @@ class MapboxGeocoder(Traceable):
return self._parse_geocoder_response(response.text)
elif response.status_code == requests.codes.bad_request:
return []
elif response.status_code == requests.codes.unprocessable_entity:
return []
else:
raise ServiceException(response.status_code, response)
except requests.Timeout as te:

View File

@@ -4,6 +4,7 @@ Uses the Mapbox Time Matrix service.
'''
import json
from cartodb_services.tools import Coordinate
from cartodb_services.tools.spherical import (get_angles,
calculate_dest_location)
from cartodb_services.mapbox.matrix_client import (validate_profile,
@@ -11,7 +12,9 @@ from cartodb_services.mapbox.matrix_client import (validate_profile,
PROFILE_WALKING,
PROFILE_DRIVING,
PROFILE_CYCLING,
ENTRY_DURATIONS)
ENTRY_DURATIONS,
ENTRY_DESTINATIONS,
ENTRY_LOCATION)
MAX_SPEEDS = {
PROFILE_WALKING: 3.3333333, # In m/s, assuming 12km/h walking speed
@@ -49,8 +52,11 @@ class MapboxIsolines():
response = self._matrix_client.matrix([origin] + targets,
profile)
json_response = json.loads(response)
if not json_response:
return []
costs = [None] * number_of_angles
destinations = [None] * number_of_angles
for idx, cost in enumerate(json_response[ENTRY_DURATIONS][0][1:]):
if cost:
@@ -58,7 +64,11 @@ class MapboxIsolines():
else:
costs[idx] = isorange
return costs
for idx, destination in enumerate(json_response[ENTRY_DESTINATIONS][1:]):
destinations[idx] = Coordinate(destination[ENTRY_LOCATION][0],
destination[ENTRY_LOCATION][1])
return costs, destinations
def calculate_isochrone(self, origin, time_ranges,
profile=DEFAULT_PROFILE):
@@ -120,10 +130,15 @@ class MapboxIsolines():
# NOTE: sometimes it cannot calculate the cost and returns None.
# Just assume isorange and stop the calculations there
costs = cost_method(origin=origin, targets=location_estimates,
isorange=isorange, profile=profile,
unit_factor=unit_factor,
number_of_angles=number_of_angles)
costs, destinations = cost_method(origin=origin,
targets=location_estimates,
isorange=isorange,
profile=profile,
unit_factor=unit_factor,
number_of_angles=number_of_angles)
if not costs:
continue
errors = [(cost - isorange) / float(isorange) for cost in costs]
max_abs_error = max([abs(e) for e in errors])
@@ -147,8 +162,8 @@ class MapboxIsolines():
# delete points that got None
location_estimates_filtered = []
for i, c in enumerate(costs):
if c != isorange:
location_estimates_filtered.append(location_estimates[i])
if c != isorange and c < isorange * (1 + tolerance):
location_estimates_filtered.append(destinations[i])
return location_estimates_filtered

View File

@@ -30,6 +30,8 @@ VALID_PROFILES = [PROFILE_DRIVING_TRAFFIC,
PROFILE_WALKING]
ENTRY_DURATIONS = 'durations'
ENTRY_DESTINATIONS = 'destinations'
ENTRY_LOCATION = 'location'
def validate_profile(profile):
@@ -72,6 +74,8 @@ class MapboxMatrixClient(Traceable):
return response.text
elif response.status_code == requests.codes.bad_request:
return '{}'
elif response.status_code == requests.codes.unprocessable_entity:
return '{}'
else:
raise ServiceException(response.status_code, response)
except requests.Timeout as te:

View File

@@ -91,6 +91,8 @@ class MapboxRouting(Traceable):
return self._parse_routing_response(response.text)
elif response.status_code == requests.codes.bad_request:
return MapboxRoutingResponse(None, None, None)
elif response.status_code == requests.codes.unprocessable_entity:
return MapboxRoutingResponse(None, None, None)
else:
raise ServiceException(response.status_code, response)
except requests.Timeout as te:

View File

@@ -28,7 +28,7 @@ def coordinates_to_polygon(coordinates):
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)
sql = "SELECT st_multi(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))

View File

@@ -136,7 +136,7 @@ class RoutingConfig(ServiceConfig):
ROUTING_PROVIDER_KEY = 'routing_provider'
MAPZEN_PROVIDER = 'mapzen'
MAPBOX_PROVIDER = 'mapbox'
DEFAULT_PROVIDER = MAPZEN_PROVIDER
DEFAULT_PROVIDER = MAPBOX_PROVIDER
QUOTA_KEY = 'mapzen_routing_quota'
SOFT_LIMIT_KEY = 'soft_mapzen_routing_limit'
METRICS_LOG_KEY = 'routing_log_path'
@@ -147,11 +147,13 @@ class RoutingConfig(ServiceConfig):
self._routing_provider = self._redis_config[self.ROUTING_PROVIDER_KEY]
if not self._routing_provider:
self._routing_provider = self.DEFAULT_PROVIDER
self._mapzen_api_key = self._db_config.mapzen_routing_api_key
self._mapzen_service_params = self._db_config.mapzen_routing_service_params
self._mapbox_api_keys = self._db_config.mapbox_routing_api_keys
self._mapbox_service_params = self._db_config.mapbox_routing_service_params
self._set_monthly_quota()
if self._routing_provider == self.MAPZEN_PROVIDER:
self._mapzen_api_key = self._db_config.mapzen_routing_api_key
self._mapzen_service_params = self._db_config.mapzen_routing_service_params
elif self._routing_provider == self.MAPBOX_PROVIDER:
self._mapbox_api_keys = self._db_config.mapbox_routing_api_keys
self._mapbox_service_params = self._db_config.mapbox_routing_service_params
self._routing_quota = self._get_effective_monthly_quota(self.QUOTA_KEY)
self._set_soft_limit()
self._period_end_date = date_parse(self._redis_config[self.PERIOD_END_DATE])
@@ -190,9 +192,13 @@ class RoutingConfig(ServiceConfig):
def mapbox_service_params(self):
return self._mapbox_service_params
@property
def routing_quota(self):
return self._routing_quota
@property
def monthly_quota(self):
return self._monthly_quota
return self._routing_quota
@property
def period_end_date(self):
@@ -202,9 +208,6 @@ class RoutingConfig(ServiceConfig):
def soft_limit(self):
return self._soft_limit
def _set_monthly_quota(self):
self._monthly_quota = self._get_effective_monthly_quota(self.QUOTA_KEY)
def _set_soft_limit(self):
if self.SOFT_LIMIT_KEY in self._redis_config and self._redis_config[self.SOFT_LIMIT_KEY].lower() == 'true':
self._soft_limit = True
@@ -226,7 +229,7 @@ class IsolinesRoutingConfig(ServiceConfig):
MAPZEN_PROVIDER = 'mapzen'
MAPBOX_PROVIDER = 'mapbox'
HEREMAPS_PROVIDER = 'heremaps'
DEFAULT_PROVIDER = MAPZEN_PROVIDER
DEFAULT_PROVIDER = MAPBOX_PROVIDER
METRICS_LOG_KEY = 'isolines_log_path'
def __init__(self, redis_connection, db_conn, username, orgname=None):
@@ -391,7 +394,7 @@ class GeocoderConfig(ServiceConfig):
USERNAME_KEY = 'username'
ORGNAME_KEY = 'orgname'
PERIOD_END_DATE = 'period_end_date'
DEFAULT_PROVIDER = MAPZEN_GEOCODER
DEFAULT_PROVIDER = MAPBOX_GEOCODER
METRICS_LOG_KEY = 'geocoder_log_path'
def __init__(self, redis_connection, db_conn, username, orgname=None, forced_provider=None):
@@ -576,50 +579,51 @@ class ServicesDBConfig:
heremaps_conf_json = self._get_conf('heremaps_conf')
if not heremaps_conf_json:
raise ConfigException('Here maps configuration missing')
else:
heremaps_conf = json.loads(heremaps_conf_json)
self._heremaps_geocoder_app_id = heremaps_conf['geocoder']['app_id']
self._heremaps_geocoder_app_code = heremaps_conf['geocoder']['app_code']
self._heremaps_geocoder_cost_per_hit = heremaps_conf['geocoder'][
'geocoder_cost_per_hit']
self._heremaps_geocoder_service_params = heremaps_conf['geocoder'].get('service', {})
self._heremaps_isolines_app_id = heremaps_conf['isolines']['app_id']
self._heremaps_isolines_app_code = heremaps_conf['isolines']['app_code']
self._heremaps_isolines_service_params = heremaps_conf['isolines'].get('service', {})
heremaps_conf = json.loads(heremaps_conf_json)
self._heremaps_geocoder_app_id = heremaps_conf['geocoder']['app_id']
self._heremaps_geocoder_app_code = heremaps_conf['geocoder']['app_code']
self._heremaps_geocoder_cost_per_hit = heremaps_conf['geocoder'][
'geocoder_cost_per_hit']
self._heremaps_geocoder_service_params = heremaps_conf['geocoder'].get('service', {})
self._heremaps_isolines_app_id = heremaps_conf['isolines']['app_id']
self._heremaps_isolines_app_code = heremaps_conf['isolines']['app_code']
self._heremaps_isolines_service_params = heremaps_conf['isolines'].get('service', {})
def _get_mapzen_config(self):
mapzen_conf_json = self._get_conf('mapzen_conf')
# We dont use mapzen anymore so we don't need to check for its configuration
if not mapzen_conf_json:
raise ConfigException('Mapzen configuration missing')
else:
mapzen_conf = json.loads(mapzen_conf_json)
self._mapzen_matrix_api_key = mapzen_conf['matrix']['api_key']
self._mapzen_matrix_quota = mapzen_conf['matrix']['monthly_quota']
self._mapzen_matrix_service_params = mapzen_conf['matrix'].get('service', {})
self._mapzen_isochrones_service_params = mapzen_conf.get('isochrones', {}).get('service', {})
self._mapzen_routing_api_key = mapzen_conf['routing']['api_key']
self._mapzen_routing_quota = mapzen_conf['routing']['monthly_quota']
self._mapzen_routing_service_params = mapzen_conf['routing'].get('service', {})
self._mapzen_geocoder_api_key = mapzen_conf['geocoder']['api_key']
self._mapzen_geocoder_quota = mapzen_conf['geocoder']['monthly_quota']
self._mapzen_geocoder_service_params = mapzen_conf['geocoder'].get('service', {})
return
mapzen_conf = json.loads(mapzen_conf_json)
self._mapzen_matrix_api_key = mapzen_conf['matrix']['api_key']
self._mapzen_matrix_quota = mapzen_conf['matrix']['monthly_quota']
self._mapzen_matrix_service_params = mapzen_conf['matrix'].get('service', {})
self._mapzen_isochrones_service_params = mapzen_conf.get('isochrones', {}).get('service', {})
self._mapzen_routing_api_key = mapzen_conf['routing']['api_key']
self._mapzen_routing_quota = mapzen_conf['routing']['monthly_quota']
self._mapzen_routing_service_params = mapzen_conf['routing'].get('service', {})
self._mapzen_geocoder_api_key = mapzen_conf['geocoder']['api_key']
self._mapzen_geocoder_quota = mapzen_conf['geocoder']['monthly_quota']
self._mapzen_geocoder_service_params = mapzen_conf['geocoder'].get('service', {})
def _get_mapbox_config(self):
mapbox_conf_json = self._get_conf('mapbox_conf')
if not mapbox_conf_json:
raise ConfigException('Mapbox configuration missing')
else:
mapbox_conf = json.loads(mapbox_conf_json)
self._mapbox_matrix_api_keys = mapbox_conf['matrix']['api_keys']
self._mapbox_matrix_quota = mapbox_conf['matrix']['monthly_quota']
self._mapbox_matrix_service_params = mapbox_conf['matrix'].get('service', {})
self._mapbox_isochrones_service_params = mapbox_conf.get('isochrones', {}).get('service', {})
self._mapbox_routing_api_keys = mapbox_conf['routing']['api_keys']
self._mapbox_routing_quota = mapbox_conf['routing']['monthly_quota']
self._mapbox_routing_service_params = mapbox_conf['routing'].get('service', {})
self._mapbox_geocoder_api_keys = mapbox_conf['geocoder']['api_keys']
self._mapbox_geocoder_quota = mapbox_conf['geocoder']['monthly_quota']
self._mapbox_geocoder_service_params = mapbox_conf['geocoder'].get('service', {})
mapbox_conf = json.loads(mapbox_conf_json)
self._mapbox_matrix_api_keys = mapbox_conf['matrix']['api_keys']
self._mapbox_matrix_quota = mapbox_conf['matrix']['monthly_quota']
self._mapbox_matrix_service_params = mapbox_conf['matrix'].get('service', {})
self._mapbox_isochrones_service_params = mapbox_conf.get('isochrones', {}).get('service', {})
self._mapbox_routing_api_keys = mapbox_conf['routing']['api_keys']
self._mapbox_routing_quota = mapbox_conf['routing']['monthly_quota']
self._mapbox_routing_service_params = mapbox_conf['routing'].get('service', {})
self._mapbox_geocoder_api_keys = mapbox_conf['geocoder']['api_keys']
self._mapbox_geocoder_quota = mapbox_conf['geocoder']['monthly_quota']
self._mapbox_geocoder_service_params = mapbox_conf['geocoder'].get('service', {})
def _get_data_observatory_config(self):
do_conf_json = self._get_conf('data_observatory_conf')

View File

@@ -122,7 +122,7 @@ class QuotaChecker:
return False
def __check_routing_quota(self):
user_quota = self._user_service_config.monthly_quota
user_quota = self._user_service_config.routing_quota
today = date.today()
service_type = self._user_service_config.service_type
current_used = self._user_service.used_quota(service_type, today)

View File

@@ -45,6 +45,8 @@ def marshall_coordinates(coordinates):
def coordinates_to_polygon(coordinates):
"""Convert a Coordinate array coordinates to a PostGIS polygon"""
if not coordinates:
return None
coordinates.append(coordinates[0]) # Close the ring
result_coordinates = []
for coordinate in coordinates:
@@ -53,7 +55,7 @@ def coordinates_to_polygon(coordinates):
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)
sql = "SELECT st_multi(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))

View File

@@ -0,0 +1,3 @@
def normalize(str_input):
return str_input.replace('&quot;', '"') \
.replace(';', ',')

View File

@@ -51,9 +51,7 @@ def polyline_to_linestring(polyline):
"""Convert a Mapzen polyline shape to a PostGIS linestring"""
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))
coordinates.append("%s %s" % (point[1], point[0]))
wkt_coordinates = ','.join(coordinates)
try:

View File

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

View File

@@ -0,0 +1,6 @@
import os
def mapbox_api_key():
"""Returns Mapbox API key. Requires setting MAPBOX_API_KEY environment variable."""
return os.environ['MAPBOX_API_KEY']

View File

@@ -301,7 +301,7 @@ class TestRoutingConfig(TestCase):
self._redis_conn.hset(self._user_key, 'mapzen_routing_quota', 1000)
orgname = None
config = RoutingConfig(self._redis_conn, self._db_conn, self._username, orgname)
assert config.monthly_quota == 1000
assert config.routing_quota == 1000
def test_org_quota_overrides_user_quota(self):
self._redis_conn.hset(self._user_key, 'mapzen_routing_quota', 1000)
@@ -315,7 +315,7 @@ class TestRoutingConfig(TestCase):
self._redis_conn.hset(orgname_key, 'here_isolines_quota', 0)
config = RoutingConfig(self._redis_conn, self._db_conn, self._username, orgname)
assert config.monthly_quota == 5000
assert config.routing_quota == 5000
def test_should_have_soft_limit_false_by_default(self):
orgname = None

View File

@@ -30,7 +30,7 @@ class TestQuotaChecker(TestCase):
username = self.username,
organization = None,
service_type = self.service_type,
monthly_quota = 1000,
routing_quota = 1000,
period_end_date = datetime.today(),
soft_limit = False
)
@@ -43,7 +43,7 @@ class TestQuotaChecker(TestCase):
username = self.username,
organization = None,
service_type = self.service_type,
monthly_quota = 1000,
routing_quota = 1000,
period_end_date = datetime.today(),
soft_limit = False
)
@@ -61,7 +61,7 @@ class TestQuotaChecker(TestCase):
username = self.username,
organization = None,
service_type = self.service_type,
monthly_quota = 1000,
routing_quota = 1000,
period_end_date = datetime.today(),
soft_limit = False
)
@@ -75,7 +75,7 @@ class TestQuotaChecker(TestCase):
username = self.username,
organization = None,
service_type = self.service_type,
monthly_quota = 1000,
routing_quota = 1000,
period_end_date = datetime.today(),
soft_limit = True
)

View File

@@ -2,8 +2,8 @@ import unittest
from mock import Mock
from cartodb_services.mapbox import MapboxGeocoder
from cartodb_services.tools.exceptions import ServiceException
from credentials import mapbox_api_key
VALID_TOKEN = 'pk.eyJ1IjoiYWNhcmxvbiIsImEiOiJjamJuZjQ1Zjc0Ymt4Mnh0YmFrMmhtYnY4In0.gt9cw0VeKc3rM2mV5pcEmg'
INVALID_TOKEN = 'invalid_token'
VALID_ADDRESS = 'Calle Siempreviva 3, Valladolid'
WELL_KNOWN_LONGITUDE = -4.730947
@@ -12,7 +12,7 @@ WELL_KNOWN_LATITUDE = 41.668654
class MapboxGeocoderTestCase(unittest.TestCase):
def setUp(self):
self.geocoder = MapboxGeocoder(token=VALID_TOKEN, logger=Mock())
self.geocoder = MapboxGeocoder(token=mapbox_api_key(), logger=Mock())
def test_invalid_token(self):
invalid_geocoder = MapboxGeocoder(token=INVALID_TOKEN, logger=Mock())
@@ -35,6 +35,11 @@ class MapboxGeocoderTestCase(unittest.TestCase):
assert place
def test_odd_characters(self):
place = self.geocoder.geocode(searchtext='Barcelona; &quot;Spain&quot;')
assert place
def test_empty_request(self):
place = self.geocoder.geocode(searchtext='', country=None, city=None, state_province=None)

View File

@@ -7,15 +7,15 @@ from cartodb_services.mapbox.routing import MapboxRouting
from cartodb_services.tools import Coordinate
from cartodb_services.tools.coordinates import (validate_coordinates,
marshall_coordinates)
from credentials import mapbox_api_key
VALID_TOKEN = 'pk.eyJ1IjoiYWNhcmxvbiIsImEiOiJjamJuZjQ1Zjc0Ymt4Mnh0YmFrMmhtYnY4In0.gt9cw0VeKc3rM2mV5pcEmg'
VALID_ORIGIN = Coordinate(-73.989, 40.733)
class MapboxIsolinesTestCase(unittest.TestCase):
def setUp(self):
matrix_client = MapboxMatrixClient(token=VALID_TOKEN, logger=Mock())
matrix_client = MapboxMatrixClient(token=mapbox_api_key(), logger=Mock())
self.mapbox_isolines = MapboxIsolines(matrix_client, logger=Mock())
def test_calculate_isochrone(self):

View File

@@ -4,8 +4,8 @@ from cartodb_services.mapbox import MapboxMatrixClient
from cartodb_services.mapbox.matrix_client import DEFAULT_PROFILE
from cartodb_services.tools.exceptions import ServiceException
from cartodb_services.tools import Coordinate
from credentials import mapbox_api_key
VALID_TOKEN = 'pk.eyJ1IjoiYWNhcmxvbiIsImEiOiJjamJuZjQ1Zjc0Ymt4Mnh0YmFrMmhtYnY4In0.gt9cw0VeKc3rM2mV5pcEmg'
INVALID_TOKEN = 'invalid_token'
VALID_ORIGIN = Coordinate(-73.989, 40.733)
VALID_TARGET = Coordinate(-74, 40.733)
@@ -22,7 +22,7 @@ INVALID_PROFILE = 'invalid_profile'
class MapboxMatrixTestCase(unittest.TestCase):
def setUp(self):
self.matrix_client = MapboxMatrixClient(token=VALID_TOKEN,
self.matrix_client = MapboxMatrixClient(token=mapbox_api_key(),
logger=Mock())
def test_invalid_profile(self):

View File

@@ -4,8 +4,8 @@ from cartodb_services.mapbox import MapboxRouting
from cartodb_services.mapbox.routing import DEFAULT_PROFILE
from cartodb_services.tools.exceptions import ServiceException
from cartodb_services.tools import Coordinate
from credentials import mapbox_api_key
VALID_TOKEN = 'pk.eyJ1IjoiYWNhcmxvbiIsImEiOiJjamJuZjQ1Zjc0Ymt4Mnh0YmFrMmhtYnY4In0.gt9cw0VeKc3rM2mV5pcEmg'
INVALID_TOKEN = 'invalid_token'
VALID_WAYPOINTS = [Coordinate(-73.989, 40.733), Coordinate(-74, 40.733)]
NUM_WAYPOINTS_MAX = 25
@@ -31,7 +31,7 @@ WELL_KNOWN_LENGTH = 1317.9
class MapboxRoutingTestCase(unittest.TestCase):
def setUp(self):
self.routing = MapboxRouting(token=VALID_TOKEN, logger=Mock())
self.routing = MapboxRouting(token=mapbox_api_key(), logger=Mock())
def test_invalid_profile(self):
with self.assertRaises(ValueError):