|
|
|
|
@@ -1,4 +1,24 @@
|
|
|
|
|
-- security definer
|
|
|
|
|
-- Information about tables in a schema.
|
|
|
|
|
-- If the schema name parameter is NULL, then tables from all schemas
|
|
|
|
|
-- that may contain user tables are returned.
|
|
|
|
|
-- For each table, the regclass, schema name and table name are returned.
|
|
|
|
|
-- Scope: private.
|
|
|
|
|
CREATE OR REPLACE FUNCTION _CDB_UserTablesInSchema(schema_name text DEFAULT NULL)
|
|
|
|
|
RETURNS TABLE(table_regclass REGCLASS, schema_name TEXT, table_name TEXT)
|
|
|
|
|
AS $$
|
|
|
|
|
SELECT
|
|
|
|
|
c.oid::regclass AS table_regclass,
|
|
|
|
|
n.nspname::text AS schema_name,
|
|
|
|
|
c.relname::text AS table_relname
|
|
|
|
|
FROM pg_class c
|
|
|
|
|
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
|
|
|
WHERE c.relkind = 'r'
|
|
|
|
|
AND c.relname NOT IN ('cdb_tablemetadata', 'spatial_ref_sys')
|
|
|
|
|
AND CASE WHEN schema_name IS NULL
|
|
|
|
|
THEN n.nspname NOT IN ('pg_catalog', 'information_schema', 'topology', 'cartodb')
|
|
|
|
|
ELSE n.nspname = schema_name
|
|
|
|
|
END;
|
|
|
|
|
$$ LANGUAGE 'sql';
|
|
|
|
|
|
|
|
|
|
-- Pattern that can be used to detect overview tables and Extract
|
|
|
|
|
-- the intended zoom level from the table name.
|
|
|
|
|
@@ -68,6 +88,41 @@ AS $$
|
|
|
|
|
END;
|
|
|
|
|
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
|
|
|
|
|
|
|
|
|
-- Schema and relation names of a table given its reloid
|
|
|
|
|
-- Scope: private.
|
|
|
|
|
-- Parameters
|
|
|
|
|
-- reloid: oid of the table.
|
|
|
|
|
-- Return (schema_name, table_name)
|
|
|
|
|
-- note that returned names will be quoted if necessary
|
|
|
|
|
CREATE OR REPLACE FUNCTION _cdb_split_table_name(reloid REGCLASS, OUT schema_name TEXT, OUT table_name TEXT)
|
|
|
|
|
AS $$
|
|
|
|
|
BEGIN
|
|
|
|
|
SELECT n.nspname, c.relname
|
|
|
|
|
INTO STRICT schema_name, table_name
|
|
|
|
|
FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid
|
|
|
|
|
WHERE c.oid = reloid;
|
|
|
|
|
END
|
|
|
|
|
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
|
|
|
|
|
|
|
|
|
-- Schema and relation names of a table given its reloid
|
|
|
|
|
-- Scope: private.
|
|
|
|
|
-- Parameters
|
|
|
|
|
-- reloid: oid of the table.
|
|
|
|
|
-- Return (schema_name, table_name)
|
|
|
|
|
-- note that returned names will be quoted if necessary
|
|
|
|
|
CREATE OR REPLACE FUNCTION _cdb_schema_name(reloid REGCLASS)
|
|
|
|
|
RETURNS TEXT
|
|
|
|
|
AS $$
|
|
|
|
|
DECLARE
|
|
|
|
|
schema_name TEXT;
|
|
|
|
|
BEGIN
|
|
|
|
|
SELECT n.nspname
|
|
|
|
|
INTO STRICT schema_name
|
|
|
|
|
FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid
|
|
|
|
|
WHERE c.oid = reloid;
|
|
|
|
|
RETURN schema_name;
|
|
|
|
|
END
|
|
|
|
|
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
|
|
|
|
|
|
|
|
|
-- Remove a dataset's existing overview tables.
|
|
|
|
|
-- Scope: public
|
|
|
|
|
@@ -77,8 +132,11 @@ CREATE OR REPLACE FUNCTION CDB_DropOverviews(reloid REGCLASS)
|
|
|
|
|
RETURNS void
|
|
|
|
|
AS $$
|
|
|
|
|
DECLARE
|
|
|
|
|
row record;
|
|
|
|
|
row record;
|
|
|
|
|
schema_name TEXT;
|
|
|
|
|
table_name TEXT;
|
|
|
|
|
BEGIN
|
|
|
|
|
SELECT * FROM _cdb_split_table_name(reloid) INTO schema_name, table_name;
|
|
|
|
|
FOR row IN
|
|
|
|
|
SELECT * FROM CDB_Overviews(reloid)
|
|
|
|
|
LOOP
|
|
|
|
|
@@ -100,18 +158,20 @@ $$ LANGUAGE PLPGSQL VOLATILE;
|
|
|
|
|
CREATE OR REPLACE FUNCTION CDB_Overviews(reloid REGCLASS)
|
|
|
|
|
RETURNS TABLE(base_table REGCLASS, z integer, overview_table REGCLASS)
|
|
|
|
|
AS $$
|
|
|
|
|
-- FIXME: this will fail if the overview tables
|
|
|
|
|
-- require a explicit schema name
|
|
|
|
|
-- possible solutions: return table names as text instead of regclass
|
|
|
|
|
-- or add schema of reloid before casting to regclass
|
|
|
|
|
SELECT
|
|
|
|
|
reloid AS base_table,
|
|
|
|
|
_CDB_OverviewTableZ(cdb_usertables) AS z,
|
|
|
|
|
cdb_usertables::regclass AS overview_table
|
|
|
|
|
FROM CDB_UserTables()
|
|
|
|
|
WHERE _CDB_IsOverviewTableOf((SELECT relname FROM pg_class WHERE oid=reloid), cdb_usertables)
|
|
|
|
|
ORDER BY z;
|
|
|
|
|
$$ LANGUAGE SQL;
|
|
|
|
|
DECLARE
|
|
|
|
|
schema_name TEXT;
|
|
|
|
|
base_table_name TEXT;
|
|
|
|
|
BEGIN
|
|
|
|
|
SELECT * FROM _cdb_split_table_name(reloid) INTO schema_name, base_table_name;
|
|
|
|
|
RETURN QUERY SELECT
|
|
|
|
|
reloid AS base_table,
|
|
|
|
|
_CDB_OverviewTableZ(table_name) AS z,
|
|
|
|
|
table_regclass AS overview_table
|
|
|
|
|
FROM _CDB_UserTablesInSchema(schema_name)
|
|
|
|
|
WHERE _CDB_IsOverviewTableOf((SELECT relname FROM pg_class WHERE oid=reloid), table_name)
|
|
|
|
|
ORDER BY z;
|
|
|
|
|
END
|
|
|
|
|
$$ LANGUAGE PLPGSQL;
|
|
|
|
|
|
|
|
|
|
-- Return existing overviews (if any) for multiple dataset tables.
|
|
|
|
|
-- Scope: public
|
|
|
|
|
@@ -127,30 +187,16 @@ RETURNS TABLE(base_table REGCLASS, z integer, overview_table REGCLASS)
|
|
|
|
|
AS $$
|
|
|
|
|
SELECT
|
|
|
|
|
base_table::regclass AS base_table,
|
|
|
|
|
_CDB_OverviewTableZ(cdb_usertables) AS z,
|
|
|
|
|
cdb_usertables::regclass AS overview_table
|
|
|
|
|
_CDB_OverviewTableZ(table_name) AS z,
|
|
|
|
|
table_regclass AS overview_table
|
|
|
|
|
FROM
|
|
|
|
|
CDB_UserTables(), unnest(tables) base_table
|
|
|
|
|
WHERE _CDB_IsOverviewTableOf((SELECT relname FROM pg_class WHERE oid=base_table), cdb_usertables)
|
|
|
|
|
_CDB_UserTablesInSchema(), unnest(tables) base_table
|
|
|
|
|
WHERE
|
|
|
|
|
schema_name = _cdb_schema_name(base_table)
|
|
|
|
|
AND _CDB_IsOverviewTableOf((SELECT relname FROM pg_class WHERE oid=base_table), table_name)
|
|
|
|
|
ORDER BY base_table, z;
|
|
|
|
|
$$ LANGUAGE SQL;
|
|
|
|
|
|
|
|
|
|
-- Schema and relation names of a table given its reloid
|
|
|
|
|
-- Scope: private.
|
|
|
|
|
-- Parameters
|
|
|
|
|
-- reloid: oid of the table.
|
|
|
|
|
-- Return (schema_name, table_name)
|
|
|
|
|
-- note that returned names will be quoted if necessary
|
|
|
|
|
CREATE OR REPLACE FUNCTION _cdb_split_table_name(reloid REGCLASS, OUT schema_name TEXT, OUT table_name TEXT)
|
|
|
|
|
AS $$
|
|
|
|
|
BEGIN
|
|
|
|
|
SELECT n.nspname, c.relname
|
|
|
|
|
INTO STRICT schema_name, table_name
|
|
|
|
|
FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid
|
|
|
|
|
WHERE c.oid = reloid;
|
|
|
|
|
END
|
|
|
|
|
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
|
|
|
|
|
|
|
|
|
-- Calculate the estimated extent of a cartodbfy'ed table.
|
|
|
|
|
-- Scope: private.
|
|
|
|
|
-- Parameters
|
|
|
|
|
@@ -175,11 +221,17 @@ AS $$
|
|
|
|
|
|
|
|
|
|
BEGIN
|
|
|
|
|
EXECUTE ext_query INTO ext;
|
|
|
|
|
EXCEPTION
|
|
|
|
|
EXCEPTION
|
|
|
|
|
-- This is the typical ERROR: stats for "mytable" do not exist
|
|
|
|
|
WHEN internal_error THEN
|
|
|
|
|
-- Get stats and execute again
|
|
|
|
|
EXECUTE format('ANALYZE %1$I', reloid);
|
|
|
|
|
EXECUTE format('ANALYZE %1$s', reloid);
|
|
|
|
|
|
|
|
|
|
-- We check the geometry type in case the error is due to empty geometries
|
|
|
|
|
IF _CDB_GeometryTypes(reloid) IS NULL THEN
|
|
|
|
|
RETURN NULL;
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
EXECUTE ext_query INTO ext;
|
|
|
|
|
END;
|
|
|
|
|
|
|
|
|
|
@@ -266,22 +318,28 @@ $$ LANGUAGE PLPGSQL STABLE;
|
|
|
|
|
-- Parameters:
|
|
|
|
|
-- reloid: oid of the input table. It must be a cartodbfy'ed table.
|
|
|
|
|
-- Return value: Z level as an integer
|
|
|
|
|
CREATE OR REPLACE FUNCTION _CDB_Feature_Density_Ref_Z_Strategy(reloid REGCLASS)
|
|
|
|
|
CREATE OR REPLACE FUNCTION _CDB_Feature_Density_Ref_Z_Strategy(reloid REGCLASS, tolerance_px FLOAT8 DEFAULT NULL)
|
|
|
|
|
RETURNS INTEGER
|
|
|
|
|
AS $$
|
|
|
|
|
DECLARE
|
|
|
|
|
lim FLOAT8 := 500; -- TODO: determine/parameterize this
|
|
|
|
|
lim FLOAT8;
|
|
|
|
|
nz integer := 4;
|
|
|
|
|
fd FLOAT8;
|
|
|
|
|
c FLOAT8;
|
|
|
|
|
BEGIN
|
|
|
|
|
IF (tolerance_px IS NULL) OR tolerance_px = 0 THEN
|
|
|
|
|
lim := 500;
|
|
|
|
|
ELSE
|
|
|
|
|
lim := floor(power(256/tolerance_px, 2))/2;
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
-- Compute fd as an estimation of the (maximum) number
|
|
|
|
|
-- of features per unit of tile area (in webmercator squared meters)
|
|
|
|
|
SELECT _CDB_Feature_Density(reloid, nz) INTO fd;
|
|
|
|
|
-- lim maximum number of (desiderable) features per tile
|
|
|
|
|
-- we have c = 2*Pi*R = CDB_XYZ_Resolution(-8) (earth circumference)
|
|
|
|
|
-- ta(z): tile area = power(c*power(2,z), 2) = c*c*power(2,2*z)
|
|
|
|
|
-- => fd*ta(z) if the average number of features per tile at level z
|
|
|
|
|
-- ta(z): tile area = power(c*power(2,-z), 2) = c*c*power(2,-2*z)
|
|
|
|
|
-- => fd*ta(z) is the average number of features per tile at level z
|
|
|
|
|
-- find minimum z so that fd*ta(z) <= lim
|
|
|
|
|
-- compute a rough 'feature density' value
|
|
|
|
|
SELECT CDB_XYZ_Resolution(-8) INTO c;
|
|
|
|
|
@@ -321,7 +379,7 @@ $$ LANGUAGE PLPGSQL IMMUTABLE;
|
|
|
|
|
-- ref_z Z level assigned to the original table
|
|
|
|
|
-- overview_z Z level of the overview to be generated, must be smaller than ref_z
|
|
|
|
|
-- Return value: Name of the generated overview table
|
|
|
|
|
CREATE OR REPLACE FUNCTION _CDB_Sampling_Reduce_Strategy(reloid REGCLASS, ref_z INTEGER, overview_z INTEGER)
|
|
|
|
|
CREATE OR REPLACE FUNCTION _CDB_Sampling_Reduce_Strategy(reloid REGCLASS, ref_z INTEGER, overview_z INTEGER, tolerance_px FLOAT8 DEFAULT NULL)
|
|
|
|
|
RETURNS REGCLASS
|
|
|
|
|
AS $$
|
|
|
|
|
DECLARE
|
|
|
|
|
@@ -330,12 +388,16 @@ AS $$
|
|
|
|
|
base_name TEXT;
|
|
|
|
|
class_info RECORD;
|
|
|
|
|
num_samples INTEGER;
|
|
|
|
|
schema_name TEXT;
|
|
|
|
|
table_name TEXT;
|
|
|
|
|
BEGIN
|
|
|
|
|
overview_rel := _CDB_Overview_Name(reloid, ref_z, overview_z);
|
|
|
|
|
-- TODO: compute fraction from tolerance_px if not NULL
|
|
|
|
|
fraction := power(2, 2*(overview_z - ref_z));
|
|
|
|
|
|
|
|
|
|
-- FIXME: handle schema name for overview_rel if reloid requires it
|
|
|
|
|
EXECUTE Format('DROP TABLE IF EXISTS %I CASCADE;', overview_rel);
|
|
|
|
|
SELECT * FROM _cdb_split_table_name(reloid) INTO schema_name, table_name;
|
|
|
|
|
|
|
|
|
|
EXECUTE Format('DROP TABLE IF EXISTS %I.%I CASCADE;', schema_name, overview_rel);
|
|
|
|
|
|
|
|
|
|
-- Estimate number of rows
|
|
|
|
|
SELECT reltuples, relpages FROM pg_class INTO STRICT class_info
|
|
|
|
|
@@ -349,16 +411,16 @@ AS $$
|
|
|
|
|
ELSE
|
|
|
|
|
num_samples := ceil(class_info.reltuples*fraction);
|
|
|
|
|
EXECUTE Format('
|
|
|
|
|
CREATE TABLE %1$I AS SELECT * FROM %2$s
|
|
|
|
|
CREATE TABLE %4$I.%1$I AS SELECT * FROM %2$s
|
|
|
|
|
WHERE ctid = ANY (
|
|
|
|
|
ARRAY[
|
|
|
|
|
(SELECT CDB_RandomTids(''%2$s'', %3$s))
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
', overview_rel, reloid, num_samples);
|
|
|
|
|
', overview_rel, reloid, num_samples, schema_name);
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
RETURN overview_rel;
|
|
|
|
|
RETURN Format('%I.%I', schema_name, overview_rel)::regclass;
|
|
|
|
|
END;
|
|
|
|
|
$$ LANGUAGE PLPGSQL;
|
|
|
|
|
|
|
|
|
|
@@ -394,9 +456,12 @@ AS $$
|
|
|
|
|
|
|
|
|
|
-- preserve the owner of the base table
|
|
|
|
|
SELECT u.usename
|
|
|
|
|
FROM pg_catalog.pg_class c JOIN pg_catalog.pg_user u ON (c.relowner=u.usesysid)
|
|
|
|
|
WHERE c.relname = dataset::text
|
|
|
|
|
FROM pg_catalog.pg_class c
|
|
|
|
|
JOIN pg_catalog.pg_user u ON (c.relowner=u.usesysid)
|
|
|
|
|
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
|
|
|
WHERE c.relname = dataset_name::text AND n.nspname = dataset_scheme
|
|
|
|
|
INTO table_owner;
|
|
|
|
|
|
|
|
|
|
EXECUTE Format('ALTER TABLE IF EXISTS %s OWNER TO %I;', overview_table::text, table_owner);
|
|
|
|
|
|
|
|
|
|
-- preserve the table privileges
|
|
|
|
|
@@ -450,6 +515,23 @@ BEGIN
|
|
|
|
|
END
|
|
|
|
|
$$ LANGUAGE PLPGSQL STABLE;
|
|
|
|
|
|
|
|
|
|
-- Check if a column of a table is of an unlimited-length text type
|
|
|
|
|
CREATE OR REPLACE FUNCTION _cdb_unlimited_text_column(reloid REGCLASS, col_name TEXT)
|
|
|
|
|
RETURNS BOOLEAN
|
|
|
|
|
AS $$
|
|
|
|
|
SELECT EXISTS (
|
|
|
|
|
SELECT *
|
|
|
|
|
FROM information_schema.columns c, pg_class _tn, pg_namespace _sn
|
|
|
|
|
WHERE table_name = _tn.relname
|
|
|
|
|
AND table_schema = _sn.nspname
|
|
|
|
|
AND c.column_name = col_name
|
|
|
|
|
AND _tn.oid = reloid
|
|
|
|
|
AND _sn.oid = _tn.relnamespace
|
|
|
|
|
AND character_maximum_length IS NULL
|
|
|
|
|
AND c.data_type IN ('text', 'character varying', 'character')
|
|
|
|
|
);
|
|
|
|
|
$$ LANGUAGE SQL STABLE;
|
|
|
|
|
|
|
|
|
|
-- SQL Aggregation expression for a datase attribute
|
|
|
|
|
-- Scope: private.
|
|
|
|
|
-- Parameters
|
|
|
|
|
@@ -464,6 +546,9 @@ AS $$
|
|
|
|
|
DECLARE
|
|
|
|
|
column_type TEXT;
|
|
|
|
|
qualified_column TEXT;
|
|
|
|
|
has_counter_column BOOLEAN;
|
|
|
|
|
feature_count TEXT;
|
|
|
|
|
total_feature_count TEXT;
|
|
|
|
|
BEGIN
|
|
|
|
|
IF table_alias <> '' THEN
|
|
|
|
|
qualified_column := Format('%I.%I', table_alias, column_name);
|
|
|
|
|
@@ -473,19 +558,32 @@ BEGIN
|
|
|
|
|
|
|
|
|
|
column_type := CDB_ColumnType(reloid, column_name);
|
|
|
|
|
|
|
|
|
|
SELECT EXISTS (
|
|
|
|
|
SELECT * FROM CDB_ColumnNames(reloid) as colname WHERE colname = '_feature_count'
|
|
|
|
|
) INTO has_counter_column;
|
|
|
|
|
IF has_counter_column THEN
|
|
|
|
|
feature_count := '_feature_count';
|
|
|
|
|
total_feature_count := 'SUM(_feature_count)';
|
|
|
|
|
ELSE
|
|
|
|
|
feature_count := '1';
|
|
|
|
|
total_feature_count := 'count(*)';
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
CASE column_type
|
|
|
|
|
WHEN 'double precision', 'real', 'integer', 'bigint' THEN
|
|
|
|
|
RETURN Format('AVG(%s)::' || column_type, qualified_column);
|
|
|
|
|
WHEN 'text' THEN
|
|
|
|
|
-- TODO: we could define a new aggregate function that returns distinct
|
|
|
|
|
-- separated values with a limit, adding ellipsis if more values existed
|
|
|
|
|
-- e.g. with '/' as separator and a limit of three:
|
|
|
|
|
-- 'A', 'B', 'A', 'C', 'D' => 'A/B/C/...'
|
|
|
|
|
-- Other ideas: if value is unique then use it, otherwise use something
|
|
|
|
|
-- like '*' or '(varies)' or '(multiple values)', or NULL
|
|
|
|
|
-- Using 'string_agg(' || qualified_column || ',''/'')'
|
|
|
|
|
-- here causes
|
|
|
|
|
RETURN 'CASE count(*) WHEN 1 THEN MIN(' || qualified_column || ') ELSE NULL END::' || column_type;
|
|
|
|
|
WHEN 'double precision', 'real', 'integer', 'bigint', 'numeric' THEN
|
|
|
|
|
IF column_name = '_feature_count' THEN
|
|
|
|
|
RETURN 'SUM(_feature_count)';
|
|
|
|
|
ELSE
|
|
|
|
|
RETURN Format('SUM(%s*%s)/%s::' || column_type, qualified_column, feature_count, total_feature_count);
|
|
|
|
|
END IF;
|
|
|
|
|
WHEN 'text', 'character varying', 'character' THEN
|
|
|
|
|
IF _cdb_unlimited_text_column(reloid, column_name) THEN
|
|
|
|
|
-- TODO: this should not be applied to columns containing largish text;
|
|
|
|
|
-- it is intended only to short names/identifiers
|
|
|
|
|
RETURN 'CASE WHEN count(distinct ' || qualified_column || ') = 1 THEN MIN(' || qualified_column || ') WHEN ' || total_feature_count || ' < 5 THEN string_agg(distinct ' || qualified_column || ','' / '') ELSE ''*'' END::' || column_type;
|
|
|
|
|
ELSE
|
|
|
|
|
RETURN 'CASE count(*) WHEN 1 THEN MIN(' || qualified_column || ') ELSE NULL END::' || column_type;
|
|
|
|
|
END IF;
|
|
|
|
|
WHEN 'boolean' THEN
|
|
|
|
|
RETURN 'CASE count(*) WHEN 1 THEN BOOL_AND(' || qualified_column || ') ELSE NULL END::' || column_type;
|
|
|
|
|
ELSE
|
|
|
|
|
@@ -547,31 +645,39 @@ $$ LANGUAGE PLPGSQL STABLE;
|
|
|
|
|
-- ref_z Z level assigned to the original table
|
|
|
|
|
-- overview_z Z level of the overview to be generated, must be smaller than ref_z
|
|
|
|
|
-- Return value: Name of the generated overview table
|
|
|
|
|
CREATE OR REPLACE FUNCTION _CDB_GridCluster_Reduce_Strategy(reloid REGCLASS, ref_z INTEGER, overview_z INTEGER)
|
|
|
|
|
CREATE OR REPLACE FUNCTION _CDB_GridCluster_Reduce_Strategy(reloid REGCLASS, ref_z INTEGER, overview_z INTEGER, grid_px FLOAT8 DEFAULT NULL)
|
|
|
|
|
RETURNS REGCLASS
|
|
|
|
|
AS $$
|
|
|
|
|
DECLARE
|
|
|
|
|
overview_rel TEXT;
|
|
|
|
|
reduction FLOAT8;
|
|
|
|
|
base_name TEXT;
|
|
|
|
|
grid_px FLOAT8 = 7.5; -- Grid size in pixels at Z level overview_z
|
|
|
|
|
grid_m FLOAT8;
|
|
|
|
|
aggr_attributes TEXT;
|
|
|
|
|
attributes TEXT;
|
|
|
|
|
columns TEXT;
|
|
|
|
|
gtypes TEXT[];
|
|
|
|
|
schema_name TEXT;
|
|
|
|
|
table_name TEXT;
|
|
|
|
|
point_geom TEXT;
|
|
|
|
|
BEGIN
|
|
|
|
|
SELECT _CDB_GeometryTypes(reloid) INTO gtypes;
|
|
|
|
|
IF array_upper(gtypes, 1) <> 1 OR gtypes[1] <> 'ST_Point' THEN
|
|
|
|
|
IF gtypes IS NULL OR array_upper(gtypes, 1) <> 1 OR gtypes[1] <> 'ST_Point' THEN
|
|
|
|
|
-- This strategy only supports datasets with point geomety
|
|
|
|
|
RETURN NULL;
|
|
|
|
|
RETURN 'x';
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
--TODO: check applicability: geometry type, minimum number of points...
|
|
|
|
|
|
|
|
|
|
overview_rel := _CDB_Overview_Name(reloid, ref_z, overview_z);
|
|
|
|
|
|
|
|
|
|
-- Grid size in pixels at Z level overview_z
|
|
|
|
|
IF grid_px IS NULL THEN
|
|
|
|
|
grid_px := 1.0;
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
SELECT * FROM _cdb_split_table_name(reloid) INTO schema_name, table_name;
|
|
|
|
|
|
|
|
|
|
-- compute grid cell size using the overview_z dimension...
|
|
|
|
|
SELECT CDB_XYZ_Resolution(overview_z)*grid_px INTO grid_m;
|
|
|
|
|
|
|
|
|
|
@@ -584,19 +690,17 @@ AS $$
|
|
|
|
|
aggr_attributes := aggr_attributes || ', ';
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
point_geom = Format('ST_SetSRID(ST_MakePoint(gx*%1$s + %2$s, gy*%1$s + %2$s), 3857)', grid_m, grid_m/2);
|
|
|
|
|
|
|
|
|
|
-- compute the resulting columns in the same order as in the base table
|
|
|
|
|
-- cartodb_id,
|
|
|
|
|
-- ST_Transform(ST_SetSRID(ST_MakePoint(sx/n, sy/n), 3857), 4326) AS the_geom,
|
|
|
|
|
-- ST_SetSRID(ST_MakePoint(sx/n, sy/n), 3857) AS the_geom_webmercator
|
|
|
|
|
-- %4$s
|
|
|
|
|
WITH cols AS (
|
|
|
|
|
SELECT
|
|
|
|
|
CASE c
|
|
|
|
|
WHEN 'cartodb_id' THEN 'cartodb_id'
|
|
|
|
|
WHEN 'the_geom' THEN
|
|
|
|
|
'ST_Transform(ST_SetSRID(ST_MakePoint(sx/n, sy/n), 3857), 4326) AS the_geom'
|
|
|
|
|
Format('ST_Transform(%s, 4326) AS the_geom', point_geom)
|
|
|
|
|
WHEN 'the_geom_webmercator' THEN
|
|
|
|
|
'ST_SetSRID(ST_MakePoint(sx/n, sy/n), 3857) AS the_geom_webmercator'
|
|
|
|
|
Format('%s AS the_geom_webmercator', point_geom)
|
|
|
|
|
ELSE c
|
|
|
|
|
END AS column
|
|
|
|
|
FROM CDB_ColumnNames(reloid) c
|
|
|
|
|
@@ -605,21 +709,22 @@ AS $$
|
|
|
|
|
SELECT * FROM cols
|
|
|
|
|
) AS s INTO columns;
|
|
|
|
|
|
|
|
|
|
-- FIXME: handle schema name for overview_rel if reloid requires it
|
|
|
|
|
EXECUTE Format('DROP TABLE IF EXISTS %I CASCADE;', overview_rel);
|
|
|
|
|
IF NOT columns LIKE '%_feature_count%' THEN
|
|
|
|
|
columns := columns || ', n AS _feature_count';
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
EXECUTE Format('DROP TABLE IF EXISTS %I.%I CASCADE;', schema_name, overview_rel);
|
|
|
|
|
|
|
|
|
|
-- Now we cluster the data using a grid of size grid_m
|
|
|
|
|
-- and selecte the centroid (average coordinates) of each cluster.
|
|
|
|
|
-- If we had a selected numeric attribute of interest we could use it
|
|
|
|
|
-- as a weight for the average coordinates.
|
|
|
|
|
EXECUTE Format('
|
|
|
|
|
CREATE TABLE %3$I AS
|
|
|
|
|
CREATE TABLE %7$I.%3$I AS
|
|
|
|
|
WITH clusters AS (
|
|
|
|
|
SELECT
|
|
|
|
|
%5$s
|
|
|
|
|
count(*) AS n,
|
|
|
|
|
SUM(ST_X(f.the_geom_webmercator)) AS sx,
|
|
|
|
|
SUM(ST_Y(f.the_geom_webmercator)) AS sy,
|
|
|
|
|
Floor(ST_X(f.the_geom_webmercator)/%2$s)::int AS gx,
|
|
|
|
|
Floor(ST_Y(f.the_geom_webmercator)/%2$s)::int AS gy,
|
|
|
|
|
MIN(cartodb_id) AS cartodb_id
|
|
|
|
|
@@ -627,9 +732,9 @@ AS $$
|
|
|
|
|
GROUP BY gx, gy
|
|
|
|
|
)
|
|
|
|
|
SELECT %6$s FROM clusters
|
|
|
|
|
', reloid::text, grid_m, overview_rel, attributes, aggr_attributes, columns);
|
|
|
|
|
', reloid::text, grid_m, overview_rel, attributes, aggr_attributes, columns, schema_name);
|
|
|
|
|
|
|
|
|
|
RETURN overview_rel;
|
|
|
|
|
RETURN Format('%I.%I', schema_name, overview_rel)::regclass;
|
|
|
|
|
END;
|
|
|
|
|
$$ LANGUAGE PLPGSQL;
|
|
|
|
|
|
|
|
|
|
@@ -644,7 +749,20 @@ $$ LANGUAGE PLPGSQL;
|
|
|
|
|
-- created by the strategy must have the same columns
|
|
|
|
|
-- as the base table and in the same order.
|
|
|
|
|
-- Return value: Array with the names of the generated overview tables
|
|
|
|
|
CREATE OR REPLACE FUNCTION CDB_CreateOverviews(reloid REGCLASS, refscale_strategy regproc DEFAULT '_CDB_Feature_Density_Ref_Z_Strategy'::regproc, reduce_strategy regproc DEFAULT '_CDB_GridCluster_Reduce_Strategy'::regproc)
|
|
|
|
|
CREATE OR REPLACE FUNCTION CDB_CreateOverviews(reloid REGCLASS, refscale_strategy regproc DEFAULT '_CDB_Feature_Density_Ref_Z_Strategy(REGCLASS,FLOAT8)'::regprocedure, reduce_strategy regproc DEFAULT '_CDB_GridCluster_Reduce_Strategy(REGCLASS,INTEGER,INTEGER,FLOAT8)'::regprocedure)
|
|
|
|
|
RETURNS text[]
|
|
|
|
|
AS $$
|
|
|
|
|
DECLARE
|
|
|
|
|
tolerance_px FLOAT8;
|
|
|
|
|
BEGIN
|
|
|
|
|
-- Use the default tolerance
|
|
|
|
|
tolerance_px := 1.0;
|
|
|
|
|
RETURN CDB_CreateOverviewsWithToleranceInPixels(reloid, tolerance_px, refscale_strategy, reduce_strategy);
|
|
|
|
|
END;
|
|
|
|
|
$$ LANGUAGE PLPGSQL;
|
|
|
|
|
|
|
|
|
|
-- Create overviews with additional parameter to define the desired detail/tolerance in pixels
|
|
|
|
|
CREATE OR REPLACE FUNCTION CDB_CreateOverviewsWithToleranceInPixels(reloid REGCLASS, tolerance_px FLOAT8, refscale_strategy regproc DEFAULT '_CDB_Feature_Density_Ref_Z_Strategy(REGCLASS,FLOAT8)'::regprocedure, reduce_strategy regproc DEFAULT '_CDB_GridCluster_Reduce_Strategy(REGCLASS,INTEGER,INTEGER,FLOAT8)'::regprocedure)
|
|
|
|
|
RETURNS text[]
|
|
|
|
|
AS $$
|
|
|
|
|
DECLARE
|
|
|
|
|
@@ -655,9 +773,14 @@ DECLARE
|
|
|
|
|
overview_z integer;
|
|
|
|
|
overview_tables REGCLASS[];
|
|
|
|
|
overviews_step integer := 1;
|
|
|
|
|
has_counter_column boolean;
|
|
|
|
|
BEGIN
|
|
|
|
|
-- Determine the referece zoom level
|
|
|
|
|
EXECUTE 'SELECT ' || quote_ident(refscale_strategy::text) || Format('(''%s'');', reloid) INTO ref_z;
|
|
|
|
|
EXECUTE 'SELECT ' || quote_ident(refscale_strategy::text) || Format('(''%s'', %s);', reloid, tolerance_px) INTO ref_z;
|
|
|
|
|
|
|
|
|
|
IF ref_z < 0 OR ref_z IS NULL THEN
|
|
|
|
|
RETURN NULL;
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
-- Determine overlay zoom levels
|
|
|
|
|
-- TODO: should be handled by the refscale_strategy?
|
|
|
|
|
@@ -671,7 +794,7 @@ BEGIN
|
|
|
|
|
base_z := ref_z;
|
|
|
|
|
base_rel := reloid;
|
|
|
|
|
FOREACH overview_z IN ARRAY overviews_z LOOP
|
|
|
|
|
EXECUTE 'SELECT ' || quote_ident(reduce_strategy::text) || Format('(''%s'', %s, %s);', base_rel, base_z, overview_z) INTO base_rel;
|
|
|
|
|
EXECUTE 'SELECT ' || quote_ident(reduce_strategy::text) || Format('(''%s'', %s, %s, %s);', base_rel, base_z, overview_z, tolerance_px) INTO base_rel;
|
|
|
|
|
IF base_rel IS NULL THEN
|
|
|
|
|
EXIT;
|
|
|
|
|
END IF;
|
|
|
|
|
@@ -680,6 +803,24 @@ BEGIN
|
|
|
|
|
SELECT array_append(overview_tables, base_rel) INTO overview_tables;
|
|
|
|
|
END LOOP;
|
|
|
|
|
|
|
|
|
|
IF overview_tables IS NOT NULL AND array_length(overview_tables, 1) > 0 THEN
|
|
|
|
|
SELECT EXISTS (
|
|
|
|
|
SELECT * FROM CDB_ColumnNames(reloid) as colname WHERE colname = '_feature_count'
|
|
|
|
|
) INTO has_counter_column;
|
|
|
|
|
IF NOT has_counter_column THEN
|
|
|
|
|
EXECUTE Format('
|
|
|
|
|
ALTER TABLE %s ADD COLUMN _feature_count integer DEFAULT 1;
|
|
|
|
|
', reloid);
|
|
|
|
|
END IF;
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
RETURN overview_tables;
|
|
|
|
|
END;
|
|
|
|
|
$$ LANGUAGE PLPGSQL;
|
|
|
|
|
|
|
|
|
|
-- Here are some older signatures of these functions, no longar in use.
|
|
|
|
|
-- They must be droped here, after the (new) definition of the function `CDB_CreateOverviews`
|
|
|
|
|
-- because that function used to contain references to them in the default argument values.
|
|
|
|
|
DROP FUNCTION IF EXISTS _CDB_Feature_Density_Ref_Z_Strategy(REGCLASS);
|
|
|
|
|
DROP FUNCTION IF EXISTS _CDB_GridCluster_Reduce_Strategy(REGCLASS,INTEGER,INTEGER);
|
|
|
|
|
DROP FUNCTION IF EXISTS _CDB_Sampling_Reduce_Strategy(REGCLASS,INTEGER,INTEGER);
|
|
|
|
|
|