27 Commits

Author SHA1 Message Date
Carla
00daf4c7c0 Merge pull request #195 from CartoDB/194-sanitize_country_inputs
Sanitize country inputs
2015-12-01 15:33:37 +01:00
Carla
c3924fb41d Update response-types.sql 2015-12-01 15:19:16 +01:00
Carla Iriberri
db88a54ff0 Solve conflicts 2015-12-01 15:14:11 +01:00
Carla Iriberri
3e8f635404 Encapsulate cleaning function and restructure 2015-12-01 14:11:44 +01:00
Carla
6baa07578b Merge pull request #196 from CartoDB/delete_old_files
Delete old files
2015-12-01 10:04:06 +01:00
Carla Iriberri
4d01d8dd7c Deletes old geocoder functions 2015-11-30 17:42:07 +01:00
Carla Iriberri
184212081c Deletes postal-code extension 2015-11-30 17:40:23 +01:00
Carla Iriberri
49eb8213eb Adds missing normalization in postal codes 2015-11-30 17:03:06 +01:00
Carla Iriberri
ec0d06446d Fixes typo 2015-11-30 16:11:50 +01:00
Carla Iriberri
21a893f6ac Cleans countries in postal codes for extension 2015-11-30 16:06:13 +01:00
Carla Iriberri
65dab2196a Cleans countries in postal codes 2015-11-30 16:04:02 +01:00
Carla Iriberri
b7562ff6f2 Cleans countries 2015-11-30 15:53:17 +01:00
Carla Iriberri
7661b28e52 Cleans countries in extensions 2015-11-30 15:53:06 +01:00
Carla Iriberri
4be28d4b9b Adds country cleaning in extension 2015-11-30 15:41:42 +01:00
Carla Iriberri
352916d247 Cleans country inputs and fixes some trailing spaces 2015-11-30 15:39:25 +01:00
Carla Iriberri
89cc5840b9 Removes test functions 2015-11-30 13:01:37 +01:00
Carla
e4c8d11ef7 Merge pull request #192 from CartoDB/182-removing_tests
Removing namedplace tests and adding granularity on them
2015-11-27 15:58:06 +01:00
Carla Iriberri
2d05db5597 More granularity for tests 2015-11-25 18:54:14 +01:00
Carla Iriberri
d0e0e5e94f First cleaning of functions 2015-11-25 16:58:30 +01:00
Carla Iriberri
3dd0827765 Reviews tests and adds granularity 2015-11-25 12:45:03 +01:00
Carla
cfef57be74 Merge pull request #187 from CartoDB/186-missing_guessing_in_extension
Add guessing function and tests to extension
2015-11-20 17:19:50 +01:00
Carla
182b34244e Delete namedplace_country_guessing.sql 2015-11-20 16:53:14 +01:00
Carla Iriberri
df1d6c0d8d Add guessing function and tests to extension 2015-11-20 15:50:46 +01:00
Carla
905d1c9348 Merge pull request #184 from CartoDB/oriol_adds
Oriol adds
2015-11-19 15:31:04 +01:00
oriolbx
2805a1c05c Update README.md 2015-11-18 12:30:09 +01:00
oriolbx
3e6593d480 Update README.md 2015-11-18 11:59:51 +01:00
oriolbx
913c4ade63 Update README.md 2015-11-18 11:57:47 +01:00
32 changed files with 4830 additions and 3888 deletions

View File

@@ -1247,6 +1247,84 @@ Data library
**Source URL**: http://www.mass.gov/anf/research-and-tech/it-serv-and-support/application-serv/office-of-geographic-information-massgis/datalayers/structures.html
#### Municipalities of Spain
**Description**: Geometries for the municipalities of Spain. Official dataset from Instituto Geográfico Nacional de España.
**Source**: [Instituto Geográfico Nacional](http://www.ign.es/)
**License**: Public data - Attribution required
**Table Name**: ign_spanish_adm3_municipalities
**Sync Table**: `false`
**Source URL**: http://centrodedescargas.cnig.es/CentroDescargas/equipamiento/lineas_limite.zip
#### Municipalities of Spain with displaced Canary Islands
**Description**: Geometries for the municipalities of Spain with the Canary Islands displaced. Official dataset from Instituto Geográfico Nacional de España.
**Source**: [Instituto Geográfico Nacional](http://www.ign.es/)
**License**: Public data - Attribution required
**Table Name**: ign_spanish_adm3_municipalities_displaced_canary
**Sync Table**: `false`
**Source URL**: http://centrodedescargas.cnig.es/CentroDescargas/equipamiento/lineas_limite.zip
#### Provinces of Spain
**Description**: Geometries for the provinces of Spain. Official dataset from Instituto Geográfico Nacional de España.
**Source**: [Instituto Geográfico Nacional](http://www.ign.es/)
**License**: Public data - Attribution required
**Table Name**: ign_spanish_adm2_provinces
**Sync Table**: `false`
**Source URL**: http://centrodedescargas.cnig.es/CentroDescargas/equipamiento/lineas_limite.zip
#### Provinces of Spain with displaced Canary Islands
**Description**: Geometries for the provinces of Spain with the Canary Islands displaced. Official dataset from Instituto Geográfico Nacional de España.
**Source**: [Instituto Geográfico Nacional](http://www.ign.es/)
**License**: Public data - Attribution required
**Table Name**: ign_spanish_adm2_provinces_displaced_canary
**Sync Table**: `false`
**Source URL**: http://centrodedescargas.cnig.es/CentroDescargas/equipamiento/lineas_limite.zip
#### Autonomous Communities of Spain.
**Description**: Geometries for the autonomous communities of Spain. Official dataset from Instituto Geográfico Nacional de España.
**Source**: [Instituto Geográfico Nacional](http://www.ign.es/)
**License**: Public data - Attribution required
**Table Name**: ign_spanish_adm1_ccaa
**Sync Table**: `false`
**Source URL**: http://centrodedescargas.cnig.es/CentroDescargas/equipamiento/lineas_limite.zip
#### Autonomous communities of Spain with displaced Canary Islands
**Description**: Geometries for the autonomous communities of Spain with the Canary Islands displaced. Official dataset from Instituto Geográfico Nacional de España.
**Source**: [Instituto Geográfico Nacional](http://www.ign.es/)
**License**: Public data - Attribution required
**Table Name**: ign_spanish_adm1_ccaa_displaced_canary
**Sync Table**: `false`
**Source URL**: http://centrodedescargas.cnig.es/CentroDescargas/equipamiento/lineas_limite.zip
***
### Historic

View File

@@ -1,218 +0,0 @@
--Text array, country text
--- Usage
--- SELECT (geocode_admin1_polygons(Array['az', 'Texas'], 'Ecuador')).*
--- Function
CREATE OR REPLACE FUNCTION test_geocode_admin1_polygons(name text[], inputcountry text)
RETURNS SETOF geocode_admin_country_v1 AS $$
DECLARE
ret geocode_admin_country_v1%rowtype;
adm0 TEXT;
adm0_check BOOLEAN := TRUE;
BEGIN
IF inputcountry IS NULL THEN
adm0_check = FALSE;
END IF;
IF trim(inputcountry)='' THEN
adm0_check = FALSE;
END IF;
IF adm0_check IS TRUE THEN
SELECT INTO adm0 adm0_a3 FROM admin0_synonyms WHERE name_ = lower(regexp_replace(inputcountry, '[^a-zA-Z\u00C0-\u00ff]+', '', 'g'))::text LIMIT 1;
FOR ret IN
SELECT
q, inputcountry, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, (
SELECT the_geom FROM adm1 WHERE global_id = (
SELECT global_id
FROM admin1_synonyms
WHERE name_ = lower(regexp_replace(d.q, '[^a-zA-Z\u00C0-\u00ff]+', '', 'g'))::text
AND adm0_a3 = adm0
LIMIT 1
)
) geom
FROM (SELECT unnest(name) q) d
) v
LOOP
RETURN NEXT ret;
END LOOP;
--Handle cases where country couldn't be found
ELSE
FOR ret IN
SELECT
q, inputcountry, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, (
SELECT the_geom FROM adm1 WHERE global_id = (
SELECT global_id
FROM admin1_synonyms
WHERE name_ = lower(regexp_replace(d.q, '[^a-zA-Z\u00C0-\u00ff]+', '', 'g'))::text
LIMIT 1
)
) geom
FROM (SELECT unnest(name) q) d
) v
LOOP
RETURN NEXT ret;
END LOOP;
END IF;
RETURN;
END
$$ LANGUAGE 'plpgsql';
--Text array, country array
--- Usage
--- SELECT (geocode_admin1_polygons(Array['az', 'az'], Array['Ecuador', 'USA'])).*
--- Function
CREATE OR REPLACE FUNCTION test_geocode_admin1_polygons(names text[], country text[])
RETURNS SETOF geocode_admin_country_v1 AS $$
DECLARE
ret geocode_admin_country_v1%rowtype;
BEGIN
FOR ret IN SELECT (test_geocode_admin1_polygons(array_agg(n), c)).* FROM (SELECT unnest(names) n, unnest(country) c) a GROUP BY c LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$ LANGUAGE 'plpgsql';
--Text text
--- Function
CREATE OR REPLACE FUNCTION test_geocode_admin1_polygons(name text[])
RETURNS SETOF geocode_admin_country_v1 AS $$
DECLARE
ret geocode_admin_v1%rowtype;
BEGIN RETURN QUERY
SELECT d.q, n.the_geom as geom, CASE WHEN s.adm1_code IS NULL then FALSE ELSE TRUE END AS success FROM (SELECT q, lower(regexp_replace(q, '[^a-zA-Z\u00C0-\u00ff]+', '', 'g'))::text x FROM (SELECT unnest(name) q) g) d LEFT OUTER JOIN admin1_synonyms s ON name_ = d.x LEFT OUTER JOIN ne_admin1_v3 n ON s.adm1_code = n.adm1_code;
END
$$ LANGUAGE 'plpgsql';
--Text array, country array --
--- Function ---
CREATE OR REPLACE FUNCTION geocode_admin_country_v1(names text[], country text[])
RETURNS SETOF geocode_admin_country_v1 AS $$
DECLARE
ret geocode_admin_country_v1%rowtype;
nans TEXT[];
BEGIN
SELECT array_agg(p) INTO nans FROM (SELECT unnest(names) p, unnest(country) c) g WHERE c IS NULL;
IF 0 < array_length(nans, 1) THEN
SELECT array_agg(p), array_agg(c) INTO names, country FROM (SELECT unnest(names) p, unnest(country) c) g WHERE c IS NOT NULL;
FOR ret IN SELECT g.q, NULL as c, g.geom, g.success FROM (SELECT (geocode_admin1_polygons(nans)).*) g LOOP
RETURN NEXT ret;
END LOOP;
END IF;
FOR ret IN WITH
p AS (SELECT r.p, r.q, c, (SELECT iso3 FROM country_decoder WHERE lower(r.c) = ANY (synonyms)) i FROM (SELECT trim(replace(lower(unnest(names)),'.',' ')) p, unnest(names) q, unnest(country) c) r)
SELECT
q, c, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, c, (
SELECT the_geom
FROM global_province_polygons
WHERE p.p = ANY (synonyms)
AND iso3 = p.i
-- To calculate frequency, I simply counted the number of users
-- we had signed up in each country. Countries with more users,
-- we favor higher in the geocoder :)
ORDER BY frequency DESC LIMIT 1
) geom
FROM p) n
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$ LANGUAGE 'plpgsql';
--Text array --
--- Function ---
CREATE OR REPLACE FUNCTION geocode_admin_country_v1(name text[])
RETURNS SETOF geocode_admin_v1 AS $$
DECLARE
ret geocode_admin_v1%rowtype;
BEGIN
FOR ret IN
SELECT
q, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, (
SELECT the_geom
FROM global_province_polygons
WHERE d.c = ANY (synonyms)
ORDER BY frequency DESC LIMIT 1
) geom
FROM (SELECT trim(replace(lower(unnest(name)),'.',' ')) c, unnest(name) q) d
) v
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$ LANGUAGE 'plpgsql';
--Text array, country text--
--- Function ---
CREATE OR REPLACE FUNCTION geocode_admin_country_v1(name text[], inputcountry text)
RETURNS SETOF geocode_admin_v1 AS $$
DECLARE
ret geocode_admin_v1%rowtype;
BEGIN
FOR ret IN WITH
p AS (SELECT r.c, r.q, (SELECT iso3 FROM country_decoder WHERE lower(inputcountry) = ANY (synonyms)) i FROM (SELECT trim(replace(lower(unnest(name)),'.',' ')) c, unnest(name) q) r)
SELECT
q, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, (
SELECT the_geom
FROM global_province_polygons
WHERE p.c = ANY (synonyms)
AND iso3 = p.i
-- To calculate frequency, I simply counted the number of users
-- we had signed up in each country. Countries with more users,
-- we favor higher in the geocoder :)
ORDER BY frequency DESC LIMIT 1
) geom
FROM p) n
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$ LANGUAGE 'plpgsql';

View File

@@ -46,13 +46,25 @@ SELECT (geocode_namedplace(Array['Portland', 'Portland', 'New York City'], Array
Portland | Maine | USA | | f
(3 rows)
SELECT namedplace_guess_country(Array['granada', 'jaen', 'cordoba', 'madrid', 'valladolid']);
namedplace_guess_country
--------------------------
(1 row)
-- Add a named place source
COPY global_cities_alternates_limited (geoname_id, name, the_geom, created_at, updated_at, the_geom_webmercator, preferred, lowername, cartodb_id, admin1_geonameid, iso2, admin1) FROM stdin;
COPY global_cities_points_limited (geoname_id, name, asciiname, altnames, featclass, featcode, iso2, admin1, admin2, population, the_geom, created_at, updated_at, the_geom_webmercator, cartodb_id, lowername) FROM stdin;
-- Check that the geocoding function is callable, should return success = true
SELECT (geocode_namedplace(Array['Barcelona'])).*
SELECT (geocode_namedplace(Array['Barcelona'])).*;
q | geom | success
-----------+----------------------------------------------------+---------
Barcelona | 0101000020E6100000CA15DEE522E653C0A4C2D842902B4540 | t
(1 row)
SELECT namedplace_guess_country(Array['Barcelona']);
namedplace_guess_country
--------------------------
ES
(1 row)

View File

@@ -0,0 +1,8 @@
-- Cleaning function
CREATE OR REPLACE FUNCTION geocode_clean_name(name text) RETURNS text
LANGUAGE plpgsql
AS $$
BEGIN
RETURN regexp_replace(name, '[^a-zA-Z\u00C0-\u00ff]+', '', 'g');
END
$$;

View File

@@ -9,7 +9,7 @@ CREATE OR REPLACE FUNCTION geocode_admin0_polygons(name text[])
-- FOR ret IN
RETURN QUERY
SELECT d.q, n.the_geom as geom, CASE WHEN s.adm0_a3 IS NULL then FALSE ELSE TRUE END AS success
FROM (SELECT q, lower(regexp_replace(q, '[^a-zA-Z\u00C0-\u00ff]+', '', 'g'))::text x
FROM (SELECT q, lower(geocode_clean_name(q))::text x
FROM (SELECT unnest(name) q) g) d
LEFT OUTER JOIN admin0_synonyms s ON name_ = d.x
LEFT OUTER JOIN ne_admin0_v3 n ON s.adm0_a3 = n.adm0_a3 GROUP BY d.q, n.the_geom, s.adm0_a3;
@@ -24,7 +24,7 @@ CREATE OR REPLACE FUNCTION admin0_synonym_lookup(name text[])
ret synonym_lookup_v1%rowtype;
BEGIN RETURN QUERY
SELECT d.q, s.adm0_a3
FROM (SELECT q, lower(regexp_replace(q, '[^a-zA-Z\u00C0-\u00ff]+', '', 'g'))::text x
FROM (SELECT q, lower(geocode_clean_name(q))::text x
FROM (SELECT unnest(name) q) g) d
LEFT OUTER JOIN admin0_synonyms s ON name_ = d.x GROUP BY d.q, s.adm0_a3;
END
@@ -74,7 +74,7 @@ CREATE INDEX idx_admin0_synonyms_rank ON admin0_synonyms USING btree (rank);
-- create trigger function. used in both admin0 and admin1 synonym tables
CREATE OR REPLACE FUNCTION alpha_numeric_identifiers() RETURNS trigger AS $alpha_numeric_identifiers$
BEGIN
NEW.name_ := lower(regexp_replace(NEW.name, '[^a-zA-Z\u00C0-\u00ff]+', '', 'g'));
NEW.name_ := lower(geocode_clean_name(NEW.name));
RETURN NEW;
END;
$alpha_numeric_identifiers$ LANGUAGE plpgsql;

View File

@@ -4,23 +4,23 @@
CREATE OR REPLACE FUNCTION geocode_admin1_polygons(name text[]) RETURNS SETOF geocode_admin_v1
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
DECLARE
ret geocode_admin_v1%rowtype;
BEGIN
FOR ret IN
SELECT
q, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
SELECT
q, (
SELECT the_geom
SELECT the_geom
FROM global_province_polygons
WHERE d.c = ANY (synonyms)
WHERE d.c = ANY (synonyms)
ORDER BY frequency DESC LIMIT 1
) geom
FROM (SELECT trim(replace(lower(unnest(name)),'.',' ')) c, unnest(name) q) d
) v
LOOP
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
@@ -31,23 +31,23 @@ $$;
CREATE OR REPLACE FUNCTION geocode_admin1_polygons(name text[], inputcountry text) RETURNS SETOF geocode_admin_v1
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
DECLARE
ret geocode_admin_v1%rowtype;
BEGIN
FOR ret IN WITH
p AS (SELECT r.c, r.q, (SELECT iso3 FROM country_decoder WHERE lower(inputcountry) = ANY (synonyms)) i FROM (SELECT trim(replace(lower(unnest(name)),'.',' ')) c, unnest(name) q) r)
FOR ret IN WITH
p AS (SELECT r.c, r.q, (SELECT iso3 FROM country_decoder WHERE lower(geocode_clean_name(inputcountry))::text = ANY (synonyms)) i FROM (SELECT trim(replace(lower(unnest(name)),'.',' ')) c, unnest(name) q) r)
SELECT
q, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
SELECT
q, (
SELECT the_geom
SELECT the_geom
FROM global_province_polygons
WHERE p.c = ANY (synonyms)
WHERE p.c = ANY (synonyms)
AND iso3 = p.i
-- To calculate frequency, I simply counted the number of users
-- we had signed up in each country. Countries with more users,
-- we had signed up in each country. Countries with more users,
-- we favor higher in the geocoder :)
ORDER BY frequency DESC LIMIT 1
) geom
@@ -63,7 +63,7 @@ $$;
CREATE OR REPLACE FUNCTION geocode_admin1_polygons(names text[], country text[]) RETURNS SETOF geocode_admin_country_v1
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
DECLARE
ret geocode_admin_country_v1%rowtype;
nans TEXT[];
BEGIN
@@ -79,19 +79,19 @@ CREATE OR REPLACE FUNCTION geocode_admin1_polygons(names text[], country text[])
END IF;
FOR ret IN WITH
p AS (SELECT r.p, r.q, c, (SELECT iso3 FROM country_decoder WHERE lower(r.c) = ANY (synonyms)) i FROM (SELECT trim(replace(lower(unnest(names)),'.',' ')) p, unnest(names) q, unnest(country) c) r)
FOR ret IN WITH
p AS (SELECT r.p, r.q, c, (SELECT iso3 FROM country_decoder WHERE lower(geocode_clean_name(r.c))::text = ANY (synonyms)) i FROM (SELECT trim(replace(lower(unnest(names)),'.',' ')) p, unnest(names) q, unnest(country) c) r)
SELECT
q, c, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
SELECT
q, c, (
SELECT the_geom
SELECT the_geom
FROM global_province_polygons
WHERE p.p = ANY (synonyms)
WHERE p.p = ANY (synonyms)
AND iso3 = p.i
-- To calculate frequency, I simply counted the number of users
-- we had signed up in each country. Countries with more users,
-- we had signed up in each country. Countries with more users,
-- we favor higher in the geocoder :)
ORDER BY frequency DESC LIMIT 1
) geom

View File

@@ -5,7 +5,7 @@
CREATE OR REPLACE FUNCTION geocode_namedplace(places text[], country text[]) RETURNS SETOF geocode_namedplace_country_v1
LANGUAGE plpgsql IMMUTABLE SECURITY DEFINER
AS $$
DECLARE
DECLARE
ret geocode_namedplace_country_v1%rowtype;
nans TEXT[];
BEGIN
@@ -27,13 +27,13 @@ CREATE OR REPLACE FUNCTION geocode_namedplace(places text[], country text[]) RET
END LOOP;
END IF;
FOR ret IN WITH
p AS (SELECT r.s, r.c, (SELECT iso2 FROM country_decoder WHERE lower(r.c) = ANY (synonyms)) i FROM (SELECT unnest(places) AS s, unnest(country)::text AS c) r),
FOR ret IN WITH
p AS (SELECT r.s, r.c, (SELECT iso2 FROM country_decoder WHERE lower(geocode_clean_name(r.c))::text = ANY (synonyms)) i FROM (SELECT unnest(places) AS s, unnest(country)::text AS c) r),
best AS (SELECT p.s AS q, p.c AS c, (SELECT gp.the_geom AS geom FROM global_cities_points_limited gp WHERE gp.lowername = lower(p.s) AND gp.iso2 = p.i ORDER BY population DESC LIMIT 1) AS geom FROM p),
next AS (SELECT p.s AS q, p.c AS c, (SELECT gp.the_geom FROM global_cities_points_limited gp, global_cities_alternates_limited ga WHERE lower(p.s) = ga.lowername AND gp.iso2 = p.i AND ga.geoname_id = gp.geoname_id ORDER BY preferred DESC LIMIT 1) geom FROM p WHERE p.s NOT IN (SELECT q FROM best WHERE c = p.c AND geom IS NOT NULL))
SELECT q, c, geom, TRUE AS success FROM best WHERE geom IS NOT NULL
UNION ALL
SELECT q, c, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success FROM next
SELECT q, c, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success FROM next
LOOP
RETURN NEXT ret;
END LOOP;
@@ -45,7 +45,7 @@ $$;
CREATE OR REPLACE FUNCTION geocode_namedplace(places text[], inputcountry text) RETURNS SETOF geocode_admin_country_v1
LANGUAGE plpgsql IMMUTABLE SECURITY DEFINER
AS $$
DECLARE
DECLARE
ret geocode_admin_country_v1%rowtype;
isoTwo TEXT := NULL;
has_country BOOLEAN;
@@ -59,22 +59,22 @@ CREATE OR REPLACE FUNCTION geocode_namedplace(places text[], inputcountry text)
END IF;
IF has_country THEN
SELECT iso2 INTO isoTwo FROM country_decoder WHERE lower(inputcountry) = ANY (synonyms) LIMIT 1;
FOR ret IN WITH
SELECT iso2 INTO isoTwo FROM country_decoder WHERE lower(geocode_clean_name(inputcountry))::text = ANY (synonyms) LIMIT 1;
FOR ret IN WITH
best AS (SELECT p.s AS q, (SELECT gp.the_geom AS geom FROM global_cities_points_limited gp WHERE gp.lowername = lower(p.s) AND gp.iso2 = isoTwo ORDER BY population DESC LIMIT 1) AS geom FROM (SELECT unnest(places) AS s) p),
next AS (SELECT p.s AS q, (SELECT gp.the_geom FROM global_cities_points_limited gp, global_cities_alternates_limited ga WHERE lower(p.s) = ga.lowername AND gp.iso2 = isoTwo AND ga.geoname_id = gp.geoname_id ORDER BY preferred DESC LIMIT 1) geom FROM (SELECT unnest(places) AS s) p WHERE p.s NOT IN (SELECT q FROM best WHERE geom IS NOT NULL))
SELECT q, inputcountry c, geom, TRUE AS success FROM best WHERE geom IS NOT NULL
UNION ALL
SELECT q, inputcountry c, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success FROM next
SELECT q, inputcountry c, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success FROM next
LOOP
RETURN NEXT ret;
END LOOP;
-- no country included, or iso interpretation found
ELSE
FOR ret IN
SELECT g.q as q, inputcountry as c, g.geom as geom, g.success as success FROM (SELECT (geocode_namedplace(places)).*) g
ELSE
FOR ret IN
SELECT g.q as q, inputcountry as c, g.geom as geom, g.success as success FROM (SELECT (geocode_namedplace(places)).*) g
LOOP
RETURN NEXT ret;
RETURN NEXT ret;
END LOOP;
END IF;
RETURN;
@@ -85,7 +85,7 @@ $$;
CREATE OR REPLACE FUNCTION geocode_namedplace(places text[], admin1s text, inputcountry text) RETURNS SETOF geocode_admin1_country_v1
LANGUAGE plpgsql IMMUTABLE SECURITY DEFINER
AS $$
DECLARE
DECLARE
ret geocode_admin1_country_v1%rowtype;
has_country BOOLEAN;
has_admin1s BOOLEAN;
@@ -115,12 +115,12 @@ CREATE OR REPLACE FUNCTION geocode_namedplace(places text[], admin1s text, input
RETURN NEXT ret;
END LOOP;
-- no country, has admin1 value
ELSE
FOR ret IN
ELSE
FOR ret IN
SELECT g.q, admin1s AS a1, inputcountry as c, g.geom, g.success FROM (
SELECT (
geocode_namedplace(
places,
places,
(SELECT array_agg(a) FROM (SELECT admin1s a FROM GENERATE_SERIES(1, Array_Length(places, 1)) s) r),
NULL
)
@@ -136,12 +136,12 @@ CREATE OR REPLACE FUNCTION geocode_namedplace(places text[], admin1s text, input
RETURN NEXT ret;
END LOOP;
-- has country, has admin1 value
ELSE
FOR ret IN
ELSE
FOR ret IN
SELECT g.q, admin1s AS a1, inputcountry as c, g.geom, g.success FROM (
SELECT (
geocode_namedplace(
places,
places,
(SELECT array_agg(a) FROM (SELECT admin1s a FROM GENERATE_SERIES(1, Array_Length(places, 1)) s) r),
inputcountry
)
@@ -158,7 +158,7 @@ $$;
CREATE OR REPLACE FUNCTION geocode_namedplace(places text[], admin1s text[], inputcountry text) RETURNS SETOF geocode_admin1_country_v1
LANGUAGE plpgsql IMMUTABLE SECURITY DEFINER
AS $$
DECLARE
DECLARE
ret geocode_admin1_country_v1%rowtype;
nans TEXT[];
isoTwo TEXT := NULL;
@@ -172,7 +172,7 @@ CREATE OR REPLACE FUNCTION geocode_namedplace(places text[], admin1s text[], inp
has_country := FALSE;
END IF;
IF has_country THEN
SELECT iso2 INTO isoTwo FROM country_decoder WHERE lower(inputcountry) = ANY (synonyms) LIMIT 1;
SELECT iso2 INTO isoTwo FROM country_decoder WHERE lower(geocode_clean_name(inputcountry))::text = ANY (synonyms) LIMIT 1;
END IF;
-- find all cases where admin1 is NULL
@@ -200,15 +200,15 @@ CREATE OR REPLACE FUNCTION geocode_namedplace(places text[], admin1s text[], inp
SELECT array_agg(p), array_agg(c) INTO places, admin1s FROM (SELECT unnest(places) p, unnest(admin1s) c) g WHERE c!='';
IF has_country THEN
-- geocode our named place without admin1 but with our iso2
FOR ret IN
SELECT g.q, '' AS a1, inputcountry as c, g.geom, g.success FROM (SELECT (geocode_namedplace(nans, inputcountry)).*) g
FOR ret IN
SELECT g.q, '' AS a1, inputcountry as c, g.geom, g.success FROM (SELECT (geocode_namedplace(nans, inputcountry)).*) g
LOOP
RETURN NEXT ret;
END LOOP;
ELSE
-- geocode our named place without admin1 and without iso2
FOR ret IN
SELECT g.q, '' AS a1, inputcountry as c, g.geom, g.success FROM (SELECT (geocode_namedplace(nans)).*) g
FOR ret IN
SELECT g.q, '' AS a1, inputcountry as c, g.geom, g.success FROM (SELECT (geocode_namedplace(nans)).*) g
LOOP
RETURN NEXT ret;
END LOOP;
@@ -217,8 +217,8 @@ CREATE OR REPLACE FUNCTION geocode_namedplace(places text[], admin1s text[], inp
-- geocode all the cases where admin1 is available
IF has_country THEN
FOR ret IN WITH
-- return c=iso2 and search without country
FOR ret IN WITH
-- return c=iso2 and search without country
p AS (
SELECT r.s, r.a1, (SELECT admin1 FROM admin1_decoder WHERE lower(r.a1) = ANY (synonyms) AND admin1_decoder.iso2 = isoTwo LIMIT 1) i FROM (SELECT unnest(places) AS s, unnest(admin1s)::text AS a1) r),
best AS (SELECT p.s AS q, p.a1 as a1, (SELECT gp.the_geom AS geom FROM global_cities_points_limited gp WHERE gp.lowername = lower(p.s) AND gp.admin1 = p.i ORDER BY population DESC LIMIT 1) AS geom FROM p),
@@ -230,15 +230,15 @@ CREATE OR REPLACE FUNCTION geocode_namedplace(places text[], admin1s text[], inp
RETURN NEXT ret;
END LOOP;
ELSE
-- return c=NULL and search without country
FOR ret IN WITH
-- return c=NULL and search without country
FOR ret IN WITH
p AS (
SELECT r.s, r.a1, (SELECT admin1 FROM admin1_decoder WHERE lower(r.a1) = ANY (synonyms) LIMIT 1) i FROM (SELECT unnest(places) AS s, unnest(admin1s)::text AS a1) r WHERE a1 IS NOT NULL and a1 != ''),
best AS (SELECT p.s AS q, p.a1 as a1, (SELECT gp.the_geom AS geom FROM global_cities_points_limited gp WHERE gp.lowername = lower(p.s) AND gp.admin1 = p.i ORDER BY population DESC LIMIT 1) AS geom FROM p),
next AS (SELECT p.s AS q, p.a1 AS a1, (SELECT gp.the_geom FROM global_cities_points_limited gp, global_cities_alternates_limited ga WHERE lower(p.s) = ga.lowername AND ga.admin1 = p.i AND ga.geoname_id = gp.geoname_id ORDER BY preferred DESC LIMIT 1) geom FROM p WHERE p.s NOT IN (SELECT q FROM best WHERE geom IS NOT NULL))
SELECT q, a1, inputcountry as c, geom, TRUE AS success FROM best WHERE geom IS NOT NULL
UNION ALL
SELECT q, a1, inputcountry as c, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success FROM next
SELECT q, a1, inputcountry as c, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success FROM next
LOOP
RETURN NEXT ret;
END LOOP;
@@ -252,14 +252,14 @@ $$;
CREATE OR REPLACE FUNCTION geocode_namedplace(places text[], admin1s text[], inputcountry text[]) RETURNS SETOF geocode_admin1_country_v1
LANGUAGE plpgsql IMMUTABLE SECURITY DEFINER
AS $$
DECLARE
DECLARE
ret geocode_admin1_country_v1%rowtype;
BEGIN
IF admin1s IS NULL THEN
FOR ret IN SELECT g.q as q, NULL as a1, g.c as c, g.geom as geom, g.success as success FROM (SELECT (geocode_namedplace(places, inputcountry)).*) g LOOP
RETURN NEXT ret;
END LOOP;
ELSE
ELSE
FOR ret IN WITH clean AS (SELECT array_agg(p) p, array_agg(a) a, c FROM (SELECT p, a, c FROM (SELECT unnest(places) p, unnest(admin1s) a, unnest(inputcountry) c) z GROUP BY p, a, c) y GROUP BY c)
SELECT (geocode_namedplace(p, a, c)).* FROM clean LOOP
RETURN NEXT ret;
@@ -273,14 +273,14 @@ $$;
CREATE OR REPLACE FUNCTION geocode_namedplace(places text[]) RETURNS SETOF geocode_namedplace_v1
LANGUAGE plpgsql IMMUTABLE SECURITY DEFINER
AS $$
DECLARE
DECLARE
ret geocode_namedplace_v1%rowtype;
BEGIN
FOR ret IN WITH best AS (SELECT s AS q, (SELECT the_geom FROM global_cities_points_limited gp WHERE gp.lowername = lower(p.s) ORDER BY population DESC LIMIT 1) AS geom FROM (SELECT unnest(places) as s) p),
next AS (SELECT p.s AS q, (SELECT gp.the_geom FROM global_cities_points_limited gp, global_cities_alternates_limited ga WHERE lower(p.s) = ga.lowername AND ga.geoname_id = gp.geoname_id ORDER BY preferred DESC LIMIT 1) geom FROM (SELECT unnest(places) as s) p WHERE p.s NOT IN (SELECT q FROM best WHERE geom IS NOT NULL))
SELECT q, geom, TRUE AS success FROM best WHERE geom IS NOT NULL
UNION ALL
SELECT q, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success FROM next
SELECT q, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success FROM next
LOOP
RETURN NEXT ret;
END LOOP;
@@ -288,6 +288,40 @@ CREATE OR REPLACE FUNCTION geocode_namedplace(places text[]) RETURNS SETOF geoco
END
$$;
CREATE OR REPLACE FUNCTION namedplace_guess_country(places text[])
RETURNS text AS $$
DECLARE
country_code text;
threshold CONSTANT float := 0.8;
input_length integer := array_length(places, 1);
BEGIN
BEGIN
WITH hist AS (
SELECT count(DISTINCT(lower(p.s), gp.iso2)) AS c, iso2
FROM global_cities_points_limited gp
inner join (SELECT unnest(places) AS s) p
ON (gp.lowername = lower(s))
GROUP BY iso2
),
best_two AS (
SELECT iso2, c
FROM hist
WHERE c > input_length * threshold
ORDER BY c DESC
LIMIT 2
)
SELECT iso2 INTO STRICT country_code
FROM (SELECT iso2, c, max(c) over() AS maxcount FROM best_two) bt
WHERE bt.c = bt.maxcount;
EXCEPTION
WHEN NO_DATA_FOUND OR too_many_rows THEN
RETURN NULL;
END;
RETURN country_code;
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER IMMUTABLE;
--------------------------------------------------------------------------------
-- Support tables

View File

@@ -5,18 +5,18 @@
CREATE FUNCTION geocode_postalcode_polygons(code text[], inputcountries text[]) RETURNS SETOF geocode_namedplace_country_v1
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
DECLARE
ret geocode_namedplace_country_v1%rowtype;
adm text[];
BEGIN
SELECT INTO adm array_agg((SELECT adm0_a3 FROM admin0_synonyms WHERE name_ = lower(regexp_replace(b.c, '[^a-zA-Z\u00C0-\u00ff]+', '', 'g'))::text LIMIT 1)) FROM (SELECT UNNEST(inputcountries) c) b;
SELECT INTO adm array_agg((SELECT adm0_a3 FROM admin0_synonyms WHERE name_ = lower(geocode_clean_name(b.c))::text LIMIT 1)) FROM (SELECT UNNEST(inputcountries) c) b;
FOR ret IN
SELECT
q, c, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
SELECT
q, c, (
SELECT the_geom
FROM global_postal_code_polygons
@@ -25,7 +25,7 @@ CREATE FUNCTION geocode_postalcode_polygons(code text[], inputcountries text[])
) geom
FROM (SELECT unnest(code) q, unnest(inputcountries) c, unnest(adm) a) d
) v
LOOP
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
@@ -38,26 +38,26 @@ $$;
CREATE FUNCTION geocode_postalcode_polygons(code text[], inputcountry text) RETURNS SETOF geocode_namedplace_v1
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
DECLARE
ret geocode_namedplace_v1%rowtype;
BEGIN
FOR ret IN
SELECT
q, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
SELECT
q, (
SELECT the_geom
SELECT the_geom
FROM global_postal_code_polygons
WHERE postal_code = upper(d.q)
AND iso3 = (
SELECT iso3 FROM country_decoder WHERE
lower(inputcountry) = ANY (synonyms) LIMIT 1
lower(geocode_clean_name(inputcountry))::text = ANY (synonyms) LIMIT 1
)
) geom
FROM (SELECT unnest(code) q) d
) v
LOOP
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
@@ -69,23 +69,23 @@ $$;
CREATE FUNCTION geocode_postalcode_polygons(code text[]) RETURNS SETOF geocode_namedplace_v1
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
DECLARE
ret geocode_namedplace_v1%rowtype;
BEGIN
FOR ret IN
SELECT
q, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
SELECT
q, (
SELECT the_geom
SELECT the_geom
FROM global_postal_code_polygons
WHERE postal_code = upper(d.q)
WHERE postal_code = upper(d.q)
LIMIT 1
) geom
FROM (SELECT unnest(code) q) d
) v
LOOP
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
@@ -98,27 +98,27 @@ $$;
CREATE FUNCTION geocode_postalcode_points(code text[], inputcountry text) RETURNS SETOF geocode_namedplace_v1
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
DECLARE
ret geocode_namedplace_v1%rowtype;
BEGIN
FOR ret IN
SELECT
q, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
SELECT
q, (
SELECT the_geom
SELECT the_geom
FROM global_postal_code_points
WHERE postal_code = upper(d.q)
AND iso3 = (
SELECT iso3 FROM country_decoder WHERE
lower(inputcountry) = ANY (synonyms) LIMIT 1
lower(geocode_clean_name(inputcountry))::text = ANY (synonyms) LIMIT 1
)
LIMIT 1
) geom
FROM (SELECT unnest(code) q) d
) v
LOOP
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
@@ -130,27 +130,27 @@ $$;
CREATE FUNCTION geocode_postalcode_points(code integer[], inputcountries text[]) RETURNS SETOF geocode_postalint_country_v1
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
DECLARE
ret geocode_postalint_country_v1%rowtype;
BEGIN
FOR ret IN
SELECT
q, c, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
SELECT
q, c, (
SELECT the_geom
FROM global_postal_code_points
WHERE postal_code_num = d.q
AND iso3 = (
SELECT iso3 FROM country_decoder WHERE
lower(d.c) = ANY (synonyms) LIMIT 1
lower(geocode_clean_name(d.c))::text = ANY (synonyms) LIMIT 1
)
LIMIT 1
) geom
FROM (SELECT unnest(code) q, unnest(inputcountries) c) d
) v
LOOP
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
@@ -162,23 +162,23 @@ $$;
CREATE FUNCTION geocode_postalcode_points(code text[]) RETURNS SETOF geocode_namedplace_v1
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
DECLARE
ret geocode_namedplace_v1%rowtype;
BEGIN
FOR ret IN
SELECT
q, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
SELECT
q, (
SELECT the_geom
SELECT the_geom
FROM global_postal_code_points
WHERE postal_code = upper(d.q)
WHERE postal_code = upper(d.q)
LIMIT 1
) geom
FROM (SELECT unnest(code) q) d
) v
LOOP
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
@@ -190,7 +190,7 @@ $$;
CREATE FUNCTION geocode_postalcode_points(code text[], inputcountries text[]) RETURNS SETOF geocode_place_country_iso_v1
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
DECLARE
ret geocode_place_country_iso_v1%rowtype;
geo GEOMETRY;
BEGIN
@@ -199,21 +199,21 @@ CREATE FUNCTION geocode_postalcode_points(code text[], inputcountries text[]) RE
SELECT
q, c, iso3, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
SELECT
q, c, (SELECT iso3 FROM country_decoder WHERE
lower(d.c) = ANY (synonyms) LIMIT 1) iso3, (
lower(geocode_clean_name(d.c))::text = ANY (synonyms) LIMIT 1) iso3, (
SELECT the_geom
FROM global_postal_code_points
WHERE postal_code = upper(d.q)
AND iso3 = (
SELECT iso3 FROM country_decoder WHERE
lower(d.c) = ANY (synonyms) LIMIT 1
lower(geocode_clean_name(d.c))::text = ANY (synonyms) LIMIT 1
)
LIMIT 1
) geom
FROM (SELECT unnest(code) q, unnest(inputcountries) c) d
) v
LOOP
LOOP
IF ret.geom IS NULL AND ret.iso3 = 'GBR' THEN
geo := geocode_greatbritain_outward(ret.q);
IF geo IS NOT NULL THEN
@@ -239,7 +239,7 @@ CREATE FUNCTION geocode_greatbritain_outward(code text) RETURNS geometry
geom := NULL;
IF array_length(string_to_array(code,' '),1) = 2 THEN
code := split_part(code, ' ', 1) || ' ' || rpad(substring(split_part(code, ' ', 2), 1, 1), 3, '#');
SELECT the_geom INTO geom FROM global_postal_code_points WHERE
SELECT the_geom INTO geom FROM global_postal_code_points WHERE
postal_code = code
AND iso3 = 'GBR'
LIMIT 1;
@@ -252,15 +252,15 @@ $$;
CREATE FUNCTION admin0_available_services(name text[]) RETURNS SETOF available_services_v1
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
DECLARE
ret available_services_v1%rowtype;
BEGIN RETURN QUERY
SELECT d.q, n.adm0_a3, n.postal_code_points, n.postal_code_polygons FROM
SELECT d.q, n.adm0_a3, n.postal_code_points, n.postal_code_polygons FROM
(
SELECT q, lower(regexp_replace(q, '[^a-zA-Z\u00C0-\u00ff]+', '', 'g'))::text x FROM
SELECT q, lower(geocode_clean_name(q))::text x FROM
(
SELECT unnest(name) q
)
)
g) d LEFT OUTER JOIN admin0_synonyms s ON name_ = d.x LEFT OUTER JOIN available_services n ON s.adm0_a3 = n.adm0_a3 GROUP BY d.q, n.adm0_a3, n.postal_code_points, n.postal_code_polygons;
END
$$;

View File

@@ -5,6 +5,7 @@ SELECT (geocode_namedplace(Array['sunapee', 'sunapeeee', 'New York City', 'Madri
SELECT (geocode_namedplace(Array['Portland', 'Portland', 'New York City'], Array['Maine', 'Oregon', NULL], 'USA')).*;
SELECT (geocode_namedplace(Array['Portland'], 'Oregon', 'USA')).*;
SELECT (geocode_namedplace(Array['Portland', 'Portland', 'New York City'], Array['Maine', 'Oregon', NULL], Array['USA'])).*;
SELECT namedplace_guess_country(Array['granada', 'jaen', 'cordoba', 'madrid', 'valladolid']);
-- Add a named place source
COPY global_cities_alternates_limited (geoname_id, name, the_geom, created_at, updated_at, the_geom_webmercator, preferred, lowername, cartodb_id, admin1_geonameid, iso2, admin1) FROM stdin;
@@ -16,5 +17,6 @@ COPY global_cities_points_limited (geoname_id, name, asciiname, altnames, featcl
\.
-- Check that the geocoding function is callable, should return success = true
SELECT (geocode_namedplace(Array['Barcelona'])).*
SELECT (geocode_namedplace(Array['Barcelona'])).*;
SELECT namedplace_guess_country(Array['Barcelona']);

View File

@@ -1,31 +0,0 @@
CREATE OR REPLACE FUNCTION geocode_ip(ip text[])
RETURNS SETOF geocode_ip_v1 AS $$
DECLARE
ret geocode_ip_v1%rowtype;
n TEXT;
new_ips INET[];
old_ips TEXT[];
BEGIN
FOR n IN SELECT unnest(ip) LOOP
BEGIN
IF family(n::inet)=6 THEN
new_ips := array_append(new_ips, n::inet);
old_ips := array_append(old_ips, n);
ELSE
new_ips := array_append(new_ips, ('::ffff:'||n)::inet);
old_ips := array_append(old_ips, n);
END IF;
EXCEPTION WHEN OTHERS THEN
SELECT n AS q, NULL as geom, FALSE as success INTO ret;
RETURN NEXT ret;
END;
END LOOP;
FOR ret IN WITH ips AS (SELECT unnest(old_ips) s, unnest(new_ips) net),
matches AS (SELECT s, (SELECT the_geom FROM ip_address_locations WHERE network_start_ip <= ips.net ORDER BY network_start_ip DESC LIMIT 1) geom FROM ips)
SELECT s, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success FROM matches
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;

View File

@@ -1,324 +0,0 @@
CREATE OR REPLACE FUNCTION public.geocode_namedplace(places text[])
RETURNS SETOF geocode_namedplace_v1
LANGUAGE plpgsql
IMMUTABLE SECURITY DEFINER
AS $function$
DECLARE
ret geocode_namedplace_v1%rowtype;
BEGIN
FOR ret IN WITH best AS (SELECT s AS q, (SELECT the_geom FROM global_cities_points_limited gp WHERE gp.lowername = lower(p.s) ORDER BY population DESC LIMIT 1) AS geom FROM (SELECT unnest(places) as s) p),
next AS (SELECT p.s AS q, (SELECT gp.the_geom FROM global_cities_points_limited gp, global_cities_alternates_limited ga WHERE lower(p.s) = ga.lowername AND ga.geoname_id = gp.geoname_id ORDER BY preferred DESC LIMIT 1) geom FROM (SELECT unnest(places) as s) p WHERE p.s NOT IN (SELECT q FROM best WHERE geom IS NOT NULL))
SELECT q, geom, TRUE AS success FROM best WHERE geom IS NOT NULL
UNION ALL
SELECT q, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success FROM next
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$function$
CREATE OR REPLACE FUNCTION public.geocode_namedplace(places text[], admin1s text, inputcountry text)
RETURNS SETOF geocode_admin1_country_v1
LANGUAGE plpgsql
IMMUTABLE SECURITY DEFINER
AS $function$
DECLARE
ret geocode_admin1_country_v1%rowtype;
has_country BOOLEAN;
has_admin1s BOOLEAN;
admin1s_a TEXT[];
BEGIN
has_country := TRUE;
has_admin1s := TRUE;
IF inputcountry IS NULL THEN
has_country := FALSE;
ELSIF inputcountry = '' THEN
has_country := FALSE;
END IF;
IF admin1s IS NULL THEN
has_admin1s := FALSE;
ELSIF admin1s = '' THEN
has_admin1s := FALSE;
END IF;
-- no country value
IF has_country IS FALSE THEN
-- no country no admin1 value
IF has_admin1s IS FALSE THEN
FOR ret IN SELECT g.q, admin1s AS a1, inputcountry as c, g.geom, g.success FROM (SELECT (geocode_namedplace(places)).*) g LOOP
RETURN NEXT ret;
END LOOP;
-- no country, has admin1 value
ELSE
FOR ret IN
SELECT g.q, admin1s AS a1, inputcountry as c, g.geom, g.success FROM (
SELECT (
geocode_namedplace(
places,
(SELECT array_agg(a) FROM (SELECT admin1s a FROM GENERATE_SERIES(1, Array_Length(places, 1)) s) r),
NULL
)
).*) g LOOP
RETURN NEXT ret;
END LOOP;
END IF;
-- has country value
ELSE
-- has country, no admin1 value
IF has_admin1s IS FALSE THEN
FOR ret IN SELECT g.q, admin1s AS a1, inputcountry as c, g.geom, g.success FROM (SELECT (geocode_namedplace(places, inputcountry)).*) g LOOP
RETURN NEXT ret;
END LOOP;
-- has country, has admin1 value
ELSE
FOR ret IN
SELECT g.q, admin1s AS a1, inputcountry as c, g.geom, g.success FROM (
SELECT (
geocode_namedplace(
places,
(SELECT array_agg(a) FROM (SELECT admin1s a FROM GENERATE_SERIES(1, Array_Length(places, 1)) s) r),
inputcountry
)
).*) g LOOP
RETURN NEXT ret;
END LOOP;
END IF;
END IF;
RETURN;
END
$function$
CREATE OR REPLACE FUNCTION public.geocode_namedplace(places text[], admin1s text[], inputcountry text)
RETURNS SETOF geocode_admin1_country_v1
LANGUAGE plpgsql
IMMUTABLE SECURITY DEFINER
AS $function$
DECLARE
ret geocode_admin1_country_v1%rowtype;
nans TEXT[];
isoTwo TEXT := NULL;
has_country BOOLEAN;
BEGIN
has_country := TRUE;
-- find the iso2 code for the input country string, else NULL
IF inputcountry IS NULL THEN
has_country := FALSE;
ELSIF inputcountry = '' THEN
has_country := FALSE;
END IF;
IF has_country THEN
SELECT iso2 INTO isoTwo FROM country_decoder WHERE lower(inputcountry) = ANY (synonyms) LIMIT 1;
END IF;
-- find all cases where admin1 is NULL
SELECT array_agg(p) INTO nans FROM (SELECT unnest(places) p, unnest(admin1s) c) g WHERE c IS NULL;
IF 0 < array_length(nans, 1) THEN
SELECT array_agg(p), array_agg(c) INTO places, admin1s FROM (SELECT unnest(places) p, unnest(admin1s) c) g WHERE c IS NOT NULL;
IF has_country THEN
-- geocode our named place without admin1 but with our iso2
FOR ret IN SELECT g.q, null AS a1, inputcountry as c, g.geom, g.success FROM (SELECT (geocode_namedplace(nans, inputcountry)).*) g LOOP
RETURN NEXT ret;
END LOOP;
ELSE
-- geocode our named place without admin1 and without iso2
FOR ret IN SELECT g.q, NULL as a1, inputcountry as c, g.geom, g.success FROM (SELECT (geocode_namedplace(nans)).*) g LOOP
RETURN NEXT ret;
END LOOP;
END IF;
END IF;
-- find all cases where admin1 is and empty string
SELECT array_agg(p) INTO nans FROM (SELECT unnest(places) p, unnest(admin1s) c) g WHERE c='';
IF 0 < array_length(nans, 1) THEN
SELECT array_agg(p), array_agg(c) INTO places, admin1s FROM (SELECT unnest(places) p, unnest(admin1s) c) g WHERE c!='';
IF has_country THEN
-- geocode our named place without admin1 but with our iso2
FOR ret IN
SELECT g.q, '' AS a1, inputcountry as c, g.geom, g.success FROM (SELECT (geocode_namedplace(nans, inputcountry)).*) g
LOOP
RETURN NEXT ret;
END LOOP;
ELSE
-- geocode our named place without admin1 and without iso2
FOR ret IN
SELECT g.q, '' AS a1, inputcountry as c, g.geom, g.success FROM (SELECT (geocode_namedplace(nans)).*) g
LOOP
RETURN NEXT ret;
END LOOP;
END IF;
END IF;
-- geocode all the cases where admin1 is available
IF has_country THEN
FOR ret IN WITH
-- return c=iso2 and search without country
p AS (
SELECT r.s, r.a1, (SELECT admin1 FROM admin1_decoder WHERE lower(r.a1) = ANY (synonyms) AND admin1_decoder.iso2 = isoTwo LIMIT 1) i FROM (SELECT unnest(places) AS s, unnest(admin1s)::text AS a1) r),
best AS (SELECT p.s AS q, p.a1 as a1, (SELECT gp.the_geom AS geom FROM global_cities_points_limited gp WHERE gp.lowername = lower(p.s) AND gp.admin1 = p.i ORDER BY population DESC LIMIT 1) AS geom FROM p),
next AS (SELECT p.s AS q, p.a1 AS a1, (SELECT gp.the_geom FROM global_cities_points_limited gp, global_cities_alternates_limited ga WHERE lower(p.s) = ga.lowername AND ga.admin1 = p.i AND ga.geoname_id = gp.geoname_id ORDER BY preferred DESC LIMIT 1) geom FROM p WHERE p.s NOT IN (SELECT q FROM best WHERE geom IS NOT NULL))
SELECT q, a1, inputcountry as c, geom, TRUE AS success FROM best WHERE geom IS NOT NULL
UNION ALL
SELECT q, a1, inputcountry as c, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success FROM next
LOOP
RETURN NEXT ret;
END LOOP;
ELSE
-- return c=NULL and search without country
FOR ret IN WITH
p AS (
SELECT r.s, r.a1, (SELECT admin1 FROM admin1_decoder WHERE lower(r.a1) = ANY (synonyms) LIMIT 1) i FROM (SELECT unnest(places) AS s, unnest(admin1s)::text AS a1) r WHERE a1 IS NOT NULL and a1 != ''),
best AS (SELECT p.s AS q, p.a1 as a1, (SELECT gp.the_geom AS geom FROM global_cities_points_limited gp WHERE gp.lowername = lower(p.s) AND gp.admin1 = p.i ORDER BY population DESC LIMIT 1) AS geom FROM p),
next AS (SELECT p.s AS q, p.a1 AS a1, (SELECT gp.the_geom FROM global_cities_points_limited gp, global_cities_alternates_limited ga WHERE lower(p.s) = ga.lowername AND ga.admin1 = p.i AND ga.geoname_id = gp.geoname_id ORDER BY preferred DESC LIMIT 1) geom FROM p WHERE p.s NOT IN (SELECT q FROM best WHERE geom IS NOT NULL))
SELECT q, a1, inputcountry as c, geom, TRUE AS success FROM best WHERE geom IS NOT NULL
UNION ALL
SELECT q, a1, inputcountry as c, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success FROM next
LOOP
RETURN NEXT ret;
END LOOP;
END IF;
RETURN;
END
$function$
CREATE OR REPLACE FUNCTION public.geocode_namedplace(places text[], admin1s text[], inputcountry text[])
RETURNS SETOF geocode_admin1_country_v1
LANGUAGE plpgsql
IMMUTABLE SECURITY DEFINER
AS $function$
DECLARE
ret geocode_admin1_country_v1%rowtype;
BEGIN
IF admin1s IS NULL THEN
FOR ret IN SELECT g.q as q, NULL as a1, g.c as c, g.geom as geom, g.success as success FROM (SELECT (geocode_namedplace(places, inputcountry)).*) g LOOP
RETURN NEXT ret;
END LOOP;
ELSE
FOR ret IN WITH clean AS (SELECT array_agg(p) p, array_agg(a) a, c FROM (SELECT p, a, c FROM (SELECT unnest(places) p, unnest(admin1s) a, unnest(inputcountry) c) z GROUP BY p, a, c) y GROUP BY c)
SELECT (geocode_namedplace(p, a, c)).* FROM clean LOOP
RETURN NEXT ret;
END LOOP;
END IF;
RETURN;
END
$function$
CREATE OR REPLACE FUNCTION public.geocode_namedplace(places text[], country text[])
RETURNS SETOF geocode_namedplace_country_v1
LANGUAGE plpgsql
IMMUTABLE SECURITY DEFINER
AS $function$
DECLARE
ret geocode_namedplace_country_v1%rowtype;
nans TEXT[];
BEGIN
SELECT array_agg(p) INTO nans FROM (SELECT unnest(places) p, unnest(country) c) g WHERE c IS NULL;
IF 0 < array_length(nans, 1) THEN
SELECT array_agg(p), array_agg(c) INTO places, country FROM (SELECT unnest(places) p, unnest(country) c) g WHERE c IS NOT NULL;
FOR ret IN SELECT g.q, NULL as c, g.geom, g.success FROM (SELECT (geocode_namedplace(nans)).*) g LOOP
RETURN NEXT ret;
END LOOP;
END IF;
SELECT array_agg(p) INTO nans FROM (SELECT unnest(places) p, unnest(country) c) g WHERE c='';
IF 0 < array_length(nans, 1) THEN
SELECT array_agg(p), array_agg(c) INTO places, country FROM (SELECT unnest(places) p, unnest(country) c) g WHERE c!='';
FOR ret IN SELECT g.q, '' as c, g.geom, g.success FROM (SELECT (geocode_namedplace(nans)).*) g LOOP
RETURN NEXT ret;
END LOOP;
END IF;
FOR ret IN WITH
p AS (SELECT r.s, r.c, (SELECT iso2 FROM country_decoder WHERE lower(r.c) = ANY (synonyms)) i FROM (SELECT unnest(places) AS s, unnest(country)::text AS c) r),
best AS (SELECT p.s AS q, p.c AS c, (SELECT gp.the_geom AS geom FROM global_cities_points_limited gp WHERE gp.lowername = lower(p.s) AND gp.iso2 = p.i ORDER BY population DESC LIMIT 1) AS geom FROM p),
next AS (SELECT p.s AS q, p.c AS c, (SELECT gp.the_geom FROM global_cities_points_limited gp, global_cities_alternates_limited ga WHERE lower(p.s) = ga.lowername AND gp.iso2 = p.i AND ga.geoname_id = gp.geoname_id ORDER BY preferred DESC LIMIT 1) geom FROM p WHERE p.s NOT IN (SELECT q FROM best WHERE c = p.c AND geom IS NOT NULL))
SELECT q, c, geom, TRUE AS success FROM best WHERE geom IS NOT NULL
UNION ALL
SELECT q, c, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success FROM next
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$function$
CREATE OR REPLACE FUNCTION public.geocode_namedplace(places text[], inputcountry text)
RETURNS SETOF geocode_admin_country_v1
LANGUAGE plpgsql
IMMUTABLE SECURITY DEFINER
AS $function$
DECLARE
ret geocode_admin_country_v1%rowtype;
isoTwo TEXT := NULL;
has_country BOOLEAN;
BEGIN
has_country := TRUE;
-- find the iso2 code for the input country string, else NULL
IF inputcountry IS NULL THEN
has_country := FALSE;
ELSIF inputcountry = '' THEN
has_country := FALSE;
END IF;
IF has_country THEN
SELECT iso2 INTO isoTwo FROM country_decoder WHERE lower(inputcountry) = ANY (synonyms) LIMIT 1;
FOR ret IN WITH
best AS (SELECT p.s AS q, (SELECT gp.the_geom AS geom FROM global_cities_points_limited gp WHERE gp.lowername = lower(p.s) AND gp.iso2 = isoTwo ORDER BY population DESC LIMIT 1) AS geom FROM (SELECT unnest(places) AS s) p),
next AS (SELECT p.s AS q, (SELECT gp.the_geom FROM global_cities_points_limited gp, global_cities_alternates_limited ga WHERE lower(p.s) = ga.lowername AND gp.iso2 = isoTwo AND ga.geoname_id = gp.geoname_id ORDER BY preferred DESC LIMIT 1) geom FROM (SELECT unnest(places) AS s) p WHERE p.s NOT IN (SELECT q FROM best WHERE geom IS NOT NULL))
SELECT q, inputcountry c, geom, TRUE AS success FROM best WHERE geom IS NOT NULL
UNION ALL
SELECT q, inputcountry c, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success FROM next
LOOP
RETURN NEXT ret;
END LOOP;
-- no country included, or iso interpretation found
ELSE
FOR ret IN
SELECT g.q as q, inputcountry as c, g.geom as geom, g.success as success FROM (SELECT (geocode_namedplace(places)).*) g
LOOP
RETURN NEXT ret;
END LOOP;
END IF;
RETURN;
END
$function$
-- geocode_namedplace_country --
CREATE OR REPLACE FUNCTION public.geocode_namedplace_country(places text[], country text[])
RETURNS SETOF geocode_namedplace_country_v1
LANGUAGE plpgsql
IMMUTABLE SECURITY DEFINER
AS $function$
DECLARE
ret geocode_namedplace_country_v1%rowtype;
iso TEXT[];
i INT;
fails INT[];
BEGIN
SELECT array_agg((SELECT iso2 FROM country_decoder WHERE lower(r.c) = ANY (synonyms))) i FROM (SELECT unnest(country)::text AS c) r INTO iso;
FOR i IN 1 .. array_upper(places, 1) LOOP
SELECT q, c, (SELECT gp.the_geom AS geom FROM global_cities_points gp WHERE gp.lowername = lower(x.q) AND gp.iso2 = x.i ORDER BY population DESC LIMIT 1), TRUE as success FROM (SELECT unnest(places) q, unnest(iso) i, unnest(country) c) x INTO ret;
IF ret.geom IS NOT NULL THEN
RETURN NEXT ret;
ELSE
fails := array_append(fails, i);
END IF;
END LOOP;
RETURN;
END
$function$

View File

@@ -1,35 +0,0 @@
-- Return a guess about the country where the places are located, if possible.
-- E.g: SELECT namedplace_guess_country(Array['granada', 'jaen', 'cordoba', 'madrid', 'valladolid']); => NULL
-- E.g: SELECT namedplace_guess_country(Array['granada', 'jaén', 'córdoba', 'madrid', 'valladolid', 'peligros'])); => 'ES'
CREATE OR REPLACE FUNCTION namedplace_guess_country(places text[])
RETURNS text AS $$
DECLARE
country_code text;
threshold CONSTANT float := 0.8;
input_length integer := array_length(places, 1);
BEGIN
BEGIN
WITH hist AS (
SELECT count(DISTINCT(lower(p.s), gp.iso2)) AS c, iso2
FROM global_cities_points_limited gp
inner join (SELECT unnest(places) AS s) p
ON (gp.lowername = lower(s))
GROUP BY iso2
),
best_two AS (
SELECT iso2, c
FROM hist
WHERE c > input_length * threshold
ORDER BY c DESC
LIMIT 2
)
SELECT iso2 INTO STRICT country_code
FROM (SELECT iso2, c, max(c) over() AS maxcount FROM best_two) bt
WHERE bt.c = bt.maxcount;
EXCEPTION
WHEN NO_DATA_FOUND OR too_many_rows THEN
RETURN NULL;
END;
RETURN country_code;
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER IMMUTABLE;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +0,0 @@
results/
regression.diffs
regression.out

View File

@@ -1,8 +0,0 @@
EXTENSION = cdb_geocoder_postalcode
DATA = cdb_geocoder_postalcode--0.0.1.sql
REGRESS = cdb_geocoder_postalcode_test
# postgres build stuff
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)

View File

@@ -1,36 +0,0 @@
# CartoDB postal code geocoder extension
Postgres extension for the CartoDB postal code geocoder. It is meant to contain the functions and related objects needed to geocode by postal codes. It is not meant to contain the actual data used to geocode them.
## Dependencies
This extension is thought to be used on top of CartoDB platform. Therefore a cartodb user is required to install the extension onto it.
The following is a non-comprehensive list of dependencies:
- Postgres 9.3+
- Postgis extension
- Schema triggers extension
- CartoDB extension
## Installation into the db cluster
This requires root privileges
```
sudo make all install
```
## Execute tests
```
PGUSER=postgres make installcheck
```
## Install onto a user's database
```
psql -U development_cartodb_user_fe3b850a-01c0-48f9-8a26-a82f09e9b53f cartodb_dev_user_fe3b850a-01c0-48f9-8a26-a82f09e9b53f_db
```
and then:
```sql
CREATE EXTENSION cdb_geocoder_postalcode;
```
The extension creation in the user's db does not require special privileges. It can be even created from the sql api.

View File

@@ -1,523 +0,0 @@
-- Complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION cdb_geocoder_postalcode" to load this file. \quit
-- Response types for admin0 geocoder
CREATE TYPE geocode_namedplace_v1 AS (q TEXT, geom GEOMETRY, success BOOLEAN);
CREATE TYPE geocode_postalint_country_v1 AS (q TEXT, c TEXT, geom GEOMETRY, success BOOLEAN);
CREATE TYPE geocode_namedplace_country_v1 AS (q TEXT, c TEXT, geom GEOMETRY, success BOOLEAN);
CREATE TYPE available_services_v1 AS (q text, adm0_a3 text, postal_code_points boolean, postal_code_polygons boolean);
CREATE TYPE geocode_place_country_iso_v1 AS (q text, c text, iso3 text, geom geometry, success boolean);
-- Public API functions --
--- Geocoding function ---
-- TODO: deal with permissions
CREATE FUNCTION geocode_postalcode_polygons(code text[], inputcountries text[]) RETURNS SETOF geocode_namedplace_country_v1
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
ret geocode_namedplace_country_v1%rowtype;
adm text[];
BEGIN
SELECT INTO adm array_agg((SELECT adm0_a3 FROM admin0_synonyms WHERE name_ = lower(regexp_replace(b.c, '[^a-zA-Z\u00C0-\u00ff]+', '', 'g'))::text LIMIT 1)) FROM (SELECT UNNEST(inputcountries) c) b;
FOR ret IN
SELECT
q, c, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, c, (
SELECT the_geom
FROM global_postal_code_polygons
WHERE postal_code = CASE WHEN a = 'CAN' THEN substring(upper(d.q) from 1 for 3) ELSE upper(d.q) END
AND iso3 = a
) geom
FROM (SELECT unnest(code) q, unnest(inputcountries) c, unnest(adm) a) d
) v
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$;
-- TODO: The next function works with an incorrect table
CREATE FUNCTION geocode_postalcode_polygons(code text[], inputcountry text) RETURNS SETOF geocode_namedplace_v1
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
ret geocode_namedplace_v1%rowtype;
BEGIN
FOR ret IN
SELECT
q, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, (
SELECT the_geom
FROM global_postal_code_polygons
WHERE postal_code = upper(d.q)
AND iso3 = (
SELECT iso3 FROM country_decoder WHERE
lower(inputcountry) = ANY (synonyms) LIMIT 1
)
) geom
FROM (SELECT unnest(code) q) d
) v
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$;
-- TODO: The next function works with an incorrect table
CREATE FUNCTION geocode_postalcode_polygons(code text[]) RETURNS SETOF geocode_namedplace_v1
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
ret geocode_namedplace_v1%rowtype;
BEGIN
FOR ret IN
SELECT
q, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, (
SELECT the_geom
FROM global_postal_code_polygons
WHERE postal_code = upper(d.q)
LIMIT 1
) geom
FROM (SELECT unnest(code) q) d
) v
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$;
------ POINTS ------
CREATE FUNCTION geocode_postalcode_points(code text[], inputcountry text) RETURNS SETOF geocode_namedplace_v1
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
ret geocode_namedplace_v1%rowtype;
BEGIN
FOR ret IN
SELECT
q, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, (
SELECT the_geom
FROM global_postal_code_points
WHERE postal_code = upper(d.q)
AND iso3 = (
SELECT iso3 FROM country_decoder WHERE
lower(inputcountry) = ANY (synonyms) LIMIT 1
)
LIMIT 1
) geom
FROM (SELECT unnest(code) q) d
) v
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$;
CREATE FUNCTION geocode_postalcode_points(code integer[], inputcountries text[]) RETURNS SETOF geocode_postalint_country_v1
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
ret geocode_postalint_country_v1%rowtype;
BEGIN
FOR ret IN
SELECT
q, c, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, c, (
SELECT the_geom
FROM global_postal_code_points
WHERE postal_code_num = d.q
AND iso3 = (
SELECT iso3 FROM country_decoder WHERE
lower(d.c) = ANY (synonyms) LIMIT 1
)
LIMIT 1
) geom
FROM (SELECT unnest(code) q, unnest(inputcountries) c) d
) v
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$;
CREATE FUNCTION geocode_postalcode_points(code text[]) RETURNS SETOF geocode_namedplace_v1
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
ret geocode_namedplace_v1%rowtype;
BEGIN
FOR ret IN
SELECT
q, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, (
SELECT the_geom
FROM global_postal_code_points
WHERE postal_code = upper(d.q)
LIMIT 1
) geom
FROM (SELECT unnest(code) q) d
) v
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$;
CREATE FUNCTION geocode_postalcode_points(code text[], inputcountries text[]) RETURNS SETOF geocode_place_country_iso_v1
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
ret geocode_place_country_iso_v1%rowtype;
geo GEOMETRY;
BEGIN
FOR ret IN
SELECT
q, c, iso3, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, c, (SELECT iso3 FROM country_decoder WHERE
lower(d.c) = ANY (synonyms) LIMIT 1) iso3, (
SELECT the_geom
FROM global_postal_code_points
WHERE postal_code = upper(d.q)
AND iso3 = (
SELECT iso3 FROM country_decoder WHERE
lower(d.c) = ANY (synonyms) LIMIT 1
)
LIMIT 1
) geom
FROM (SELECT unnest(code) q, unnest(inputcountries) c) d
) v
LOOP
IF ret.geom IS NULL AND ret.iso3 = 'GBR' THEN
geo := geocode_greatbritain_outward(ret.q);
IF geo IS NOT NULL THEN
ret.geom := geo;
ret.success := TRUE;
END IF;
END IF;
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$;
CREATE FUNCTION geocode_greatbritain_outward(code text) RETURNS geometry
LANGUAGE plpgsql
AS $$
DECLARE
geom GEOMETRY;
BEGIN
code := trim(code);
geom := NULL;
IF array_length(string_to_array(code,' '),1) = 2 THEN
code := split_part(code, ' ', 1) || ' ' || rpad(substring(split_part(code, ' ', 2), 1, 1), 3, '#');
SELECT the_geom INTO geom FROM global_postal_code_points WHERE
postal_code = code
AND iso3 = 'GBR'
LIMIT 1;
END IF;
RETURN geom;
END
$$;
CREATE FUNCTION admin0_available_services(name text[]) RETURNS SETOF available_services_v1
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
ret available_services_v1%rowtype;
BEGIN RETURN QUERY
SELECT d.q, n.adm0_a3, n.postal_code_points, n.postal_code_polygons FROM
(
SELECT q, lower(regexp_replace(q, '[^a-zA-Z\u00C0-\u00ff]+', '', 'g'))::text x FROM
(
SELECT unnest(name) q
)
g) d LEFT OUTER JOIN admin0_synonyms s ON name_ = d.x LEFT OUTER JOIN available_services n ON s.adm0_a3 = n.adm0_a3 GROUP BY d.q, n.adm0_a3, n.postal_code_points, n.postal_code_polygons;
END
$$;
--------------------------------------------------------------------------------
-- Support tables
CREATE TABLE global_postal_code_polygons (
the_geom geometry(Geometry,4326),
zcta5ce10 text,
geoid10 text,
mtfcc10 text,
cartodb_id integer NOT NULL,
created_at timestamp with time zone DEFAULT now() NOT NULL,
updated_at timestamp with time zone DEFAULT now() NOT NULL,
the_geom_webmercator geometry(Geometry,3857),
iso3 text,
postal_code text,
postal_code_num integer
);
CREATE SEQUENCE global_postal_code_polygons_cartodb_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE global_postal_code_polygons_cartodb_id_seq OWNED BY global_postal_code_polygons.cartodb_id;
ALTER TABLE ONLY global_postal_code_polygons ALTER COLUMN cartodb_id SET DEFAULT nextval('global_postal_code_polygons_cartodb_id_seq'::regclass);
ALTER TABLE ONLY global_postal_code_polygons
ADD CONSTRAINT global_postal_code_polygons_cartodb_id_key UNIQUE (cartodb_id);
ALTER TABLE ONLY global_postal_code_polygons
ADD CONSTRAINT global_postal_code_polygons_pkey PRIMARY KEY (cartodb_id);
CREATE INDEX global_postal_code_polygons_the_geom_idx ON global_postal_code_polygons USING gist (the_geom);
CREATE INDEX global_postal_code_polygons_the_geom_webmercator_idx ON global_postal_code_polygons USING gist (the_geom_webmercator);
CREATE INDEX global_postal_code_polygons_postal_code_idx ON global_postal_code_polygons USING btree (postal_code);
CREATE INDEX global_postal_code_polygons_iso3_idx ON global_postal_code_polygons USING btree (iso3);
CREATE INDEX global_global_postal_code_polygons_postal_code_num_idx ON global_postal_code_polygons USING btree (postal_code_num);
CREATE TRIGGER track_updates AFTER INSERT OR DELETE OR UPDATE OR TRUNCATE ON global_postal_code_polygons FOR EACH STATEMENT EXECUTE PROCEDURE cartodb.cdb_tablemetadata_trigger();
CREATE TRIGGER update_the_geom_webmercator_trigger BEFORE INSERT OR UPDATE OF the_geom ON global_postal_code_polygons FOR EACH ROW EXECUTE PROCEDURE cartodb._cdb_update_the_geom_webmercator();
CREATE TRIGGER update_updated_at_trigger BEFORE UPDATE ON global_postal_code_polygons FOR EACH ROW EXECUTE PROCEDURE cartodb._cdb_update_updated_at();
CREATE TABLE global_postal_code_points (
iso2 text,
postal_code text,
place_name text,
admin_name1 text,
admin_code1 text,
admin_name2 text,
admin_code2 text,
admin_name3 text,
admin_code3 text,
accuracy text,
the_geom geometry(Geometry,4326),
cartodb_id integer NOT NULL,
created_at timestamp with time zone DEFAULT now() NOT NULL,
updated_at timestamp with time zone DEFAULT now() NOT NULL,
the_geom_webmercator geometry(Geometry,3857),
iso3 text,
frompoly boolean,
postal_code_num integer,
datasource text
);
CREATE SEQUENCE global_postal_code_points_cartodb_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE global_postal_code_points_cartodb_id_seq OWNED BY global_postal_code_points.cartodb_id;
ALTER TABLE ONLY global_postal_code_points ALTER COLUMN cartodb_id SET DEFAULT nextval('global_postal_code_points_cartodb_id_seq'::regclass);
ALTER TABLE ONLY global_postal_code_points
ADD CONSTRAINT global_postal_code_points_cartodb_id_key UNIQUE (cartodb_id);
ALTER TABLE ONLY global_postal_code_points
ADD CONSTRAINT global_postal_code_points_pkey PRIMARY KEY (cartodb_id);
CREATE INDEX global_postal_code_points_the_geom_idx ON global_postal_code_points USING gist (the_geom);
CREATE INDEX global_postal_code_points_the_geom_webmercator_idx ON global_postal_code_points USING gist (the_geom_webmercator);
CREATE INDEX global_postal_code_points_postal_code_idx ON global_postal_code_points USING btree (postal_code);
CREATE INDEX global_postal_code_points_iso3_idx ON global_postal_code_points USING btree (iso3);
CREATE INDEX global_postal_code_points_postal_code_num_idx ON global_postal_code_points USING btree (postal_code_num);
CREATE TRIGGER track_updates AFTER INSERT OR DELETE OR UPDATE OR TRUNCATE ON global_postal_code_points FOR EACH STATEMENT EXECUTE PROCEDURE cartodb.cdb_tablemetadata_trigger();
CREATE TRIGGER update_the_geom_webmercator_trigger BEFORE INSERT OR UPDATE OF the_geom ON global_postal_code_points FOR EACH ROW EXECUTE PROCEDURE cartodb._cdb_update_the_geom_webmercator();
CREATE TRIGGER update_updated_at_trigger BEFORE UPDATE ON global_postal_code_points FOR EACH ROW EXECUTE PROCEDURE cartodb._cdb_update_updated_at();
CREATE TABLE available_services (
adm0_a3 text,
admin0 boolean,
cartodb_id integer NOT NULL,
created_at timestamp with time zone DEFAULT now() NOT NULL,
updated_at timestamp with time zone DEFAULT now() NOT NULL,
the_geom geometry(Geometry,4326),
the_geom_webmercator geometry(Geometry,3857),
postal_code_points boolean,
postal_code_polygons boolean
);
CREATE SEQUENCE available_services_cartodb_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE available_services_cartodb_id_seq OWNED BY available_services.cartodb_id;
ALTER TABLE ONLY available_services ALTER COLUMN cartodb_id SET DEFAULT nextval('available_services_cartodb_id_seq'::regclass);
ALTER TABLE ONLY available_services
ADD CONSTRAINT available_services_cartodb_id_key UNIQUE (cartodb_id);
ALTER TABLE ONLY available_services
ADD CONSTRAINT available_services_pkey PRIMARY KEY (cartodb_id);
CREATE INDEX available_services_the_geom_idx ON available_services USING gist (the_geom);
CREATE INDEX available_services_the_geom_webmercator_idx ON available_services USING gist (the_geom_webmercator);
CREATE TRIGGER track_updates AFTER INSERT OR DELETE OR UPDATE OR TRUNCATE ON available_services FOR EACH STATEMENT EXECUTE PROCEDURE cartodb.cdb_tablemetadata_trigger();
CREATE TRIGGER update_the_geom_webmercator_trigger BEFORE INSERT OR UPDATE OF the_geom ON available_services FOR EACH ROW EXECUTE PROCEDURE cartodb._cdb_update_the_geom_webmercator();
CREATE TRIGGER update_updated_at_trigger BEFORE UPDATE ON available_services FOR EACH ROW EXECUTE PROCEDURE cartodb._cdb_update_updated_at();
CREATE TABLE country_decoder (
name text,
nativename text,
tld text,
iso2 text,
ccn3 text,
iso3 text,
currency text,
callingcode text,
capital text,
altspellings text,
relevance text,
region text,
subregion text,
language text,
languagescodes text,
translations text,
population text,
latlng text,
demonym text,
borders text,
the_geom geometry(Geometry,4326),
cartodb_id integer NOT NULL,
created_at timestamp with time zone DEFAULT now() NOT NULL,
updated_at timestamp with time zone DEFAULT now() NOT NULL,
the_geom_webmercator geometry(Geometry,3857),
synbu text[],
synonyms text[],
users double precision
);
CREATE SEQUENCE country_decoder_cartodb_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE available_services_cartodb_id_seq OWNED BY country_decoder.cartodb_id;
ALTER TABLE ONLY country_decoder ALTER COLUMN cartodb_id SET DEFAULT nextval('country_decoder_cartodb_id_seq'::regclass);
ALTER TABLE ONLY country_decoder
ADD CONSTRAINT country_decoder_cartodb_id_key UNIQUE (cartodb_id);
ALTER TABLE ONLY country_decoder
ADD CONSTRAINT country_decoder_pkey PRIMARY KEY (cartodb_id);
CREATE INDEX country_decoder_the_geom_idx ON country_decoder USING gist (the_geom);
CREATE INDEX country_decoder_the_geom_webmercator_idx ON country_decoder USING gist (the_geom_webmercator);
CREATE TRIGGER track_updates AFTER INSERT OR DELETE OR UPDATE OR TRUNCATE ON country_decoder FOR EACH STATEMENT EXECUTE PROCEDURE cartodb.cdb_tablemetadata_trigger();
CREATE TRIGGER update_the_geom_webmercator_trigger BEFORE INSERT OR UPDATE OF the_geom ON country_decoder FOR EACH ROW EXECUTE PROCEDURE cartodb._cdb_update_the_geom_webmercator();
CREATE TRIGGER update_updated_at_trigger BEFORE UPDATE ON country_decoder FOR EACH ROW EXECUTE PROCEDURE cartodb._cdb_update_updated_at();
CREATE TABLE admin0_synonyms (
name text,
rank double precision,
created_at timestamp with time zone DEFAULT now() NOT NULL,
updated_at timestamp with time zone DEFAULT now() NOT NULL,
the_geom geometry(Geometry,4326),
the_geom_webmercator geometry(Geometry,3857),
cartodb_id integer NOT NULL,
adm0_a3 text,
name_ text
);
CREATE SEQUENCE admin0_synonyms_cartodb_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE admin0_synonyms_cartodb_id_seq OWNED BY admin0_synonyms.cartodb_id;
ALTER TABLE ONLY admin0_synonyms ALTER COLUMN cartodb_id SET DEFAULT nextval('admin0_synonyms_cartodb_id_seq'::regclass);
ALTER TABLE ONLY admin0_synonyms
ADD CONSTRAINT admin0_synonyms_cartodb_id_key UNIQUE (cartodb_id);
ALTER TABLE ONLY admin0_synonyms
ADD CONSTRAINT admin0_synonyms_pkey PRIMARY KEY (cartodb_id);
CREATE INDEX admin0_synonyms_the_geom_idx ON admin0_synonyms USING gist (the_geom);
CREATE INDEX admin0_synonyms_the_geom_webmercator_idx ON admin0_synonyms USING gist (the_geom_webmercator);
CREATE INDEX idx_admin0_synonyms_nam ON admin0_synonyms USING btree (name);
CREATE INDEX idx_admin0_synonyms_name ON admin0_synonyms USING btree (lower(regexp_replace(name, '\W+'::text, ''::text)));
CREATE INDEX idx_admin0_synonyms_name_ ON admin0_synonyms USING btree (name_);
CREATE INDEX idx_admin0_synonyms_name_patt ON admin0_synonyms USING btree (name_ text_pattern_ops);
CREATE INDEX idx_admin0_synonyms_name_rank ON admin0_synonyms USING btree (name_, rank);
CREATE INDEX idx_admin0_synonyms_rank ON admin0_synonyms USING btree (rank);
-- create trigger function. used in both admin0 and admin1 synonym tables
CREATE OR REPLACE FUNCTION alpha_numeric_identifiers() RETURNS trigger AS $alpha_numeric_identifiers$
BEGIN
NEW.name_ := lower(regexp_replace(NEW.name, '[^a-zA-Z\u00C0-\u00ff]+', '', 'g'));
RETURN NEW;
END;
$alpha_numeric_identifiers$ LANGUAGE plpgsql;
CREATE TRIGGER admin0_synonyms_name_update BEFORE INSERT OR UPDATE OF name ON admin0_synonyms FOR EACH ROW EXECUTE PROCEDURE alpha_numeric_identifiers();
CREATE TRIGGER track_updates AFTER INSERT OR DELETE OR UPDATE OR TRUNCATE ON admin0_synonyms FOR EACH STATEMENT EXECUTE PROCEDURE cartodb.cdb_tablemetadata_trigger();
CREATE TRIGGER update_the_geom_webmercator_trigger BEFORE INSERT OR UPDATE OF the_geom ON admin0_synonyms FOR EACH ROW EXECUTE PROCEDURE cartodb._cdb_update_the_geom_webmercator();
CREATE TRIGGER update_updated_at_trigger BEFORE UPDATE ON admin0_synonyms FOR EACH ROW EXECUTE PROCEDURE cartodb._cdb_update_updated_at();

View File

@@ -1,6 +0,0 @@
# cdb geocoder postalcode extension
comment = 'CartoDB postalcode internal geocoder'
default_version = '0.0.1'
relocatable = true
requires = cartodb
superuser = false

View File

@@ -1,108 +0,0 @@
CREATE EXTENSION postgis;
CREATE EXTENSION schema_triggers;
CREATE EXTENSION plpythonu;
CREATE EXTENSION cartodb;
CREATE EXTENSION cdb_geocoder_postalcode;
SELECT (geocode_postalcode_polygons(Array['03204'], Array['Spain'])).*;
q | c | geom | success
-------+-------+------+---------
03204 | Spain | | f
(1 row)
SELECT (geocode_postalcode_polygons(Array['03204'], 'ESP')).*;
q | geom | success
-------+------+---------
03204 | | f
(1 row)
SELECT (geocode_postalcode_polygons(Array['03204'])).*;
q | geom | success
-------+------+---------
03204 | | f
(1 row)
SELECT (geocode_postalcode_points(Array['03204'], 'Spain')).*;
q | geom | success
-------+------+---------
03204 | | f
(1 row)
SELECT (geocode_postalcode_points('{03204}', 'Spain')).*;
q | geom | success
-------+------+---------
03204 | | f
(1 row)
SELECT (geocode_postalcode_points(Array['03204'])).*;
q | geom | success
-------+------+---------
03204 | | f
(1 row)
SELECT (geocode_postalcode_points(Array['03204'], Array['Spain'])).*;
q | c | iso3 | geom | success
-------+-------+------+------+---------
03204 | Spain | | | f
(1 row)
SELECT geocode_greatbritain_outward('YO1 721');
geocode_greatbritain_outward
------------------------------
(1 row)
SELECT (admin0_available_services(Array['Spain'])).*;
q | adm0_a3 | postal_code_points | postal_code_polygons
-------+---------+--------------------+----------------------
Spain | | |
(1 row)
-- Mock the varnish invalidation function
CREATE OR REPLACE FUNCTION public.cdb_invalidate_varnish(table_name text) RETURNS void AS $$
BEGIN
RETURN;
END
$$
LANGUAGE plpgsql;
-- Insert mock source data
INSERT INTO global_postal_code_points (the_geom, iso3, postal_code, postal_code_num) VALUES (
'0101000020E61000000000000000E040408036B47414764840',
'ESP',
'03204',
3204
);
INSERT INTO global_postal_code_polygons (the_geom, iso3, postal_code, postal_code_num) VALUES (
'0106000020E610000001000000010300000001000000040000000000000000E000C01F383D7839B740400000000000E000C0AA3C0EDE220F3B4000000000004812404FB7FCCD04893D400000000000E000C01F383D7839B74040',
'ESP',
'03204',
3204
);
INSERT INTO country_decoder (iso3, synonyms) VALUES (
'ESP',
Array['spain', 'Spain', 'ESP']
);
INSERT INTO available_services (adm0_a3, admin0, postal_code_points, postal_code_polygons) VALUES (
'ESP',
't',
't',
't'
);
INSERT INTO admin0_synonyms (adm0_a3, name, name_, rank) VALUES (
'ESP',
'Spain',
'spain',
3
);
INSERT INTO admin0_synonyms (adm0_a3, name, name_, rank) VALUES (
'ESP',
'ESP',
'esp',
4
);
-- Check that the geocoding function is callable, should return success = true
SELECT (geocode_postalcode_polygons(Array['03204'], Array['Spain'])).geom;
geom
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
0106000020E610000001000000010300000001000000040000000000000000E000C01F383D7839B740400000000000E000C0AA3C0EDE220F3B4000000000004812404FB7FCCD04893D400000000000E000C01F383D7839B74040
(1 row)

View File

@@ -1,67 +0,0 @@
CREATE EXTENSION postgis;
CREATE EXTENSION schema_triggers;
CREATE EXTENSION plpythonu;
CREATE EXTENSION cartodb;
CREATE EXTENSION cdb_geocoder_postalcode;
SELECT (geocode_postalcode_polygons(Array['03204'], Array['Spain'])).*;
SELECT (geocode_postalcode_polygons(Array['03204'], 'ESP')).*;
SELECT (geocode_postalcode_polygons(Array['03204'])).*;
SELECT (geocode_postalcode_points(Array['03204'], 'Spain')).*;
SELECT (geocode_postalcode_points('{03204}', 'Spain')).*;
SELECT (geocode_postalcode_points(Array['03204'])).*;
SELECT (geocode_postalcode_points(Array['03204'], Array['Spain'])).*;
SELECT geocode_greatbritain_outward('YO1 721');
SELECT (admin0_available_services(Array['Spain'])).*;
-- Mock the varnish invalidation function
CREATE OR REPLACE FUNCTION public.cdb_invalidate_varnish(table_name text) RETURNS void AS $$
BEGIN
RETURN;
END
$$
LANGUAGE plpgsql;
-- Insert mock source data
INSERT INTO global_postal_code_points (the_geom, iso3, postal_code, postal_code_num) VALUES (
'0101000020E61000000000000000E040408036B47414764840',
'ESP',
'03204',
3204
);
INSERT INTO global_postal_code_polygons (the_geom, iso3, postal_code, postal_code_num) VALUES (
'0106000020E610000001000000010300000001000000040000000000000000E000C01F383D7839B740400000000000E000C0AA3C0EDE220F3B4000000000004812404FB7FCCD04893D400000000000E000C01F383D7839B74040',
'ESP',
'03204',
3204
);
INSERT INTO country_decoder (iso3, synonyms) VALUES (
'ESP',
Array['spain', 'Spain', 'ESP']
);
INSERT INTO available_services (adm0_a3, admin0, postal_code_points, postal_code_polygons) VALUES (
'ESP',
't',
't',
't'
);
INSERT INTO admin0_synonyms (adm0_a3, name, name_, rank) VALUES (
'ESP',
'Spain',
'spain',
3
);
INSERT INTO admin0_synonyms (adm0_a3, name, name_, rank) VALUES (
'ESP',
'ESP',
'esp',
4
);
-- Check that the geocoding function is callable, should return success = true
SELECT (geocode_postalcode_polygons(Array['03204'], Array['Spain'])).geom;

View File

@@ -1,320 +0,0 @@
-- Functions for points --
-- codes array, country text
CREATE OR REPLACE FUNCTION geocode_postalcode_points(code text[], inputcountry text)
RETURNS SETOF geocode_namedplace_v1 AS $$
DECLARE
ret geocode_namedplace_v1%rowtype;
BEGIN
FOR ret IN
SELECT
q, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, (
SELECT the_geom
FROM global_postal_code_points
WHERE postal_code = upper(d.q)
AND iso3 = (
SELECT iso3 FROM country_decoder WHERE
lower(inputcountry) = ANY (synonyms) LIMIT 1
)
LIMIT 1
) geom
FROM (SELECT unnest(code) q) d
) v
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
-- codes array, countries array
CREATE OR REPLACE FUNCTION geocode_postalcode_points(code text[], inputcountries text[])
RETURNS SETOF geocode_place_country_iso_v1 AS $$
DECLARE
ret geocode_place_country_iso_v1%rowtype;
geo GEOMETRY;
BEGIN
FOR ret IN
SELECT
q, c, iso3, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, c, (SELECT iso3 FROM country_decoder WHERE
lower(d.c) = ANY (synonyms) LIMIT 1) iso3, (
SELECT the_geom
FROM global_postal_code_points
WHERE postal_code = upper(d.q)
AND iso3 = (
SELECT iso3 FROM country_decoder WHERE
lower(d.c) = ANY (synonyms) LIMIT 1
)
LIMIT 1
) geom
FROM (SELECT unnest(code) q, unnest(inputcountries) c) d
) v
LOOP
IF ret.geom IS NULL AND ret.iso3 = 'GBR' THEN
geo := geocode_greatbritain_outward(ret.q);
IF geo IS NOT NULL THEN
ret.geom := geo;
ret.success := TRUE;
END IF;
END IF;
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
-- codes array text
CREATE OR REPLACE FUNCTION geocode_postalcode_points(code text[])
RETURNS SETOF geocode_namedplace_v1 AS $$
DECLARE
ret geocode_namedplace_v1%rowtype;
BEGIN
FOR ret IN
SELECT
q, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, (
SELECT the_geom
FROM global_postal_code_points
WHERE postal_code = upper(d.q)
LIMIT 1
) geom
FROM (SELECT unnest(code) q) d
) v
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
-- codes array integers, countries array
CREATE OR REPLACE FUNCTION geocode_postalcode_points(code integer[], inputcountries text[])
RETURNS SETOF geocode_postalint_country_v1 AS $$
DECLARE
ret geocode_postalint_country_v1%rowtype;
BEGIN
FOR ret IN
SELECT
q, c, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, c, (
SELECT the_geom
FROM global_postal_code_points
WHERE postal_code_num = d.q
AND iso3 = (
SELECT iso3 FROM country_decoder WHERE
lower(d.c) = ANY (synonyms) LIMIT 1
)
LIMIT 1
) geom
FROM (SELECT unnest(code) q, unnest(inputcountries) c) d
) v
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
-- Functions for polygons --
-- Usage
--SELECT (geocode_postalcode_polygons(Array['10013','G9H','03782'], Array['USA', 'Canada', 'US'])).*
-- codes array, countries array
CREATE FUNCTION geocode_postalcode_polygons(code text[], inputcountries text[]) RETURNS SETOF geocode_namedplace_country_v1
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
ret geocode_namedplace_country_v1%rowtype;
adm text[];
BEGIN
SELECT INTO adm array_agg((SELECT adm0_a3 FROM admin0_synonyms WHERE name_ = lower(regexp_replace(b.c, '[^a-zA-Z\u00C0-\u00ff]+', '', 'g'))::text LIMIT 1)) FROM (SELECT UNNEST(inputcountries) c) b;
FOR ret IN
SELECT
q, c, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, c, (
SELECT the_geom
FROM global_postal_code_polygons
WHERE postal_code = CASE WHEN a = 'CAN' THEN substring(upper(d.q) from 1 for 3) ELSE upper(d.q) END
AND iso3 = a
) geom
FROM (SELECT unnest(code) q, unnest(inputcountries) c, unnest(adm) a) d
) v
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$;
-- codes array integers, countries array
CREATE OR REPLACE FUNCTION geocode_postalcode_polygons(code integer[], inputcountries text[])
RETURNS SETOF geocode_postalint_country_v1 AS $$
DECLARE
ret geocode_postalint_country_v1%rowtype;
BEGIN
FOR ret IN
SELECT
q, c, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, c, (
SELECT the_geom
FROM global_postal_code_polygons
WHERE postal_code_num = d.q
AND iso3 = (
SELECT iso3 FROM country_decoder WHERE
lower(d.c) = ANY (synonyms) LIMIT 1
)
) geom
FROM (SELECT unnest(code) q, unnest(inputcountries) c) d
) v
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
-- codes array text
CREATE OR REPLACE FUNCTION geocode_postalcode_polygons(code text[])
RETURNS SETOF geocode_namedplace_v1 AS $$
DECLARE
ret geocode_namedplace_v1%rowtype;
BEGIN
FOR ret IN
SELECT
q, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, (
SELECT the_geom
FROM global_postal_code_polygons
WHERE postal_code = upper(d.q)
LIMIT 1
) geom
FROM (SELECT unnest(code) q) d
) v
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
-- codes array text, countries text
CREATE OR REPLACE FUNCTION geocode_postalcode_polygons(code text[], inputcountry text)
RETURNS SETOF geocode_namedplace_v1 AS $$
DECLARE
ret geocode_namedplace_v1%rowtype;
BEGIN
FOR ret IN
SELECT
q, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, (
SELECT the_geom
FROM global_postal_code_polygons
WHERE postal_code = upper(d.q)
AND iso3 = (
SELECT iso3 FROM country_decoder WHERE
lower(inputcountry) = ANY (synonyms) LIMIT 1
)
) geom
FROM (SELECT unnest(code) q) d
) v
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
-- Test functions for polygons --
-- code array, countries array
CREATE OR REPLACE FUNCTION test_geocode_postalcode_polygons(code text[], inputcountries text[])
RETURNS SETOF geocode_namedplace_country_v1 AS $$
DECLARE
ret geocode_namedplace_country_v1%rowtype;
adm text[];
BEGIN
SELECT INTO adm array_agg((SELECT adm0_a3 FROM admin0_synonyms WHERE name_ = lower(regexp_replace(b.c, '[^a-zA-Z\u00C0-\u00ff]+', '', 'g'))::text LIMIT 1)) FROM (SELECT UNNEST(inputcountries) c) b;
FOR ret IN
SELECT
q, c, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, c, (
SELECT the_geom
FROM postal_code_polygons
WHERE postal_code = CASE WHEN a = 'CAN' THEN substring(upper(d.q) from 1 for 3) ELSE upper(d.q) END
AND adm0_a3 = a
) geom
FROM (SELECT unnest(code) q, unnest(inputcountries) c, unnest(adm) a) d
) v
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
-- codes array, country text
CREATE OR REPLACE FUNCTION test_geocode_postalcode_polygons(code text[], inputcountry text)
RETURNS SETOF geocode_namedplace_v1 AS $$
DECLARE
c text;
c_iso3 text;
ret geocode_namedplace_v1%rowtype;
BEGIN
c := inputcountry;
SELECT INTO c_iso3 adm0_a3 FROM admin0_synonyms WHERE name_ = lower(regexp_replace(c, '[^a-zA-Z\u00C0-\u00ff]+', '', 'g'))::text LIMIT 1;
FOR ret IN
SELECT
q, geom, CASE WHEN geom IS NULL THEN FALSE ELSE TRUE END AS success
FROM (
SELECT
q, (
SELECT the_geom
FROM postal_code_polygons
WHERE postal_code = upper(d.q)
AND adm0_a3 = c_iso3
) geom
FROM (SELECT unnest(code) q) d
) v
LOOP
RETURN NEXT ret;
END LOOP;
RETURN;
END
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;