1200 lines
39 KiB
PL/PgSQL
1200 lines
39 KiB
PL/PgSQL
|
|
--For Longer term Dev
|
|
|
|
|
|
--Break out table definitions to types
|
|
--Automate type creation from a script, something like
|
|
----CREATE OR REPLACE FUNCTION OBS_Get<%=tag_name%>(geom GEOMETRY)
|
|
----RETURNS TABLE(
|
|
----<%=get_dimensions_for_tag(tag_name)%>
|
|
----AS $$
|
|
----DECLARE
|
|
----target_cols text[];
|
|
----names text[];
|
|
----vals NUMERIC[];-
|
|
----q text;
|
|
----BEGIN
|
|
----target_cols := Array[<%=get_dimensions_for_tag(tag_name)%>],
|
|
|
|
|
|
--Functions for augmenting specific tables
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- Creates a table of demographic snapshot
|
|
|
|
CREATE OR REPLACE FUNCTION cdb_observatory.OBS_GetDemographicSnapshot(geom geometry(Geometry, 4326),
|
|
time_span text DEFAULT NULL,
|
|
boundary_id text DEFAULT NULL)
|
|
RETURNS SETOF JSON
|
|
AS $$
|
|
DECLARE
|
|
target_cols text[];
|
|
BEGIN
|
|
|
|
IF time_span IS NULL THEN
|
|
time_span = '2010 - 2014';
|
|
END IF;
|
|
|
|
IF boundary_id IS NULL THEN
|
|
boundary_id = 'us.census.tiger.block_group';
|
|
END IF;
|
|
|
|
target_cols := Array['us.census.acs.B01003001',
|
|
'us.census.acs.B01001002',
|
|
'us.census.acs.B01001026',
|
|
'us.census.acs.B01002001',
|
|
'us.census.acs.B03002003',
|
|
'us.census.acs.B03002004',
|
|
'us.census.acs.B03002006',
|
|
'us.census.acs.B03002012',
|
|
'us.census.acs.B03002005',
|
|
'us.census.acs.B03002008',
|
|
'us.census.acs.B03002009',
|
|
'us.census.acs.B03002002',
|
|
--'not_us_citizen_pop',
|
|
--'workers_16_and_over',
|
|
--'commuters_by_car_truck_van',
|
|
--'commuters_drove_alone',
|
|
--'commuters_by_carpool',
|
|
--'commuters_by_public_transportation',
|
|
--'commuters_by_bus',
|
|
--'commuters_by_subway_or_elevated',
|
|
--'walked_to_work',
|
|
--'worked_at_home',
|
|
--'children',
|
|
'us.census.acs.B11001001',
|
|
--'population_3_years_over',
|
|
--'in_school',
|
|
--'in_grades_1_to_4',
|
|
--'in_grades_5_to_8',
|
|
--'in_grades_9_to_12',
|
|
--'in_undergrad_college',
|
|
'us.census.acs.B15003001',
|
|
'us.census.acs.B15003017',
|
|
'us.census.acs.B15003019',
|
|
'us.census.acs.B15003020',
|
|
'us.census.acs.B15003021',
|
|
'us.census.acs.B15003022',
|
|
'us.census.acs.B15003023',
|
|
--'pop_5_years_over',
|
|
--'speak_only_english_at_home',
|
|
--'speak_spanish_at_home',
|
|
--'pop_determined_poverty_status',
|
|
--'poverty',
|
|
'us.census.acs.B19013001',
|
|
'us.census.acs.B19083001',
|
|
'us.census.acs.B19301001',
|
|
'us.census.acs.B25001001',
|
|
'us.census.acs.B25002003',
|
|
'us.census.acs.B25004002',
|
|
'us.census.acs.B25004004',
|
|
'us.census.acs.B25058001',
|
|
'us.census.acs.B25071001',
|
|
'us.census.acs.B25075001',
|
|
'us.census.acs.B25075025',
|
|
'us.census.acs.B25081002',
|
|
--'pop_15_and_over',
|
|
--'pop_never_married',
|
|
--'pop_now_married',
|
|
--'pop_separated',
|
|
--'pop_widowed',
|
|
--'pop_divorced',
|
|
'us.census.acs.B08134001',
|
|
'us.census.acs.B08134002',
|
|
'us.census.acs.B08134003',
|
|
'us.census.acs.B08134004',
|
|
'us.census.acs.B08134005',
|
|
'us.census.acs.B08134006',
|
|
'us.census.acs.B08134007',
|
|
'us.census.acs.B08134008',
|
|
'us.census.acs.B08134009',
|
|
'us.census.acs.B08134010',
|
|
'us.census.acs.B08135001',
|
|
'us.census.acs.B19001002',
|
|
'us.census.acs.B19001003',
|
|
'us.census.acs.B19001004',
|
|
'us.census.acs.B19001005',
|
|
'us.census.acs.B19001006',
|
|
'us.census.acs.B19001007',
|
|
'us.census.acs.B19001008',
|
|
'us.census.acs.B19001009',
|
|
'us.census.acs.B19001010',
|
|
'us.census.acs.B19001011',
|
|
'us.census.acs.B19001012',
|
|
'us.census.acs.B19001013',
|
|
'us.census.acs.B19001014',
|
|
'us.census.acs.B19001015',
|
|
'us.census.acs.B19001016',
|
|
'us.census.acs.B19001017'];
|
|
RETURN QUERY
|
|
EXECUTE
|
|
'select * from cdb_observatory._OBS_Get($1, $2, $3, $4 )'
|
|
USING geom, target_cols, time_span, boundary_id
|
|
RETURN;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
|
|
--Base functions for performing augmentation
|
|
----------------------------------------------------------------------------------------
|
|
|
|
-- Base augmentation fucntion.
|
|
CREATE OR REPLACE FUNCTION cdb_observatory._OBS_Get(
|
|
geom geometry(Geometry, 4326),
|
|
column_ids text[],
|
|
time_span text,
|
|
geometry_level text
|
|
)
|
|
RETURNS SETOF JSON
|
|
AS $$
|
|
DECLARE
|
|
results json[];
|
|
geom_table_name text;
|
|
names text[];
|
|
query text;
|
|
data_table_info json[];
|
|
BEGIN
|
|
|
|
EXECUTE
|
|
'SELECT array_agg(_obs_getcolumndata)
|
|
FROM cdb_observatory._OBS_GetColumnData($1, $2, $3);'
|
|
INTO data_table_info
|
|
USING geometry_level, column_ids, time_span;
|
|
|
|
IF geometry_level IS NULL THEN
|
|
geometry_level = data_table_info[1]->>'boundary_id';
|
|
END IF;
|
|
|
|
geom_table_name := cdb_observatory._OBS_GeomTable(geom, geometry_level);
|
|
|
|
IF geom_table_name IS NULL
|
|
THEN
|
|
--raise notice 'Point % is outside of the data region', ST_AsText(geom);
|
|
-- TODO this should return JSON
|
|
RETURN QUERY SELECT '{}'::json;
|
|
RETURN;
|
|
END IF;
|
|
|
|
IF data_table_info IS NULL THEN
|
|
--raise notice 'Cannot find data table for boundary ID %, column_ids %, and time_span %', geometry_level, column_ids, time_span;
|
|
END IF;
|
|
|
|
IF geom IS NULL
|
|
THEN
|
|
results := NULL;
|
|
ELSIF ST_GeometryType(geom) = 'ST_Point'
|
|
THEN
|
|
--raise notice 'geom_table_name %, data_table_info %', geom_table_name, data_table_info::json[];
|
|
results := cdb_observatory._OBS_GetPoints(geom,
|
|
geom_table_name,
|
|
data_table_info);
|
|
|
|
ELSIF ST_GeometryType(geom) IN ('ST_Polygon', 'ST_MultiPolygon')
|
|
THEN
|
|
results := cdb_observatory._OBS_GetPolygons(geom,
|
|
geom_table_name,
|
|
data_table_info);
|
|
END IF;
|
|
|
|
RETURN QUERY
|
|
EXECUTE
|
|
$query$
|
|
SELECT unnest($1)
|
|
$query$
|
|
USING results;
|
|
RETURN;
|
|
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
|
|
-- If the variable of interest is just a rate return it as such,
|
|
-- otherwise normalize it to the census block area and return that
|
|
CREATE OR REPLACE FUNCTION cdb_observatory._OBS_GetPoints(
|
|
geom geometry(Geometry, 4326),
|
|
geom_table_name text, -- TODO: change to boundary_id
|
|
data_table_info json[]
|
|
)
|
|
RETURNS json[]
|
|
AS $$
|
|
DECLARE
|
|
result NUMERIC[];
|
|
json_result json[];
|
|
query text;
|
|
i int;
|
|
geoid text;
|
|
data_geoid_colname text;
|
|
geom_geoid_colname text;
|
|
area NUMERIC;
|
|
BEGIN
|
|
|
|
-- TODO we're assuming our geom_table has only one geom_ref column
|
|
-- we *really* should pass in both geom_table_name and boundary_id
|
|
-- TODO tablename should not be passed here (use boundary_id)
|
|
EXECUTE
|
|
'SELECT ct.colname
|
|
FROM observatory.obs_column_to_column c2c,
|
|
observatory.obs_column_table ct,
|
|
observatory.obs_table t
|
|
WHERE c2c.reltype = ''geom_ref''
|
|
AND ct.column_id = c2c.source_id
|
|
AND ct.table_id = t.id
|
|
AND t.tablename = $1'
|
|
INTO data_geoid_colname USING (data_table_info)[1]->>'tablename';
|
|
EXECUTE
|
|
'SELECT ct.colname
|
|
FROM observatory.obs_column_to_column c2c,
|
|
observatory.obs_column_table ct,
|
|
observatory.obs_table t
|
|
WHERE c2c.reltype = ''geom_ref''
|
|
AND ct.column_id = c2c.source_id
|
|
AND ct.table_id = t.id
|
|
AND t.tablename = $1'
|
|
INTO geom_geoid_colname USING geom_table_name;
|
|
|
|
EXECUTE
|
|
format('SELECT %I
|
|
FROM observatory.%I
|
|
WHERE ST_Within($1, the_geom)',
|
|
geom_geoid_colname,
|
|
geom_table_name)
|
|
USING geom
|
|
INTO geoid;
|
|
|
|
--raise notice 'geoid is %, geometry table is % ', geoid, geom_table_name;
|
|
|
|
EXECUTE
|
|
format('SELECT ST_Area(the_geom::geography) / (1000 * 1000)
|
|
FROM observatory.%I
|
|
WHERE %I = $1',
|
|
geom_table_name,
|
|
geom_geoid_colname)
|
|
INTO area USING geoid;
|
|
|
|
IF area IS NULL
|
|
THEN
|
|
--raise notice 'No geometry at %', ST_AsText(geom);
|
|
END IF;
|
|
|
|
query := 'SELECT Array[';
|
|
FOR i IN 1..array_upper(data_table_info, 1)
|
|
LOOP
|
|
IF area is NULL OR area = 0
|
|
THEN
|
|
-- give back null values
|
|
query := query || format('NULL::numeric ');
|
|
ELSIF ((data_table_info)[i])->>'aggregate' != 'sum'
|
|
THEN
|
|
-- give back full variable
|
|
query := query || format('%I ', ((data_table_info)[i])->>'colname');
|
|
ELSE
|
|
-- give back variable normalized by area of geography
|
|
query := query || format('%I/%s ',
|
|
((data_table_info)[i])->>'colname',
|
|
area);
|
|
END IF;
|
|
|
|
IF i < array_upper(data_table_info, 1)
|
|
THEN
|
|
query := query || ',';
|
|
END IF;
|
|
END LOOP;
|
|
|
|
query := query || format(' ]::numeric[]
|
|
FROM observatory.%I
|
|
WHERE %I.%I = %L
|
|
',
|
|
((data_table_info)[1])->>'tablename',
|
|
((data_table_info)[1])->>'tablename',
|
|
data_geoid_colname,
|
|
geoid
|
|
);
|
|
|
|
EXECUTE
|
|
query
|
|
INTO result
|
|
USING geom;
|
|
|
|
EXECUTE
|
|
$query$
|
|
SELECT array_agg(row_to_json(t)) FROM (
|
|
SELECT values As value,
|
|
meta->>'name' As name,
|
|
meta->>'tablename' As tablename,
|
|
meta->>'aggregate' As aggregate,
|
|
meta->>'type' As type,
|
|
meta->>'description' As description
|
|
FROM (SELECT unnest($1) As values, unnest($2) As meta) b
|
|
) t
|
|
$query$
|
|
INTO json_result
|
|
USING result, data_table_info;
|
|
|
|
RETURN json_result;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION cdb_observatory.OBS_GetMeasureMetaMulti(
|
|
geom geometry(Geometry, 4326),
|
|
params JSON,
|
|
max_timespan_rank INTEGER DEFAULT NULL, -- cutoff for timespan ranks when there's ambiguity
|
|
max_score_rank INTEGER DEFAULT NULL, -- cutoff for geom ranks when there's ambiguity
|
|
target_geoms INTEGER DEFAULT NULL
|
|
)
|
|
RETURNS JSON
|
|
AS $$
|
|
DECLARE
|
|
result JSON;
|
|
BEGIN
|
|
IF max_timespan_rank IS NULL THEN
|
|
max_timespan_rank := 1;
|
|
END IF;
|
|
IF max_score_rank IS NULL THEN
|
|
max_score_rank := 1;
|
|
END IF;
|
|
EXECUTE $string$
|
|
WITH _filters AS (SELECT
|
|
generate_series(1, array_length($3, 1)) id,
|
|
(unnest($3))->>'numer_id' numer_id,
|
|
(unnest($3))->>'denom_id' denom_id,
|
|
(unnest($3))->>'geom_id' geom_id,
|
|
(unnest($3))->>'timespan' timespan
|
|
), meta AS (SELECT
|
|
id, geom_tid,
|
|
numer_aggregate, numer_colname, numer_geomref_colname,
|
|
numer_tablename, denom_aggregate, denom_colname, denom_geomref_colname,
|
|
denom_tablename, geom_colname, geom_geomref_colname,
|
|
geom_tablename, numer_name, denom_name, geom_name,
|
|
numer_type, denom_type, geom_type,
|
|
m.denom_id, m.geom_id, m.numer_timespan
|
|
FROM observatory.obs_meta m JOIN _filters f
|
|
ON m.numer_id = f.numer_id
|
|
WHERE
|
|
m.numer_id = ANY ($6)
|
|
AND (m.denom_id = f.denom_id OR COALESCE(f.denom_id, '') = '')
|
|
AND (m.geom_id = f.geom_id OR COALESCE(f.geom_id, '') = '')
|
|
AND (m.numer_timespan = f.timespan OR COALESCE(f.timespan, '') = '')
|
|
), scores AS (
|
|
SELECT *
|
|
FROM cdb_observatory._OBS_GetGeometryScores($1,
|
|
(SELECT Array_Agg(geom_id) FROM meta), $2) scores
|
|
), groups AS (SELECT
|
|
id, scores.score, numer_timespan,
|
|
dense_rank() OVER (PARTITION BY id ORDER BY numer_timespan DESC) timespan_rank,
|
|
dense_rank() OVER (PARTITION BY id ORDER BY score DESC) score_rank,
|
|
json_build_object(
|
|
'timespan_rank', dense_rank() OVER (PARTITION BY id ORDER BY numer_timespan DESC),
|
|
'score_rank', dense_rank() OVER (PARTITION BY id ORDER BY score DESC),
|
|
'score', scores.score,
|
|
'numer_aggregate', meta.numer_aggregate,
|
|
'numer_colname', meta.numer_colname,
|
|
'numer_geomref_colname', meta.numer_geomref_colname,
|
|
'numer_tablename', meta.numer_tablename,
|
|
'numer_type', meta.numer_type,
|
|
'denom_aggregate', meta.denom_aggregate,
|
|
'denom_colname', denom_colname,
|
|
'denom_geomref_colname', denom_geomref_colname,
|
|
'denom_tablename', denom_tablename,
|
|
'denom_type', meta.denom_type,
|
|
'geom_colname', geom_colname,
|
|
'geom_geomref_colname', geom_geomref_colname,
|
|
'geom_tablename', geom_tablename,
|
|
'geom_type', meta.geom_type,
|
|
'timespan', numer_timespan,
|
|
'numer_name', numer_name,
|
|
'denom_name', denom_name,
|
|
'geom_name', geom_name,
|
|
'denom_id', denom_id,
|
|
'geom_id', meta.geom_id
|
|
) metadata
|
|
FROM meta, scores
|
|
WHERE meta.geom_id = scores.column_id
|
|
AND meta.geom_tid = scores.table_id
|
|
) SELECT JSON_AGG(metadata ORDER BY id)
|
|
FROM groups
|
|
WHERE timespan_rank <= $4
|
|
AND score_rank <= $5
|
|
$string$
|
|
INTO result
|
|
USING
|
|
CASE WHEN ST_GeometryType(geom) = 'ST_Point' THEN
|
|
ST_Buffer(geom::geography, 200)::geometry(geometry, 4326)
|
|
ELSE geom
|
|
END,
|
|
target_geoms,
|
|
(SELECT ARRAY(SELECT json_array_elements_text(params))::json[]),
|
|
max_timespan_rank,
|
|
max_score_rank,
|
|
(SELECT Array_Agg(val) from (select (JSON_Array_Elements(params))->>'numer_id' val) foo)
|
|
;
|
|
RETURN result;
|
|
END;
|
|
$$ LANGUAGE plpgsql IMMUTABLE;
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION cdb_observatory.OBS_GetMeasure(
|
|
geom geometry(Geometry, 4326),
|
|
measure_id TEXT,
|
|
normalize TEXT DEFAULT NULL,
|
|
boundary_id TEXT DEFAULT NULL,
|
|
time_span TEXT DEFAULT NULL,
|
|
simplification NUMERIC DEFAULT 0.00001
|
|
)
|
|
RETURNS NUMERIC
|
|
AS $$
|
|
DECLARE
|
|
geom_type TEXT;
|
|
params JSON;
|
|
result NUMERIC;
|
|
numer_name TEXT;
|
|
denom_name TEXT;
|
|
geom_name TEXT;
|
|
denom_id TEXT;
|
|
map_type TEXT;
|
|
BEGIN
|
|
IF geom IS NULL THEN
|
|
RETURN NULL;
|
|
END IF;
|
|
|
|
IF simplification IS NOT NULL THEN
|
|
geom := ST_Simplify(geom, simplification);
|
|
END IF;
|
|
|
|
IF ST_GeometryType(geom) = 'ST_Point' THEN
|
|
geom_type := 'point';
|
|
ELSIF ST_GeometryType(geom) IN ('ST_Polygon', 'ST_MultiPolygon') THEN
|
|
geom_type := 'polygon';
|
|
--geom := ST_Buffer(geom, 0.000001);
|
|
geom := ST_CollectionExtract(ST_MakeValid(geom), 3);
|
|
ELSE
|
|
RAISE EXCEPTION 'Invalid geometry type (%), can only handle ''ST_Point'', ''ST_Polygon'', and ''ST_MultiPolygon''',
|
|
ST_GeometryType(geom);
|
|
END IF;
|
|
|
|
IF normalize ILIKE 'area%' THEN --AND numer_aggregate ILIKE 'sum' THEN
|
|
map_type := 'areaNormalized';
|
|
ELSIF normalize ILIKE 'denom%' THEN
|
|
map_type := 'denominated';
|
|
ELSE
|
|
-- defaults: area normalization for point if it's possible and none for
|
|
-- polygon or non-summable point
|
|
IF geom_type = 'point' THEN --AND numer_aggregate ILIKE 'sum' THEN
|
|
map_type := 'areaNormalized';
|
|
ELSE
|
|
map_type := 'predenominated';
|
|
END IF;
|
|
END IF;
|
|
params := (SELECT cdb_observatory.OBS_GetMeasureMetaMulti(
|
|
geom, JSON_Build_Array(JSON_Build_Object('numer_id', measure_id,
|
|
'geom_id', boundary_id,
|
|
'timespan', time_span
|
|
)), 1, 1, 500));
|
|
|
|
params := JSON_Build_Array(JSONB_Set((params::JSONB)->0, '{normalization}', to_jsonb(map_type))::JSON);
|
|
|
|
IF params->0->>'geom_id' IS NULL THEN
|
|
RAISE NOTICE 'No boundary found for geom';
|
|
RETURN NULL;
|
|
ELSE
|
|
RAISE NOTICE 'Using boundary %', params->0->>'geom_id';
|
|
END IF;
|
|
|
|
SELECT measure FROM
|
|
cdb_observatory.OBS_GetMeasureDataMulti(ARRAY[(geom, 1)::geomval], params)
|
|
AS (id INT, measure NUMERIC)
|
|
INTO result;
|
|
|
|
RETURN result;
|
|
|
|
END;
|
|
$$ LANGUAGE plpgsql IMMUTABLE;
|
|
|
|
CREATE OR REPLACE FUNCTION cdb_observatory.OBS_GetMeasureById(
|
|
geom_ref TEXT,
|
|
measure_id TEXT,
|
|
boundary_id TEXT,
|
|
time_span TEXT DEFAULT NULL
|
|
)
|
|
RETURNS NUMERIC
|
|
AS $$
|
|
DECLARE
|
|
target_table TEXT;
|
|
colname TEXT;
|
|
measure_val NUMERIC;
|
|
data_geoid_colname TEXT;
|
|
BEGIN
|
|
IF geom_ref IS NULL THEN
|
|
RETURN NULL;
|
|
END IF;
|
|
|
|
EXECUTE
|
|
$query$
|
|
SELECT numer_colname, numer_geomref_colname, numer_tablename
|
|
FROM observatory.obs_meta
|
|
WHERE (geom_id = $1 OR ($1 = ''))
|
|
AND numer_id = $2
|
|
AND (numer_timespan = $3 OR ($3 = ''))
|
|
ORDER BY geom_weight DESC, numer_timespan DESC
|
|
LIMIT 1
|
|
$query$
|
|
INTO colname, data_geoid_colname, target_table
|
|
USING COALESCE(boundary_id, ''), measure_id, COALESCE(time_span, '');
|
|
|
|
--RAISE DEBUG 'target_table %, colname %', target_table, colname;
|
|
|
|
EXECUTE format(
|
|
'SELECT %I
|
|
FROM observatory.%I data
|
|
WHERE data.%I = $1',
|
|
colname,
|
|
target_table,
|
|
data_geoid_colname)
|
|
INTO measure_val USING geom_ref;
|
|
|
|
RETURN measure_val;
|
|
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION cdb_observatory.OBS_GetMeasureDataMulti(
|
|
geomvals geomval[],
|
|
params JSON
|
|
)
|
|
RETURNS SETOF RECORD
|
|
AS $$
|
|
DECLARE
|
|
colspecs TEXT;
|
|
tables TEXT;
|
|
obs_wheres TEXT;
|
|
user_wheres TEXT;
|
|
|
|
measure_id text;
|
|
measures_list text;
|
|
measures_query text;
|
|
geom_table_name text;
|
|
data_table_name text;
|
|
BEGIN
|
|
EXECUTE
|
|
$query$
|
|
WITH _meta AS (SELECT
|
|
generate_series(1, array_length($1, 1)) colid,
|
|
(unnest($1))->>'numer_id' numer_id,
|
|
(unnest($1))->>'numer_aggregate' numer_aggregate,
|
|
(unnest($1))->>'numer_colname' numer_colname,
|
|
(unnest($1))->>'numer_geomref_colname' numer_geomref_colname,
|
|
(unnest($1))->>'numer_tablename' numer_tablename,
|
|
(unnest($1))->>'numer_type' numer_type,
|
|
(unnest($1))->>'denom_id' denom_id,
|
|
(unnest($1))->>'denom_aggregate' denom_aggregate,
|
|
(unnest($1))->>'denom_colname' denom_colname,
|
|
(unnest($1))->>'denom_geomref_colname' denom_geomref_colname,
|
|
(unnest($1))->>'denom_tablename' denom_tablename,
|
|
(unnest($1))->>'denom_type' denom_type,
|
|
(unnest($1))->>'geom_id' geom_id,
|
|
(unnest($1))->>'geom_colname' geom_colname,
|
|
(unnest($1))->>'geom_geomref_colname' geom_geomref_colname,
|
|
(unnest($1))->>'geom_tablename' geom_tablename,
|
|
(unnest($1))->>'geom_type' geom_type,
|
|
(unnest($1))->>'timespan' timespan,
|
|
(unnest($1))->>'normalization' normalization
|
|
)
|
|
SELECT String_Agg(
|
|
CASE WHEN LOWER(numer_type) LIKE 'numeric' THEN
|
|
CASE
|
|
-- denominated
|
|
WHEN LOWER(normalization) LIKE 'denom%' OR (normalization IS NULL AND denom_id IS NOT NULL)
|
|
THEN ' CASE ' ||
|
|
-- denominated point-in-poly or user polygon is same as OBS polygon
|
|
' WHEN ST_GeometryType(cdb_observatory.FIRST(_geoms.geom)) = ''ST_Point'' ' ||
|
|
' OR cdb_observatory.FIRST(_geoms.geom = ' || geom_tablename || '.' || geom_colname || ')' ||
|
|
' THEN cdb_observatory.FIRST(' || numer_tablename || '.' || numer_colname ||
|
|
' / NullIf(' || denom_tablename || '.' || denom_colname || ', 0))' ||
|
|
-- denominated polygon interpolation
|
|
-- SUM (numer * (% OBS geom in user geom)) / SUM (denom * (% OBS geom in user geom))
|
|
' ELSE ' ||
|
|
' SUM(' || numer_tablename || '.' || numer_colname || ' ' ||
|
|
' * CASE WHEN ST_Within(_geoms.geom, ' || geom_tablename || '.' || geom_colname || ') ' ||
|
|
' THEN ST_Area(_geoms.geom) / ST_Area(' || geom_tablename || '.' || geom_colname || ') ' ||
|
|
' WHEN ST_Within(' || geom_tablename || '.' || geom_colname || ', _geoms.geom) ' ||
|
|
' THEN 1 ' ||
|
|
' ELSE (ST_Area(ST_Intersection(_geoms.geom, ' || geom_tablename || '.' || geom_colname || ')) ' ||
|
|
' / ST_Area(' || geom_tablename || '.' || geom_colname || '))' ||
|
|
' END) / '
|
|
' NULLIF(SUM(' || denom_tablename || '.' || denom_colname || ' ' ||
|
|
' * CASE WHEN ST_Within(_geoms.geom, ' || geom_tablename || '.' || geom_colname || ') ' ||
|
|
' THEN ST_Area(_geoms.geom) / ST_Area(' || geom_tablename || '.' || geom_colname || ') ' ||
|
|
' WHEN ST_Within(' || geom_tablename || '.' || geom_colname || ', _geoms.geom) ' ||
|
|
' THEN 1 ' ||
|
|
' ELSE (ST_Area(ST_Intersection(_geoms.geom, ' || geom_tablename || '.' || geom_colname || ')) ' ||
|
|
' / ST_Area(' || geom_tablename || '.' || geom_colname || '))' ||
|
|
' END), 0) '
|
|
' END '
|
|
-- areaNormalized
|
|
WHEN LOWER(normalization) LIKE 'area%' OR (normalization IS NULL AND numer_aggregate ILIKE 'sum')
|
|
THEN ' CASE ' ||
|
|
-- areaNormalized point-in-poly or user polygon is the same as OBS polygon
|
|
' WHEN ST_GeometryType(cdb_observatory.FIRST(_geoms.geom)) = ''ST_Point'' ' ||
|
|
' OR cdb_observatory.FIRST(_geoms.geom = ' || geom_tablename || '.' || geom_colname || ')' ||
|
|
' THEN cdb_observatory.FIRST(' || numer_tablename || '.' || numer_colname ||
|
|
' / (ST_Area(' || geom_tablename || '.' || geom_colname || '::Geography)/1000000)) ' ||
|
|
-- areaNormalized polygon interpolation
|
|
-- SUM (numer * (% OBS geom in user geom)) / area of big geom
|
|
' ELSE ' ||
|
|
--' NULL END '
|
|
' SUM((' || numer_tablename || '.' || numer_colname || ') ' ||
|
|
' * CASE WHEN ST_Within(_geoms.geom, ' || geom_tablename || '.' || geom_colname || ') THEN 1 ' ||
|
|
' WHEN ST_Within(' || geom_tablename || '.' || geom_colname || ', _geoms.geom) THEN ' ||
|
|
' ST_Area(' || geom_tablename || '.' || geom_colname || ') ' ||
|
|
' / ST_Area(_geoms.geom)' ||
|
|
' ELSE (ST_Area(ST_Intersection(_geoms.geom, ' || geom_tablename || '.' || geom_colname || ')) ' ||
|
|
' / ST_Area(_geoms.geom))' ||
|
|
' END / (ST_Area(' || geom_tablename || '.' || geom_colname || '::Geography) / 1000000)) END '
|
|
-- prenormalized
|
|
ELSE ' CASE ' ||
|
|
-- predenominated point-in-poly or user polygon is the same as OBS- polygon
|
|
' WHEN ST_GeometryType(cdb_observatory.FIRST(_geoms.geom)) = ''ST_Point'' ' ||
|
|
' OR cdb_observatory.FIRST(_geoms.geom = ' || geom_tablename || '.' || geom_colname || ')' ||
|
|
' THEN cdb_observatory.FIRST(' || numer_tablename || '.' || numer_colname || ') ' ||
|
|
' ELSE ' ||
|
|
-- predenominated polygon interpolation
|
|
-- TODO should weight by universe instead of area
|
|
-- SUM (numer * (% user geom in OBS geom))
|
|
' SUM(' || numer_tablename || '.' || numer_colname || ' ' ||
|
|
' * CASE WHEN ST_Within(_geoms.geom, ' || geom_tablename || '.' || geom_colname || ') ' ||
|
|
' THEN ST_Area(_geoms.geom) / ST_Area(' || geom_tablename || '.' || geom_colname || ') ' ||
|
|
' WHEN ST_Within(' || geom_tablename || '.' || geom_colname || ', _geoms.geom) ' ||
|
|
' THEN 1 ' ||
|
|
' ELSE (ST_Area(ST_Intersection(_geoms.geom, ' || geom_tablename || '.' || geom_colname || ')) ' ||
|
|
' / ST_Area(' || geom_tablename || '.' || geom_colname || '))' ||
|
|
' END) END '
|
|
END
|
|
WHEN LOWER(numer_type) LIKE 'text' THEN
|
|
'FIRST(' || numer_tablename || '.' || numer_colname || ')'
|
|
ELSE ''
|
|
END || ':: ' || numer_type || ' AS ' || numer_colname, ', ')
|
|
AS colspecs,
|
|
|
|
(SELECT String_Agg(tablename, ', ') FROM (SELECT JSONB_Object_Keys(JSONB_Object(
|
|
Array_Cat(Array_Agg('observatory.' || numer_tablename),
|
|
Array_Cat(Array_Agg('observatory.' || geom_tablename),
|
|
Array_Agg('observatory.' || denom_tablename) FILTER (WHERE denom_tablename IS NOT NULL))),
|
|
Array_Cat(Array_Agg(numer_tablename),
|
|
Array_Cat(Array_Agg(geom_tablename),
|
|
Array_Agg(denom_tablename) FILTER (WHERE denom_tablename IS NOT NULL)))
|
|
)) tablename) bar) tablenames,
|
|
String_Agg(numer_tablename || '.' || numer_geomref_colname || ' = ' ||
|
|
geom_tablename || '.' || geom_geomref_colname ||
|
|
Coalesce(' AND ' || numer_tablename || '.' || numer_geomref_colname || ' = ' ||
|
|
denom_tablename || '.' || denom_geomref_colname, ''),
|
|
' AND ') AS obs_wheres,
|
|
String_Agg('ST_Intersects(' || geom_tablename || '.' || geom_colname
|
|
|| ', _geoms.geom)', ' AND ')
|
|
AS user_wheres
|
|
FROM _meta
|
|
;
|
|
$query$
|
|
INTO colspecs, tables, obs_wheres, user_wheres
|
|
USING (SELECT ARRAY(SELECT json_array_elements_text(params))::json[]);
|
|
|
|
RETURN QUERY EXECUTE format($query$
|
|
WITH _geoms AS (SELECT
|
|
(UNNEST($1)).val as id,
|
|
(UNNEST($1)).geom AS geom)
|
|
SELECT _geoms.id::INT, %s
|
|
FROM %s, _geoms
|
|
WHERE %s
|
|
AND %s
|
|
GROUP BY _geoms.id
|
|
ORDER BY _geoms.id
|
|
$query$, colspecs, tables, obs_wheres, user_wheres)
|
|
USING geomvals;
|
|
RETURN;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION cdb_observatory.OBS_GetCategory(
|
|
geom geometry(Geometry, 4326),
|
|
category_id TEXT,
|
|
boundary_id TEXT DEFAULT NULL,
|
|
time_span TEXT DEFAULT NULL
|
|
)
|
|
RETURNS TEXT
|
|
AS $$
|
|
DECLARE
|
|
data_table TEXT;
|
|
geom_table TEXT;
|
|
colname TEXT;
|
|
data_geomref_colname TEXT;
|
|
geom_geomref_colname TEXT;
|
|
geom_colname TEXT;
|
|
category_val TEXT;
|
|
category_share NUMERIC;
|
|
BEGIN
|
|
IF geom IS NULL THEN
|
|
RETURN NULL;
|
|
END IF;
|
|
|
|
EXECUTE
|
|
$query$
|
|
SELECT numer_colname, numer_geomref_colname, numer_tablename,
|
|
geom_geomref_colname, geom_colname, geom_tablename
|
|
FROM observatory.obs_meta
|
|
WHERE (geom_id = $1 OR ($1 = ''))
|
|
AND numer_id = $2
|
|
AND (numer_timespan = $3 OR ($3 = ''))
|
|
ORDER BY geom_weight DESC, numer_timespan DESC
|
|
LIMIT 1
|
|
$query$
|
|
INTO colname, data_geomref_colname, data_table,
|
|
geom_geomref_colname, geom_colname, geom_table
|
|
USING COALESCE(boundary_id, ''), category_id, COALESCE(time_span, '');
|
|
|
|
IF ST_GeometryType(geom) = 'ST_Point' THEN
|
|
EXECUTE format(
|
|
'SELECT data.%I
|
|
FROM observatory.%I data, observatory.%I geom
|
|
WHERE data.%I = geom.%I
|
|
AND ST_WITHIN($1, geom.%I) ',
|
|
colname, data_table, geom_table, data_geomref_colname,
|
|
geom_geomref_colname, geom_colname)
|
|
INTO category_val USING geom;
|
|
ELSE
|
|
-- favor the category with the most area
|
|
EXECUTE format(
|
|
'SELECT data.%I category, SUM(overlap_fraction) category_share
|
|
FROM observatory.%I data, (
|
|
SELECT ST_Area(
|
|
ST_Intersection($1, a.%I)
|
|
) / ST_Area($1) AS overlap_fraction, a.%I geomref
|
|
FROM observatory.%I as a
|
|
WHERE $1 && a.%I) _overlaps
|
|
WHERE data.%I = _overlaps.geomref
|
|
GROUP BY category
|
|
ORDER BY SUM(overlap_fraction) DESC
|
|
LIMIT 1',
|
|
colname, data_table, geom_colname, geom_geomref_colname,
|
|
geom_table, geom_colname, data_geomref_colname)
|
|
INTO category_val, category_share USING geom;
|
|
END IF;
|
|
|
|
RETURN category_val;
|
|
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE OR REPLACE FUNCTION cdb_observatory.OBS_GetUSCensusMeasure(
|
|
geom geometry(Geometry, 4326),
|
|
name TEXT,
|
|
normalize TEXT DEFAULT NULL,
|
|
boundary_id TEXT DEFAULT NULL,
|
|
time_span TEXT DEFAULT NULL
|
|
)
|
|
RETURNS NUMERIC AS $$
|
|
DECLARE
|
|
standardized_name text;
|
|
measure_id text;
|
|
result NUMERIC;
|
|
BEGIN
|
|
standardized_name = cdb_observatory._OBS_StandardizeMeasureName(name);
|
|
|
|
EXECUTE $string$
|
|
SELECT c.id
|
|
FROM observatory.obs_column c
|
|
JOIN observatory.obs_column_tag ct
|
|
ON c.id = ct.column_id
|
|
WHERE cdb_observatory._OBS_StandardizeMeasureName(c.name) = $1
|
|
AND ct.tag_id ILIKE 'us.census%'
|
|
$string$
|
|
INTO measure_id
|
|
USING standardized_name;
|
|
|
|
EXECUTE 'SELECT cdb_observatory.OBS_GetMeasure($1, $2, $3, $4, $5)'
|
|
INTO result
|
|
USING geom, measure_id, normalize, boundary_id, time_span;
|
|
RETURN result;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE OR REPLACE FUNCTION cdb_observatory.OBS_GetUSCensusCategory(
|
|
geom geometry(Geometry, 4326),
|
|
name TEXT,
|
|
boundary_id TEXT DEFAULT NULL,
|
|
time_span TEXT DEFAULT NULL
|
|
)
|
|
RETURNS TEXT AS $$
|
|
DECLARE
|
|
standardized_name text;
|
|
category_id text;
|
|
result TEXT;
|
|
BEGIN
|
|
standardized_name = cdb_observatory._OBS_StandardizeMeasureName(name);
|
|
|
|
EXECUTE $string$
|
|
SELECT c.id
|
|
FROM observatory.obs_column c
|
|
--JOIN observatory.obs_column_tag ct
|
|
-- ON c.id = ct.column_id
|
|
WHERE cdb_observatory._OBS_StandardizeMeasureName(c.name) = $1
|
|
AND c.type ILIKE 'TEXT'
|
|
AND c.id ILIKE 'us.census%' -- TODO this should be done by tag
|
|
--AND ct.tag_id = 'us.census.acs.demographics'
|
|
$string$
|
|
INTO category_id
|
|
USING standardized_name;
|
|
|
|
EXECUTE 'SELECT cdb_observatory.OBS_GetCategory($1, $2, $3, $4)'
|
|
INTO result
|
|
USING geom, category_id, boundary_id, time_span;
|
|
RETURN result;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE OR REPLACE FUNCTION cdb_observatory.OBS_GetPopulation(
|
|
geom geometry(Geometry, 4326),
|
|
normalize TEXT DEFAULT NULL,
|
|
boundary_id TEXT DEFAULT NULL,
|
|
time_span TEXT DEFAULT NULL
|
|
)
|
|
RETURNS NUMERIC
|
|
AS $$
|
|
DECLARE
|
|
population_measure_id TEXT;
|
|
result NUMERIC;
|
|
BEGIN
|
|
-- TODO use a super-column for global pop
|
|
population_measure_id := 'us.census.acs.B01003001';
|
|
|
|
EXECUTE 'SELECT cdb_observatory.OBS_GetMeasure(
|
|
$1, $2, $3, $4, $5
|
|
) LIMIT 1'
|
|
INTO result
|
|
USING geom, population_measure_id, normalize, boundary_id, time_span;
|
|
|
|
return result;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION cdb_observatory._OBS_GetPolygons(
|
|
geom geometry(Geometry, 4326),
|
|
geom_table_name text,
|
|
data_table_info json[]
|
|
)
|
|
RETURNS json[]
|
|
AS $$
|
|
DECLARE
|
|
result numeric[];
|
|
json_result json[];
|
|
q_select text;
|
|
q_sum text;
|
|
q text;
|
|
i NUMERIC;
|
|
data_geoid_colname text;
|
|
geom_geoid_colname text;
|
|
BEGIN
|
|
|
|
-- TODO we're assuming our geom_table has only one geom_ref column
|
|
-- we *really* should pass in both geom_table_name and boundary_id
|
|
-- TODO tablename should not be passed here (use boundary_id)
|
|
EXECUTE
|
|
'SELECT ct.colname
|
|
FROM observatory.obs_column_to_column c2c,
|
|
observatory.obs_column_table ct,
|
|
observatory.obs_table t
|
|
WHERE c2c.reltype = ''geom_ref''
|
|
AND ct.column_id = c2c.source_id
|
|
AND ct.table_id = t.id
|
|
AND t.tablename = $1'
|
|
INTO data_geoid_colname USING (data_table_info)[1]->>'tablename';
|
|
EXECUTE
|
|
'SELECT ct.colname
|
|
FROM observatory.obs_column_to_column c2c,
|
|
observatory.obs_column_table ct,
|
|
observatory.obs_table t
|
|
WHERE c2c.reltype = ''geom_ref''
|
|
AND ct.column_id = c2c.source_id
|
|
AND ct.table_id = t.id
|
|
AND t.tablename = $1'
|
|
INTO geom_geoid_colname USING geom_table_name;
|
|
|
|
q_select := format('SELECT %I, ', data_geoid_colname);
|
|
q_sum := 'SELECT Array[';
|
|
|
|
FOR i IN 1..array_upper(data_table_info, 1)
|
|
LOOP
|
|
q_select := q_select || format( '%I ', ((data_table_info)[i])->>'colname');
|
|
|
|
IF ((data_table_info)[i])->>'aggregate' ='sum'
|
|
THEN
|
|
q_sum := q_sum || format('sum(overlap_fraction * COALESCE(%I, 0)) ',((data_table_info)[i])->>'colname',((data_table_info)[i])->>'colname');
|
|
ELSE
|
|
q_sum := q_sum || ' NULL::numeric ';
|
|
END IF;
|
|
|
|
IF i < array_upper(data_table_info,1)
|
|
THEN
|
|
q_select := q_select || format(',');
|
|
q_sum := q_sum || format(',');
|
|
END IF;
|
|
END LOOP;
|
|
|
|
q := format('
|
|
WITH _overlaps As (
|
|
SELECT ST_Area(
|
|
ST_Intersection($1, a.the_geom)
|
|
) / ST_Area(a.the_geom) As overlap_fraction,
|
|
%I
|
|
FROM observatory.%I As a
|
|
WHERE $1 && a.the_geom
|
|
),
|
|
values As (
|
|
', geom_geoid_colname, geom_table_name);
|
|
|
|
q := q || q_select || format('FROM observatory.%I ', ((data_table_info)[1]->>'tablename'));
|
|
|
|
q := format(q || ' ) ' || q_sum || ' ]::numeric[] FROM _overlaps, values
|
|
WHERE values.%I = _overlaps.%I', data_geoid_colname, geom_geoid_colname);
|
|
|
|
EXECUTE
|
|
q
|
|
INTO result
|
|
USING geom;
|
|
|
|
EXECUTE
|
|
$query$
|
|
SELECT array_agg(row_to_json(t)) FROM (
|
|
SELECT values As value,
|
|
meta->>'name' As name,
|
|
meta->>'tablename' As tablename,
|
|
meta->>'aggregate' As aggregate,
|
|
meta->>'type' As type,
|
|
meta->>'description' As description
|
|
FROM (SELECT unnest($1) As values, unnest($2) As meta) b
|
|
) t
|
|
$query$
|
|
INTO json_result
|
|
USING result, data_table_info;
|
|
|
|
RETURN json_result;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION cdb_observatory.OBS_GetSegmentSnapshot(
|
|
geom geometry(Geometry, 4326),
|
|
boundary_id text DEFAULT NULL
|
|
)
|
|
RETURNS JSON
|
|
AS $$
|
|
DECLARE
|
|
target_cols text[];
|
|
result json;
|
|
seg_name Text;
|
|
geom_id Text;
|
|
q Text;
|
|
segment_names Text[];
|
|
BEGIN
|
|
IF boundary_id IS NULL THEN
|
|
boundary_id = 'us.census.tiger.census_tract';
|
|
END IF;
|
|
target_cols := Array[
|
|
'us.census.acs.B01003001_quantile',
|
|
'us.census.acs.B01001002_quantile',
|
|
'us.census.acs.B01001026_quantile',
|
|
'us.census.acs.B01002001_quantile',
|
|
'us.census.acs.B03002003_quantile',
|
|
'us.census.acs.B03002004_quantile',
|
|
'us.census.acs.B03002006_quantile',
|
|
'us.census.acs.B03002012_quantile',
|
|
'us.census.acs.B05001006_quantile',--
|
|
'us.census.acs.B08006001_quantile',--
|
|
'us.census.acs.B08006002_quantile',--
|
|
'us.census.acs.B08006008_quantile',--
|
|
'us.census.acs.B08006009_quantile',--
|
|
'us.census.acs.B08006011_quantile',--
|
|
'us.census.acs.B08006015_quantile',--
|
|
'us.census.acs.B08006017_quantile',--
|
|
'us.census.acs.B09001001_quantile',--
|
|
'us.census.acs.B11001001_quantile',
|
|
'us.census.acs.B14001001_quantile',--
|
|
'us.census.acs.B14001002_quantile',--
|
|
'us.census.acs.B14001005_quantile',--
|
|
'us.census.acs.B14001006_quantile',--
|
|
'us.census.acs.B14001007_quantile',--
|
|
'us.census.acs.B14001008_quantile',--
|
|
'us.census.acs.B15003001_quantile',
|
|
'us.census.acs.B15003017_quantile',
|
|
'us.census.acs.B15003022_quantile',
|
|
'us.census.acs.B15003023_quantile',
|
|
'us.census.acs.B16001001_quantile',--
|
|
'us.census.acs.B16001002_quantile',--
|
|
'us.census.acs.B16001003_quantile',--
|
|
'us.census.acs.B17001001_quantile',--
|
|
'us.census.acs.B17001002_quantile',--
|
|
'us.census.acs.B19013001_quantile',
|
|
'us.census.acs.B19083001_quantile',
|
|
'us.census.acs.B19301001_quantile',
|
|
'us.census.acs.B25001001_quantile',
|
|
'us.census.acs.B25002003_quantile',
|
|
'us.census.acs.B25004002_quantile',
|
|
'us.census.acs.B25004004_quantile',
|
|
'us.census.acs.B25058001_quantile',
|
|
'us.census.acs.B25071001_quantile',
|
|
'us.census.acs.B25075001_quantile',
|
|
'us.census.acs.B25075025_quantile'
|
|
];
|
|
|
|
EXECUTE
|
|
$query$
|
|
SELECT array_agg(_OBS_GetCategories->>'category')
|
|
FROM cdb_observatory._OBS_GetCategories(
|
|
$1,
|
|
Array['us.census.spielman_singleton_segments.X10', 'us.census.spielman_singleton_segments.X55'],
|
|
$2)
|
|
$query$
|
|
INTO segment_names
|
|
USING geom, boundary_id;
|
|
|
|
q :=
|
|
format($query$
|
|
WITH a As (
|
|
SELECT
|
|
array_agg(_OBS_GET->>'name') As names,
|
|
array_agg(_OBS_GET->>'value') As vals
|
|
FROM cdb_observatory._OBS_Get($1,
|
|
$2,
|
|
'2010 - 2014',
|
|
$3)
|
|
|
|
), percentiles As (
|
|
%s
|
|
FROM a)
|
|
SELECT row_to_json(r) FROM
|
|
( SELECT $4 as x10_segment, $5 as x55_segment, percentiles.*
|
|
FROM percentiles) r
|
|
$query$, cdb_observatory._OBS_BuildSnapshotQuery(target_cols)) results;
|
|
|
|
|
|
EXECUTE
|
|
q
|
|
into result
|
|
USING geom, target_cols, boundary_id, segment_names[1], segment_names[2];
|
|
|
|
return result;
|
|
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
--Get categorical variables from point
|
|
|
|
CREATE OR REPLACE FUNCTION cdb_observatory._OBS_GetCategories(
|
|
geom geometry(Geometry, 4326),
|
|
dimension_names text[],
|
|
boundary_id text DEFAULT NULL,
|
|
time_span text DEFAULT NULL
|
|
)
|
|
RETURNS SETOF JSON as $$
|
|
DECLARE
|
|
geom_table_name text;
|
|
geoid text;
|
|
names text[];
|
|
results text[];
|
|
query text;
|
|
data_table_info json[];
|
|
BEGIN
|
|
|
|
IF time_span IS NULL THEN
|
|
time_span = '2010 - 2014';
|
|
END IF;
|
|
|
|
IF boundary_id IS NULL THEN
|
|
boundary_id = 'us.census.tiger.block_group';
|
|
END IF;
|
|
|
|
geom_table_name := cdb_observatory._OBS_GeomTable(geom, boundary_id);
|
|
|
|
IF geom_table_name IS NULL
|
|
THEN
|
|
--raise notice 'Point % is outside of the data region', ST_AsText(geom);
|
|
RETURN QUERY SELECT '{}'::text[], '{}'::text[];
|
|
RETURN;
|
|
END IF;
|
|
|
|
EXECUTE '
|
|
SELECT array_agg(_obs_getcolumndata)
|
|
FROM cdb_observatory._OBS_GetColumnData($1, $2, $3);
|
|
'
|
|
INTO data_table_info
|
|
USING boundary_id, dimension_names, time_span;
|
|
|
|
IF data_table_info IS NULL
|
|
THEN
|
|
--raise notice 'No data table found for this location';
|
|
RETURN QUERY SELECT NULL::json;
|
|
RETURN;
|
|
END IF;
|
|
|
|
EXECUTE
|
|
format('SELECT geoid
|
|
FROM observatory.%I
|
|
WHERE the_geom && $1',
|
|
geom_table_name)
|
|
USING geom
|
|
INTO geoid;
|
|
|
|
IF geoid IS NULL
|
|
THEN
|
|
--raise notice 'No geometry id for this location';
|
|
RETURN QUERY SELECT NULL::json;
|
|
RETURN;
|
|
END IF;
|
|
|
|
query := 'SELECT ARRAY[';
|
|
|
|
FOR i IN 1..array_upper(data_table_info, 1)
|
|
LOOP
|
|
query = query || format('%I ', lower(((data_table_info)[i])->>'colname'));
|
|
IF i < array_upper(data_table_info, 1)
|
|
THEN
|
|
query := query || ',';
|
|
END IF;
|
|
END LOOP;
|
|
|
|
query := query || format(' ]::text[]
|
|
FROM observatory.%I
|
|
WHERE %I.geoid = %L
|
|
',
|
|
((data_table_info)[1])->>'tablename',
|
|
((data_table_info)[1])->>'tablename',
|
|
geoid
|
|
);
|
|
|
|
EXECUTE
|
|
query
|
|
INTO results
|
|
USING geom;
|
|
|
|
RETURN QUERY
|
|
EXECUTE
|
|
$query$
|
|
SELECT row_to_json(t) FROM (
|
|
SELECT categories As category,
|
|
meta->>'name' As name,
|
|
meta->>'tablename' As tablename,
|
|
meta->>'aggregate' As aggregate,
|
|
meta->>'type' As type,
|
|
meta->>'description' As description
|
|
FROM (SELECT unnest($1) As categories,
|
|
unnest($2) As meta) As b
|
|
) t
|
|
$query$
|
|
USING results, data_table_info;
|
|
RETURN;
|
|
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|