Compare commits

...

106 Commits

Author SHA1 Message Date
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
Mario de Frutos
019df2b4e0 Merge pull request #451 from CartoDB/development
Hotfix circular dependencies exceptions
2018-02-02 12:38:23 +01:00
Mario de Frutos
9e67beab3d Merge pull request #450 from CartoDB/move_exceptions_to_avoid_circular_dependencies
Move exceptions to a proper place
2018-02-02 12:37:14 +01:00
Mario de Frutos
5c7d1e9ac1 Solve some dependencies bugs 2018-02-02 12:29:48 +01:00
Mario de Frutos
6e54507b5a Move exceptions to a proper place 2018-02-02 12:06:43 +01:00
Mario de Frutos
14925ea448 Add missing imports for google gecode street 2018-02-02 11:18:56 +01:00
Mario de Frutos
26e0a5e00f Merge pull request #448 from CartoDB/development
Release server 0.30.2 and python library 0.16.4
2018-02-02 10:37:16 +01:00
Mario de Frutos
a99e0cb513 Updated NEWS.md 2018-02-02 10:32:24 +01:00
Mario de Frutos
1e4fef5868 Merge pull request #447 from CartoDB/dont_send_quota_exceptions_rollbar
Avoid treating quota exception as bug exception
2018-02-02 10:29:04 +01:00
Mario de Frutos
3973483af2 Move limits check inside the try 2018-02-01 19:03:32 +01:00
Mario de Frutos
88edad9414 Release artifacts for version 0.30.2 2018-02-01 18:46:24 +01:00
Mario de Frutos
88ad551e4a Bump python version to 0.16.4 2018-02-01 18:40:21 +01:00
Mario de Frutos
94eaf84f91 Treat quota exception as a failure 2018-02-01 18:27:32 +01:00
Mario de Frutos
0df0b0d49c Avoid treating quota exception as bug exception 2018-02-01 18:21:15 +01:00
Mario de Frutos
b260c29b8c Merge pull request #445 from CartoDB/development
Updated routing documentation
2018-02-01 12:10:20 +01:00
Mario de Frutos
da8aa5a8c0 Merge pull request #444 from CartoDB/fix_routing_documentation
Change routing docs with the Mapbox addition as provider
2018-02-01 12:05:27 +01:00
Mario de Frutos
423534068a Change routing docs with the Mapbox addition as provider 2018-02-01 11:55:22 +01:00
Mario de Frutos
6f508b550d Merge pull request #441 from CartoDB/development
Release python library 0.16.3
2018-01-31 17:06:00 +01:00
Mario de Frutos
1462f87d97 Merge pull request #440 from CartoDB/439_filter_empty_requests_mapbox_geocoder
Improve empty requests treatment in geocoders
2018-01-31 16:31:11 +01:00
Mario de Frutos
6ae8aa45fd CR changes 2018-01-31 16:13:20 +01:00
Mario de Frutos
02cb4862f9 Update NEWS.md 2018-01-31 13:18:09 +01:00
Mario de Frutos
07f00cc0ae Added tests for the client/channel part of google 2018-01-31 13:17:36 +01:00
Mario de Frutos
544e0fa763 Going green with non empty request strings 2018-01-31 12:25:35 +01:00
Mario de Frutos
2bbd6bac91 Going red with non empty request string 2018-01-31 12:25:18 +01:00
Mario de Frutos
2f8dbbb5dc Dont raise exception when empty params passed to HERE geocoder 2018-01-31 12:16:13 +01:00
Mario de Frutos
3484cce88b Update NEWS.md 2018-01-31 12:13:02 +01:00
Mario de Frutos
56c90cc541 Bump version to 0.16.3 2018-01-31 12:12:52 +01:00
Mario de Frutos
3583cc6f47 Going green with empty requests that leads to 404 responses 2018-01-31 12:11:25 +01:00
Mario de Frutos
850dc09a4f Going green with empty responses 2018-01-31 12:11:25 +01:00
Mario de Frutos
876bae6b56 Going red with bad request parameters 2018-01-31 12:11:25 +01:00
Mario de Frutos
8f30359cc7 Added rate limits topic to the README 2018-01-30 15:02:40 +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
51 changed files with 10965 additions and 131 deletions

69
NEWS.md
View File

@@ -1,3 +1,72 @@
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
* Create a QuotaExceededException instead of using a generic one
* Version `0.30.2` of server side
* Return empty value when the quota is exceeded and don't send the exception to external loggers
to avoid noise
January 31th, 2018
==================
* Version `0.16.3` of the python library
* Fix for Mapbox geocoder to handle empty requests and empty responses
* Remove raising an exception when non parameters are passed to the HERE geocoder
* Fix for HERE geocoder with non empty requests
* Added more coverage to the google geocoder credentials parse logic
January 29th, 2018
==================
* Version `0.30.1` of server side

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:
@@ -296,3 +300,6 @@ ALTER ROLE "<USER_ROLE>" SET search_path="$user", public, cartodb, cdb_dataservi
#### Option 2 (from builder)
See [the **Configuring Dataservices** documentation](http://cartodb.readthedocs.io/en/latest/operations/configure_data_services.html)
### Rate limits
See [docs](https://github.com/CartoDB/dataservices-api/blob/master/doc/rate_limits.md)

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

@@ -14,7 +14,7 @@ Name | Type | Description | Accepted values
`destination` | `geometry(Point)` | Destination point, in 4326 projection, which defines the end location. |
`mode` | `text` | Type of transport used to calculate the routes. | `car`, `walk`, `bicycle` or `public_transport`
`options` | `text[]` | (Optional) Multiple options to add more capabilities to the analysis. See [Optional routing parameters](#optional-routing-parameters) for details.
`units` | `text` | Unit used to represent the length of the route. | `kilometers`, `miles`. By default is `kilometers`
`units` | `text` | (Optional) Unit used to represent the length of the route. | `kilometers`, `miles`. By default is `kilometers`. This option is not supported by Mapbox provider
#### Returns
@@ -22,7 +22,7 @@ Name | Type | Description | Accepted values
Name | Type | Description
--- | --- | ---
`duration` | `integer` | Duration in seconds of the calculated route.
`length` | `real` | Length in the defined unit in the `units` field. `kilometers` by default .
`length` | `real` | Length in the defined unit in the `units` field. `meters` by default .
`the_geom` | `geometry(LineString)` | LineString geometry of the calculated route in the 4326 projection.
#### Examples
@@ -49,7 +49,7 @@ Name | Type | Description | Accepted values
`waypoints` | `geometry(Point)[]` | Array of ordered points, in 4326 projection, which defines the origin point, one or more locations for the route path to travel through, and the destination. The first element of the array defines the origin and the last element the destination of the route. |
`mode` | `text` | Type of transport used to calculate the routes. | `car`, `walk`, `bicycle` or `public_transport`
`options` | `text[]` | (Optional) Multiple options to add more capabilities to the analysis. See [Optional routing parameters](#optional-routing-parameters) for details.
`units` | `text` | Unit used to represent the length of the route. | `kilometers`, `miles`. By default is `kilometers`
`units` | `text` | (Optional) Unit used to represent the length of the route. | `kilometers`, `miles`. By default is `kilometers`. This option is not supported by Mapbox provider
#### Returns
@@ -57,7 +57,7 @@ Name | Type | Description | Accepted values
Name | Type | Description
--- | --- | ---
`duration` | `integer` | Duration in seconds of the calculated route.
`length` | `real` | Length in the defined unit in the `units` field. `kilometers` by default .
`length` | `real` | Length in the defined unit in the `units` field. `meters` by default .
`the_geom` | `geometry(LineString)` | LineString geometry of the calculated route in the 4326 projection.
*Note*: A request to the function _cdb\_route\_with\_waypoints(waypoints geometry(Point)[], mode text, [options text[], units text])_ with only two points in the geometry array are automatically defined as origin and destination. It is equivalent to performing the following request with these two locations as parameters: _cdb\_route\_point\_to\_point(origin geometry(Point), destination geometry(Point), mode text, [options text[], units text])_.
@@ -75,10 +75,9 @@ INSERT INTO <TABLE> (duration, length, the_geom) SELECT duration, length, shape
UPDATE <TABLE> SET the_geom = (SELECT shape FROM cdb_route_with_waypoints(Array['POINT(-3.7109 40.4234)'::GEOMETRY, 'POINT(-3.7059 40.4203)'::geometry, 'POINT(-3.7046 40.4180)'::geometry]::geometry[], 'car', ARRAY['mode_type=shortest']::text[]))
```
### Optional routing parameters
The optional value parameters must be passed using the format: `option=value`.
The optional value parameters must be passed using the format: `option=value`. Not all are available for all the routing providers
Name | Type | Description | Accepted values
--- | --- | --- | ---

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;

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

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

View File

@@ -0,0 +1,156 @@
--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_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
from cartodb_services.tools import QuotaExceededException
from cartodb_services.here import HereMapsGeocoder
plpy.execute("SELECT cdb_dataservices_server._get_logger_config()")
service_manager = LegacyServiceManager('geocoder', username, orgname, GD)
try:
service_manager.assert_within_limits()
geocoder = HereMapsGeocoder(service_manager.config.heremaps_app_id, service_manager.config.heremaps_app_code, service_manager.logger, service_manager.config.heremaps_service_params)
coordinates = geocoder.geocode(searchtext=searchtext, city=city, state=state_province, country=country)
if coordinates:
service_manager.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:
service_manager.quota_service.increment_empty_service_use()
return None
except QuotaExceededException as qe:
service_manager.quota_service.increment_failed_service_use()
return None
except BaseException as e:
import sys
service_manager.quota_service.increment_failed_service_use()
service_manager.logger.error('Error trying to geocode street point using here maps', sys.exc_info(), data={"username": username, "orgname": orgname})
raise Exception('Error trying to geocode street point using here maps')
finally:
service_manager.quota_service.increment_total_service_use()
$$ LANGUAGE plpythonu STABLE PARALLEL RESTRICTED;
CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_google_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, QuotaExceededException
from cartodb_services.google import GoogleMapsGeocoder
plpy.execute("SELECT cdb_dataservices_server._get_logger_config()")
service_manager = LegacyServiceManager('geocoder', username, orgname, GD)
try:
service_manager.assert_within_limits(quota=False)
geocoder = GoogleMapsGeocoder(service_manager.config.google_client_id, service_manager.config.google_api_key, service_manager.logger)
coordinates = geocoder.geocode(searchtext=searchtext, city=city, state=state_province, country=country)
if coordinates:
service_manager.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:
service_manager.quota_service.increment_empty_service_use()
return None
except QuotaExceededException as qe:
service_manager.quota_service.increment_failed_service_use()
return None
except BaseException as e:
import sys
service_manager.quota_service.increment_failed_service_use()
service_manager.logger.error('Error trying to geocode street point using google maps', sys.exc_info(), data={"username": username, "orgname": orgname})
raise Exception('Error trying to geocode street point using google maps')
finally:
service_manager.quota_service.increment_total_service_use()
$$ LANGUAGE plpythonu STABLE PARALLEL RESTRICTED;
CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapzen_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 ServiceManager, QuotaExceededException
from cartodb_services.mapzen import MapzenGeocoder
from cartodb_services.tools.country import country_to_iso3
from cartodb_services.refactor.service.mapzen_geocoder_config import MapzenGeocoderConfigBuilder
import cartodb_services
cartodb_services.init(plpy, GD)
service_manager = ServiceManager('geocoder', MapzenGeocoderConfigBuilder, username, orgname)
try:
service_manager.assert_within_limits()
geocoder = MapzenGeocoder(service_manager.config.mapzen_api_key, service_manager.logger, service_manager.config.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, search_type='address')
if coordinates:
service_manager.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:
service_manager.quota_service.increment_empty_service_use()
return None
except QuotaExceededException as qe:
service_manager.quota_service.increment_failed_service_use()
return None
except BaseException as e:
import sys
service_manager.quota_service.increment_failed_service_use()
service_manager.logger.error('Error trying to geocode street point using mapzen', sys.exc_info(), data={"username": username, "orgname": orgname})
raise Exception('Error trying to geocode street point using mapzen')
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 iso3166 import countries
from cartodb_services.tools import ServiceManager, QuotaExceededException
from cartodb_services.mapbox import MapboxGeocoder
from cartodb_services.tools.country import country_to_iso3
from cartodb_services.refactor.service.mapbox_geocoder_config import MapboxGeocoderConfigBuilder
import cartodb_services
cartodb_services.init(plpy, GD)
service_manager = ServiceManager('geocoder', MapboxGeocoderConfigBuilder, username, orgname, GD)
try:
service_manager.assert_within_limits()
geocoder = MapboxGeocoder(service_manager.config.mapbox_api_key, service_manager.logger, service_manager.config.service_params)
country_iso3166 = None
if country:
country_iso3 = country_to_iso3(country)
if country_iso3:
country_iso3166 = countries.get(country_iso3).alpha2.lower()
coordinates = geocoder.geocode(searchtext=searchtext, city=city,
state_province=state_province,
country=country_iso3166)
if coordinates:
service_manager.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:
service_manager.quota_service.increment_empty_service_use()
return None
except QuotaExceededException as qe:
service_manager.quota_service.increment_failed_service_use()
return None
except BaseException as e:
import sys
service_manager.quota_service.increment_failed_service_use()
service_manager.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:
service_manager.quota_service.increment_total_service_use()
$$ LANGUAGE plpythonu STABLE PARALLEL RESTRICTED;

View File

@@ -0,0 +1,114 @@
--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.1'" to load this file. \quit
-- HERE goes your code to upgrade/downgrade
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
from cartodb_services.here import HereMapsGeocoder
plpy.execute("SELECT cdb_dataservices_server._get_logger_config()")
service_manager = LegacyServiceManager('geocoder', username, orgname, GD)
service_manager.assert_within_limits()
try:
geocoder = HereMapsGeocoder(service_manager.config.heremaps_app_id, service_manager.config.heremaps_app_code, service_manager.logger, service_manager.config.heremaps_service_params)
coordinates = geocoder.geocode(searchtext=searchtext, city=city, state=state_province, country=country)
if coordinates:
service_manager.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:
service_manager.quota_service.increment_empty_service_use()
return None
except BaseException as e:
import sys
service_manager.quota_service.increment_failed_service_use()
service_manager.logger.error('Error trying to geocode street point using here maps', sys.exc_info(), data={"username": username, "orgname": orgname})
raise Exception('Error trying to geocode street point using here maps')
finally:
service_manager.quota_service.increment_total_service_use()
$$ LANGUAGE plpythonu STABLE PARALLEL RESTRICTED;
CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapzen_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 ServiceManager
from cartodb_services.mapzen import MapzenGeocoder
from cartodb_services.tools.country import country_to_iso3
from cartodb_services.refactor.service.mapzen_geocoder_config import MapzenGeocoderConfigBuilder
import cartodb_services
cartodb_services.init(plpy, GD)
service_manager = ServiceManager('geocoder', MapzenGeocoderConfigBuilder, username, orgname)
service_manager.assert_within_limits()
try:
geocoder = MapzenGeocoder(service_manager.config.mapzen_api_key, service_manager.logger, service_manager.config.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, search_type='address')
if coordinates:
service_manager.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:
service_manager.quota_service.increment_empty_service_use()
return None
except BaseException as e:
import sys
service_manager.quota_service.increment_failed_service_use()
service_manager.logger.error('Error trying to geocode street point using mapzen', sys.exc_info(), data={"username": username, "orgname": orgname})
raise Exception('Error trying to geocode street point using mapzen')
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 iso3166 import countries
from cartodb_services.tools import ServiceManager
from cartodb_services.mapbox import MapboxGeocoder
from cartodb_services.tools.country import country_to_iso3
from cartodb_services.refactor.service.mapbox_geocoder_config import MapboxGeocoderConfigBuilder
import cartodb_services
cartodb_services.init(plpy, GD)
service_manager = ServiceManager('geocoder', MapboxGeocoderConfigBuilder, username, orgname, GD)
service_manager.assert_within_limits()
try:
geocoder = MapboxGeocoder(service_manager.config.mapbox_api_key, service_manager.logger, service_manager.config.service_params)
country_iso3166 = None
if country:
country_iso3 = country_to_iso3(country)
if country_iso3:
country_iso3166 = countries.get(country_iso3).alpha2.lower()
coordinates = geocoder.geocode(searchtext=searchtext, city=city,
state_province=state_province,
country=country_iso3166)
if coordinates:
service_manager.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:
service_manager.quota_service.increment_empty_service_use()
return None
except BaseException as e:
import sys
service_manager.quota_service.increment_failed_service_use()
service_manager.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:
service_manager.quota_service.increment_total_service_use()
$$ LANGUAGE plpythonu 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.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

@@ -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;

File diff suppressed because it is too large Load Diff

View File

@@ -90,13 +90,14 @@ $$ 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
from cartodb_services.tools import QuotaExceededException
from cartodb_services.here import HereMapsGeocoder
plpy.execute("SELECT cdb_dataservices_server._get_logger_config()")
service_manager = LegacyServiceManager('geocoder', username, orgname, GD)
service_manager.assert_within_limits()
try:
service_manager.assert_within_limits()
geocoder = HereMapsGeocoder(service_manager.config.heremaps_app_id, service_manager.config.heremaps_app_code, service_manager.logger, service_manager.config.heremaps_service_params)
coordinates = geocoder.geocode(searchtext=searchtext, city=city, state=state_province, country=country)
if coordinates:
@@ -107,6 +108,9 @@ RETURNS Geometry AS $$
else:
service_manager.quota_service.increment_empty_service_use()
return None
except QuotaExceededException as qe:
service_manager.quota_service.increment_failed_service_use()
return None
except BaseException as e:
import sys
service_manager.quota_service.increment_failed_service_use()
@@ -118,14 +122,14 @@ $$ LANGUAGE plpythonu STABLE PARALLEL RESTRICTED;
CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_google_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
from cartodb_services.tools import LegacyServiceManager, QuotaExceededException
from cartodb_services.google import GoogleMapsGeocoder
plpy.execute("SELECT cdb_dataservices_server._get_logger_config()")
service_manager = LegacyServiceManager('geocoder', username, orgname, GD)
service_manager.assert_within_limits(quota=False)
try:
service_manager.assert_within_limits(quota=False)
geocoder = GoogleMapsGeocoder(service_manager.config.google_client_id, service_manager.config.google_api_key, service_manager.logger)
coordinates = geocoder.geocode(searchtext=searchtext, city=city, state=state_province, country=country)
if coordinates:
@@ -136,6 +140,9 @@ RETURNS Geometry AS $$
else:
service_manager.quota_service.increment_empty_service_use()
return None
except QuotaExceededException as qe:
service_manager.quota_service.increment_failed_service_use()
return None
except BaseException as e:
import sys
service_manager.quota_service.increment_failed_service_use()
@@ -147,7 +154,7 @@ $$ LANGUAGE plpythonu STABLE PARALLEL RESTRICTED;
CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapzen_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 ServiceManager
from cartodb_services.tools import ServiceManager, QuotaExceededException
from cartodb_services.mapzen import MapzenGeocoder
from cartodb_services.tools.country import country_to_iso3
from cartodb_services.refactor.service.mapzen_geocoder_config import MapzenGeocoderConfigBuilder
@@ -156,9 +163,9 @@ RETURNS Geometry AS $$
cartodb_services.init(plpy, GD)
service_manager = ServiceManager('geocoder', MapzenGeocoderConfigBuilder, username, orgname)
service_manager.assert_within_limits()
try:
service_manager.assert_within_limits()
geocoder = MapzenGeocoder(service_manager.config.mapzen_api_key, service_manager.logger, service_manager.config.service_params)
country_iso3 = None
if country:
@@ -174,6 +181,9 @@ RETURNS Geometry AS $$
else:
service_manager.quota_service.increment_empty_service_use()
return None
except QuotaExceededException as qe:
service_manager.quota_service.increment_failed_service_use()
return None
except BaseException as e:
import sys
service_manager.quota_service.increment_failed_service_use()
@@ -186,7 +196,7 @@ $$ 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 iso3166 import countries
from cartodb_services.tools import ServiceManager
from cartodb_services.tools import ServiceManager, QuotaExceededException
from cartodb_services.mapbox import MapboxGeocoder
from cartodb_services.tools.country import country_to_iso3
from cartodb_services.refactor.service.mapbox_geocoder_config import MapboxGeocoderConfigBuilder
@@ -195,9 +205,9 @@ RETURNS Geometry AS $$
cartodb_services.init(plpy, GD)
service_manager = ServiceManager('geocoder', MapboxGeocoderConfigBuilder, username, orgname, GD)
service_manager.assert_within_limits()
try:
service_manager.assert_within_limits()
geocoder = MapboxGeocoder(service_manager.config.mapbox_api_key, service_manager.logger, service_manager.config.service_params)
country_iso3166 = None
@@ -217,6 +227,9 @@ RETURNS Geometry AS $$
else:
service_manager.quota_service.increment_empty_service_use()
return None
except QuotaExceededException as qe:
service_manager.quota_service.increment_failed_service_use()
return None
except BaseException as e:
import sys
service_manager.quota_service.increment_failed_service_use()

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_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

@@ -67,10 +67,10 @@ class HereMapsGeocoder(Traceable):
def geocode(self, **kwargs):
params = {}
for key, value in kwargs.iteritems():
if value:
if value and value.strip():
params[key] = value
if not params:
raise NoGeocodingParams()
return []
return self._execute_geocode(params)
def _execute_geocode(self, params):

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 Mapbox returns more that one result, take the first one
if json_response:
feature = json_response[ENTRY_FEATURES][0]
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,14 +64,30 @@ 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):
address = [searchtext]
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(city)
address.append(normalize(city))
if state_province:
address.append(state_province)
address.append(normalize(state_province))
country = [country] if country else None
@@ -77,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

@@ -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

@@ -3,5 +3,7 @@ from coordinates import Coordinate
from polyline import PolyLine
from log import Logger, LoggerConfig
from rate_limiter import RateLimiter
from service_manager import ServiceManager, RateLimitExceeded
from service_manager import ServiceManager
from legacy_service_manager import LegacyServiceManager
from exceptions import QuotaExceededException, RateLimitExceeded
from country import country_to_iso3

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:

View File

@@ -1,6 +1,5 @@
import json
class TimeoutException(Exception):
def __str__(self):
return repr('Timeout requesting to server')
@@ -29,3 +28,11 @@ class WrongParams(Exception):
class MalformedResult(Exception):
def __str__(self):
return repr('Result structure is malformed')
class RateLimitExceeded(Exception):
def __str__(self):
return repr('Rate limit exceeded')
class QuotaExceededException(Exception):
def __str__(self):
return repr('You have reached the limit of your quota')

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

@@ -1,16 +1,12 @@
from cartodb_services.metrics import QuotaService
from cartodb_services.tools import Logger
from cartodb_services.tools import RateLimiter
from cartodb_services.tools.log import Logger
from cartodb_services.tools.rate_limiter import RateLimiter
from cartodb_services.tools.exceptions import QuotaExceededException, RateLimitExceeded
from cartodb_services.refactor.tools.logger import LoggerConfigBuilder
from cartodb_services.refactor.backend.redis_metrics_connection import RedisMetricsConnectionFactory
from cartodb_services.config import ServiceConfiguration, RateLimitsConfigBuilder
class RateLimitExceeded(Exception):
def __str__(self):
return repr('Rate limit exceeded')
class ServiceManagerBase:
"""
A Service manager collects the configuration needed to use a service,
@@ -37,7 +33,7 @@ class ServiceManagerBase:
if rate and not self.rate_limiter.check():
raise RateLimitExceeded()
if quota and not self.quota_service.check_user_quota():
raise Exception('You have reached the limit of your quota')
raise QuotaExceededException()
@property
def config(self):

View File

@@ -10,7 +10,7 @@ from setuptools import setup, find_packages
setup(
name='cartodb_services',
version='0.16.2',
version='0.17.5',
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

@@ -18,10 +18,10 @@ class GoogleMapsClientFactoryTestCase(unittest.TestCase):
GoogleMapsClientFactory.clients = {}
def test_consecutive_calls_with_same_params_return_same_client(self):
id = 'any_id'
client_id = 'any_id'
key = base64.b64encode('any_key')
client1 = GoogleMapsClientFactory.get(id, key)
client2 = GoogleMapsClientFactory.get(id, key)
client1 = GoogleMapsClientFactory.get(client_id, key)
client2 = GoogleMapsClientFactory.get(client_id, key)
self.assertEqual(client1, client2)
def test_consecutive_calls_with_different_key_return_different_clients(self):
@@ -29,11 +29,11 @@ class GoogleMapsClientFactoryTestCase(unittest.TestCase):
This requirement is important for security reasons as well as not to
cache a wrong key accidentally.
"""
id = 'any_id'
client_id = 'any_id'
key1 = base64.b64encode('any_key')
key2 = base64.b64encode('another_key')
client1 = GoogleMapsClientFactory.get(id, key1)
client2 = GoogleMapsClientFactory.get(id, key2)
client1 = GoogleMapsClientFactory.get(client_id, key1)
client2 = GoogleMapsClientFactory.get(client_id, key2)
self.assertNotEqual(client1, client2)
def test_consecutive_calls_with_different_ids_return_different_clients(self):
@@ -59,3 +59,11 @@ class GoogleMapsClientFactoryTestCase(unittest.TestCase):
def test_credentials_with_underscores_can_be_valid(self):
client = GoogleMapsClientFactory.get('yet_another_dummy_client_id', 'Ola_k_ase___')
self.assertIsInstance(client, googlemaps.Client)
def test_invalid_credentials(self):
with self.assertRaises(InvalidGoogleCredentials):
GoogleMapsClientFactory.get('dummy_client_id', 'lalala')
def test_credentials_with_channel(self):
client = GoogleMapsClientFactory.get('yet_another_dummy_client_id', 'Ola_k_ase___', 'channel')
self.assertIsInstance(client, googlemaps.Client)

View File

@@ -118,3 +118,18 @@ class GoogleGeocoderTestCase(unittest.TestCase):
searchtext='Calle Eloy Gonzalo 27',
city='Madrid',
country='España')
def test_client_data_extraction(self, req_mock):
client_id, channel = self.geocoder.parse_client_id('dummy_client_id')
self.assertEqual(client_id, 'dummy_client_id')
self.assertEqual(channel, None)
def test_client_data_extraction_with_client_parameter(self, req_mock):
client_id, channel = self.geocoder.parse_client_id('client=gme-test')
self.assertEqual(client_id, 'gme-test')
self.assertEqual(channel, None)
def test_client_data_extraction_with_client_and_channel_parameter(self, req_mock):
client_id, channel = self.geocoder.parse_client_id('client=gme-test&channel=testchannel')
self.assertEqual(client_id, 'gme-test')
self.assertEqual(channel, 'testchannel')

View File

@@ -128,8 +128,14 @@ class HereMapsGeocoderTestCase(unittest.TestCase):
def test_geocode_address_with_no_params(self, req_mock):
req_mock.register_uri('GET', HereMapsGeocoder.PRODUCTION_GEOCODE_JSON_URL,
text=self.GOOD_RESPONSE)
with self.assertRaises(NoGeocodingParams):
self.geocoder.geocode()
result = self.geocoder.geocode()
self.assertEqual(result, [])
def test_geocode_address_with_non_empty_string_params(self, req_mock):
req_mock.register_uri('GET', HereMapsGeocoder.PRODUCTION_GEOCODE_JSON_URL,
text=self.GOOD_RESPONSE)
result = self.geocoder.geocode(searchtext=" ", city=None, state=" ", country=" ")
self.assertEqual(result, [])
def test_geocode_address_empty_response(self, req_mock):
req_mock.register_uri('GET', HereMapsGeocoder.PRODUCTION_GEOCODE_JSON_URL,

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())
@@ -34,3 +34,23 @@ class MapboxGeocoderTestCase(unittest.TestCase):
place = self.geocoder.geocode(searchtext='New York', country='us')
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)
assert place == []
def test_empty_search_text_request(self):
place = self.geocoder.geocode(searchtext=' ', country='us', city=None, state_province="")
assert place == []
def test_unknown_place_request(self):
place = self.geocoder.geocode(searchtext='[unknown]', country='ch', state_province=None, city=None)
assert place == []

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):