+
+CREATE OR REPLACE FUNCTION quad_split_geometry(geometry GEOMETRY, maxarea FLOAT, maxdepth INTEGER)
+ RETURNS SETOF GEOMETRY
+ AS $$
+DECLARE
+ xmin FLOAT;
+ ymin FLOAT;
+ xmax FLOAT;
+ ymax FLOAT;
+ xmid FLOAT;
+ ymid FLOAT;
+ secgeo GEOMETRY;
+ secbox GEOMETRY;
+ seg INTEGER;
+ geo RECORD;
+ area FLOAT;
+ remainingdepth INTEGER;
+ added INTEGER;
+
+BEGIN
+
+-- RAISE WARNING 'quad_split_geometry: maxarea=%, depth=%',maxarea,maxdepth;
+
+ IF (ST_GeometryType(geometry) not in ('ST_Polygon','ST_MultiPolygon') OR NOT ST_IsValid(geometry)) THEN
+ RETURN NEXT geometry;
+ RETURN;
+ END IF;
+
+ remainingdepth := maxdepth - 1;
+ area := ST_AREA(geometry);
+ IF remainingdepth < 1 OR area < maxarea THEN
+ RETURN NEXT geometry;
+ RETURN;
+ END IF;
+
+ xmin := st_xmin(geometry);
+ xmax := st_xmax(geometry);
+ ymin := st_ymin(geometry);
+ ymax := st_ymax(geometry);
+ secbox := ST_SetSRID(ST_MakeBox2D(ST_Point(ymin,xmin),ST_Point(ymax,xmax)),4326);
+
+ -- if the geometry completely covers the box don't bother to slice any more
+ IF ST_AREA(secbox) = area THEN
+ RETURN NEXT geometry;
+ RETURN;
+ END IF;
+
+ xmid := (xmin+xmax)/2;
+ ymid := (ymin+ymax)/2;
+
+ added := 0;
+ FOR seg IN 1..4 LOOP
+
+ IF seg = 1 THEN
+ secbox := ST_SetSRID(ST_MakeBox2D(ST_Point(xmin,ymin),ST_Point(xmid,ymid)),4326);
+ END IF;
+ IF seg = 2 THEN
+ secbox := ST_SetSRID(ST_MakeBox2D(ST_Point(xmin,ymid),ST_Point(xmid,ymax)),4326);
+ END IF;
+ IF seg = 3 THEN
+ secbox := ST_SetSRID(ST_MakeBox2D(ST_Point(xmid,ymin),ST_Point(xmax,ymid)),4326);
+ END IF;
+ IF seg = 4 THEN
+ secbox := ST_SetSRID(ST_MakeBox2D(ST_Point(xmid,ymid),ST_Point(xmax,ymax)),4326);
+ END IF;
+
+ IF st_intersects(geometry, secbox) THEN
+ secgeo := st_intersection(geometry, secbox);
+ IF NOT ST_IsEmpty(secgeo) AND ST_GeometryType(secgeo) in ('ST_Polygon','ST_MultiPolygon') THEN
+ FOR geo IN select quad_split_geometry(secgeo, maxarea, remainingdepth) as geom LOOP
+ IF NOT ST_IsEmpty(geo.geom) AND ST_GeometryType(geo.geom) in ('ST_Polygon','ST_MultiPolygon') THEN
+ added := added + 1;
+ RETURN NEXT geo.geom;
+ END IF;
+ END LOOP;
+ END IF;
+ END IF;
+ END LOOP;
+
+ RETURN;
+END;
+$$
+LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION split_geometry(geometry GEOMETRY)
+ RETURNS SETOF GEOMETRY
+ AS $$
+DECLARE
+ geo RECORD;
+BEGIN
+ -- 10000000000 is ~~ 1x1 degree
+ FOR geo IN select quad_split_geometry(geometry, 0.25, 20) as geom LOOP
+ RETURN NEXT geo.geom;
+ END LOOP;
+ RETURN;
+END;
+$$
+LANGUAGE plpgsql;
+
+
+CREATE OR REPLACE FUNCTION place_force_delete(placeid BIGINT) RETURNS BOOLEAN
+ AS $$
+DECLARE
+ osmid BIGINT;
+ osmtype character(1);
+ pclass text;
+ ptype text;
+BEGIN
+ SELECT osm_type, osm_id, class, type FROM placex WHERE place_id = placeid INTO osmtype, osmid, pclass, ptype;
+ DELETE FROM import_polygon_delete where osm_type = osmtype and osm_id = osmid and class = pclass and type = ptype;
+ DELETE FROM import_polygon_error where osm_type = osmtype and osm_id = osmid and class = pclass and type = ptype;
+ -- force delete from place/placex by making it a very small geometry
+ UPDATE place set geometry = ST_SetSRID(ST_Point(0,0), 4326) where osm_type = osmtype and osm_id = osmid and class = pclass and type = ptype;
+ DELETE FROM place where osm_type = osmtype and osm_id = osmid and class = pclass and type = ptype;
+
+ RETURN TRUE;
+END;
+$$
+LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION place_force_update(placeid BIGINT) RETURNS BOOLEAN
+ AS $$
+DECLARE
+ placegeom GEOMETRY;
+ geom GEOMETRY;
+ diameter FLOAT;
+ rank INTEGER;
+BEGIN
+ UPDATE placex SET indexed_status = 2 WHERE place_id = placeid;
+ SELECT geometry, rank_search FROM placex WHERE place_id = placeid INTO placegeom, rank;
+ IF placegeom IS NOT NULL AND ST_IsValid(placegeom) THEN
+ IF ST_GeometryType(placegeom) in ('ST_Polygon','ST_MultiPolygon') THEN
+ FOR geom IN select split_geometry(placegeom) FROM placex WHERE place_id = placeid LOOP
+ update placex set indexed_status = 2 where (st_covers(geom, placex.geometry) OR ST_Intersects(geom, placex.geometry))
+ AND rank_search > rank and indexed_status = 0 and ST_geometrytype(placex.geometry) = 'ST_Point' and (rank_search < 28 or name is not null or (rank >= 16 and addr_place is not null));
+ update placex set indexed_status = 2 where (st_covers(geom, placex.geometry) OR ST_Intersects(geom, placex.geometry))
+ AND rank_search > rank and indexed_status = 0 and ST_geometrytype(placex.geometry) != 'ST_Point' and (rank_search < 28 or name is not null or (rank >= 16 and addr_place is not null));
+ END LOOP;
+ ELSE
+ diameter := 0;
+ IF rank = 11 THEN
+ diameter := 0.05;
+ ELSEIF rank < 18 THEN
+ diameter := 0.1;
+ ELSEIF rank < 20 THEN
+ diameter := 0.05;
+ ELSEIF rank = 21 THEN
+ diameter := 0.001;
+ ELSEIF rank < 24 THEN
+ diameter := 0.02;
+ ELSEIF rank < 26 THEN
+ diameter := 0.002; -- 100 to 200 meters
+ ELSEIF rank < 28 THEN
+ diameter := 0.001; -- 50 to 100 meters
+ END IF;
+ IF diameter > 0 THEN
+ IF rank >= 26 THEN
+ -- roads may cause reparenting for >27 rank places
+ update placex set indexed_status = 2 where indexed_status = 0 and rank_search > rank and ST_DWithin(placex.geometry, placegeom, diameter);
+ ELSEIF rank >= 16 THEN
+ -- up to rank 16, street-less addresses may need reparenting
+ update placex set indexed_status = 2 where indexed_status = 0 and rank_search > rank and ST_DWithin(placex.geometry, placegeom, diameter) and (rank_search < 28 or name is not null or addr_place is not null);
+ ELSE
+ -- for all other places the search terms may change as well
+ update placex set indexed_status = 2 where indexed_status = 0 and rank_search > rank and ST_DWithin(placex.geometry, placegeom, diameter) and (rank_search < 28 or name is not null);
+ END IF;
+ END IF;
+ END IF;
+ RETURN TRUE;
+ END IF;
+
+ RETURN FALSE;
+END;
+$$
+LANGUAGE plpgsql;