Skip to content

Instantly share code, notes, and snippets.

@bitner
Created August 25, 2021 17:59
Show Gist options
  • Save bitner/1d3cf94a2865f1792789f1b472686570 to your computer and use it in GitHub Desktop.
Save bitner/1d3cf94a2865f1792789f1b472686570 to your computer and use it in GitHub Desktop.
CREATE OR REPLACE FUNCTION xyzsearch_asmvtrows(
IN _x int,
IN _y int,
IN _z int,
IN queryhash text,
IN fields jsonb DEFAULT NULL,
IN _scanlimit int DEFAULT 10000,
IN _limit int DEFAULT 100,
IN _timelimit interval DEFAULT '5 seconds'::interval,
IN exitwhenfull boolean DEFAULT TRUE, -- Return as soon as the passed in geometry is full covered
IN skipcovered boolean DEFAULT TRUE, -- Skip any items that would show up completely under the previous items
OUT mvtgeom geometry,
OUT id text
) RETURNS setof RECORD AS $$
DECLARE
search searches%ROWTYPE;
curs refcursor;
_where text;
query text;
iter_record items%ROWTYPE;
out_records jsonb[] := '{}'::jsonb[];
exit_flag boolean := FALSE;
counter int := 1;
scancounter int := 1;
remaining_limit int := _scanlimit;
tilearea float;
geom geometry;
unionedgeom geometry;
clippedgeom geometry;
unionedgeom_area float := 0;
prev_area float := 0;
excludes text[];
includes text[];
BEGIN
-- If skipcovered is true then you will always want to exit when the passed in geometry is full
IF skipcovered THEN
exitwhenfull := TRUE;
END IF;
geom := st_transform(tileenvelope(_z, _x, _y), 4326);
SELECT * INTO search FROM searches WHERE hash=queryhash;
IF NOT FOUND THEN
RAISE EXCEPTION 'Search with Query Hash % Not Found', queryhash;
END IF;
tilearea := st_area(geom);
_where := format('%s AND st_intersects(geometry, %L::geometry)', search._where, geom);
FOR query IN SELECT * FROM partition_queries(_where, search.orderby) LOOP
query := format('%s LIMIT %L', query, remaining_limit);
RAISE NOTICE '%', query;
curs = create_cursor(query);
LOOP
FETCH curs INTO iter_record;
EXIT WHEN NOT FOUND;
IF exitwhenfull OR skipcovered THEN -- If we are not using exitwhenfull or skipcovered, we do not need to do expensive geometry operations
clippedgeom := st_intersection(geom, iter_record.geometry);
IF unionedgeom IS NULL THEN
unionedgeom := clippedgeom;
ELSE
unionedgeom := st_union(unionedgeom, clippedgeom);
END IF;
unionedgeom_area := st_area(unionedgeom);
IF skipcovered AND prev_area = unionedgeom_area THEN
scancounter := scancounter + 1;
CONTINUE;
END IF;
prev_area := unionedgeom_area;
RAISE NOTICE '% % % %', unionedgeom_area/tilearea, counter, scancounter, ftime();
END IF;
mvtgeom := ST_ASMVTGeom(
ST_Transform(iter_record.geometry, 3857),
box2d(st_transform(geom, 3857)),
4096,
0,
TRUE
);
id := iter_record.id;
RETURN NEXT;
IF counter >= _limit
OR scancounter > _scanlimit
OR ftime() > _timelimit
OR (exitwhenfull AND unionedgeom_area >= tilearea)
THEN
exit_flag := TRUE;
EXIT;
END IF;
counter := counter + 1;
scancounter := scancounter + 1;
END LOOP;
EXIT WHEN exit_flag;
remaining_limit := _scanlimit - scancounter;
END LOOP;
RETURN;
END;
$$ LANGUAGE PLPGSQL;
CREATE OR REPLACE FUNCTION xyzsearch_asmvt(
IN _x int,
IN _y int,
IN _z int,
IN queryhash text,
IN fields jsonb DEFAULT NULL,
IN _scanlimit int DEFAULT 10000,
IN _limit int DEFAULT 100,
IN _timelimit interval DEFAULT '5 seconds'::interval,
IN exitwhenfull boolean DEFAULT TRUE,
IN skipcovered boolean DEFAULT TRUE
) RETURNS bytea AS $$
SELECT ST_ASMVT(m.*) FROM xyzsearch_asmvtrows(
_x,
_y,
_z,
queryhash,
fields,
_scanlimit,
_limit,
_timelimit,
exitwhenfull,
skipcovered
) m;
$$ LANGUAGE SQL;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment