X-Git-Url: https://git.openstreetmap.org./nominatim.git/blobdiff_plain/98b93df83d35f9062c85a5c93692206dfcbfa92d..6a1e599f31049b940d906fce9505a79672ef527a:/sql/functions.sql diff --git a/sql/functions.sql b/sql/functions.sql index 49a6e972..6a465e0b 100644 --- a/sql/functions.sql +++ b/sql/functions.sql @@ -271,6 +271,20 @@ END; $$ LANGUAGE plpgsql IMMUTABLE; +CREATE OR REPLACE FUNCTION get_name_ids(lookup_word TEXT) + RETURNS INTEGER[] + AS $$ +DECLARE + lookup_token TEXT; + return_word_ids INTEGER[]; +BEGIN + lookup_token := ' '||trim(lookup_word); + SELECT array_agg(word_id) FROM word WHERE word_token = lookup_token and class is null and type is null into return_word_ids; + RETURN return_word_ids; +END; +$$ +LANGUAGE plpgsql IMMUTABLE; + CREATE OR REPLACE FUNCTION array_merge(a INTEGER[], b INTEGER[]) RETURNS INTEGER[] AS $$ @@ -554,20 +568,6 @@ END; $$ LANGUAGE plpgsql IMMUTABLE; -CREATE OR REPLACE FUNCTION get_country_code(place geometry, in_country_code VARCHAR(2)) RETURNS TEXT - AS $$ -DECLARE - nearcountry RECORD; -BEGIN - FOR nearcountry IN select country_code from country_name where country_code = lower(in_country_code) - LOOP - RETURN nearcountry.country_code; - END LOOP; - RETURN get_country_code(place); -END; -$$ -LANGUAGE plpgsql IMMUTABLE; - CREATE OR REPLACE FUNCTION get_country_language_code(search_country_code VARCHAR(2)) RETURNS TEXT AS $$ DECLARE @@ -596,10 +596,9 @@ END; $$ LANGUAGE plpgsql IMMUTABLE; -CREATE OR REPLACE FUNCTION get_partition(place geometry, in_country_code VARCHAR(10)) RETURNS INTEGER +CREATE OR REPLACE FUNCTION get_partition(in_country_code VARCHAR(10)) RETURNS INTEGER AS $$ DECLARE - place_centre GEOMETRY; nearcountry RECORD; BEGIN FOR nearcountry IN select partition from country_name where country_code = in_country_code @@ -965,9 +964,9 @@ BEGIN NEW.place_id := nextval('seq_place'); NEW.indexed_status := 1; --STATUS_NEW - NEW.calculated_country_code := lower(get_country_code(NEW.geometry, NEW.country_code)); + NEW.calculated_country_code := lower(get_country_code(NEW.geometry)); - NEW.partition := get_partition(NEW.geometry, NEW.calculated_country_code); + NEW.partition := get_partition(NEW.calculated_country_code); NEW.geometry_sector := geometry_sector(NEW.partition, NEW.geometry); -- copy 'name' to or from the default language (if there is a default language) @@ -1078,18 +1077,6 @@ BEGIN ELSEIF NEW.type in ('village','hamlet','municipality','district','unincorporated_area','borough') THEN NEW.rank_search := 19; NEW.rank_address := 16; - ELSEIF NEW.type in ('airport') AND ST_GeometryType(NEW.geometry) in ('ST_Polygon','ST_MultiPolygon') THEN - NEW.rank_search := 18; - NEW.rank_address := 17; - ELSEIF NEW.type in ('moor') AND ST_GeometryType(NEW.geometry) in ('ST_Polygon','ST_MultiPolygon') THEN - NEW.rank_search := 17; - NEW.rank_address := 18; - ELSEIF NEW.type in ('moor') THEN - NEW.rank_search := 17; - NEW.rank_address := 0; - ELSEIF NEW.type in ('national_park') THEN - NEW.rank_search := 18; - NEW.rank_address := 18; ELSEIF NEW.type in ('suburb','croft','subdivision','isolated_dwelling') THEN NEW.rank_search := 20; NEW.rank_address := NEW.rank_search; @@ -1100,12 +1087,9 @@ BEGIN IF (NEW.extratags -> 'locality') = 'townland' THEN NEW.rank_address := 20; END IF; - ELSEIF NEW.type in ('hall_of_residence','neighbourhood','housing_estate','nature_reserve') THEN + ELSEIF NEW.type in ('neighbourhood') THEN NEW.rank_search := 22; NEW.rank_address := 22; - ELSEIF NEW.type in ('airport','street') THEN - NEW.rank_search := 26; - NEW.rank_address := NEW.rank_search; ELSEIF NEW.type in ('house','building') THEN NEW.rank_search := 30; NEW.rank_address := NEW.rank_search; @@ -1134,6 +1118,11 @@ BEGIN ELSEIF NEW.class = 'natural' and NEW.type in ('peak','volcano','mountain_range') THEN NEW.rank_search := 18; NEW.rank_address := 0; + ELSEIF NEW.class = 'natural' and NEW.type = 'sea' THEN + NEW.rank_search := 4; + NEW.rank_address := NEW.rank_search; + ELSEIF NEW.class = 'natural' and NEW.type in ('coastline') THEN + RETURN NULL; -- any feature more than 5 square miles is probably worth indexing ELSEIF ST_GeometryType(NEW.geometry) in ('ST_Polygon','ST_MultiPolygon') AND ST_Area(NEW.geometry) > 0.1 THEN NEW.rank_search := 22; @@ -1147,18 +1136,14 @@ BEGIN ELSEIF NEW.class = 'waterway' AND NEW.name is NULL THEN RETURN NULL; ELSEIF NEW.class = 'waterway' THEN - NEW.rank_address := 17; + NEW.rank_search := 17; + NEW.rank_address := 0; ELSEIF NEW.class = 'highway' AND NEW.osm_type != 'N' AND NEW.type in ('service','cycleway','path','footway','steps','bridleway','motorway_link','primary_link','trunk_link','secondary_link','tertiary_link') THEN NEW.rank_search := 27; NEW.rank_address := NEW.rank_search; ELSEIF NEW.class = 'highway' AND NEW.osm_type != 'N' THEN NEW.rank_search := 26; NEW.rank_address := NEW.rank_search; - ELSEIF NEW.class = 'natural' and NEW.type = 'sea' THEN - NEW.rank_search := 4; - NEW.rank_address := NEW.rank_search; - ELSEIF NEW.class = 'natural' and NEW.type in ('coastline') THEN - RETURN NULL; ELSEIF NEW.class = 'mountain_pass' THEN NEW.rank_search := 20; NEW.rank_address := 0; @@ -1179,7 +1164,7 @@ BEGIN END IF; -- a country code make no sense below rank 4 (country) - IF NEW.rank_address < 4 THEN + IF NEW.rank_search < 4 THEN NEW.calculated_country_code := NULL; END IF; @@ -1192,53 +1177,54 @@ BEGIN RETURN NEW; -- @DIFFUPDATES@ The following is not needed until doing diff updates, and slows the main index process down - IF (ST_GeometryType(NEW.geometry) in ('ST_Polygon','ST_MultiPolygon') AND ST_IsValid(NEW.geometry)) THEN - -- Performance: We just can't handle re-indexing for country level changes - IF st_area(NEW.geometry) < 1 THEN - -- mark items within the geometry for re-indexing --- RAISE WARNING 'placex poly insert: % % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type; - - -- work around bug in postgis, this may have been fixed in 2.0.0 (see http://trac.osgeo.org/postgis/ticket/547) - update placex set indexed_status = 2 where (st_covers(NEW.geometry, placex.geometry) OR ST_Intersects(NEW.geometry, placex.geometry)) - AND rank_search > NEW.rank_search and indexed_status = 0 and ST_geometrytype(placex.geometry) = 'ST_Point' and (rank_search < 28 or name is not null or (NEW.rank_search >= 16 and addr_place is not null)); - update placex set indexed_status = 2 where (st_covers(NEW.geometry, placex.geometry) OR ST_Intersects(NEW.geometry, placex.geometry)) - AND rank_search > NEW.rank_search and indexed_status = 0 and ST_geometrytype(placex.geometry) != 'ST_Point' and (rank_search < 28 or name is not null or (NEW.rank_search >= 16 and addr_place is not null)); - END IF; - ELSE - -- mark nearby items for re-indexing, where 'nearby' depends on the features rank_search and is a complete guess :( - diameter := 0; - -- 16 = city, anything higher than city is effectively ignored (polygon required!) - IF NEW.type='postcode' THEN - diameter := 0.05; - ELSEIF NEW.rank_search < 16 THEN + IF NEW.rank_address > 0 THEN + IF (ST_GeometryType(NEW.geometry) in ('ST_Polygon','ST_MultiPolygon') AND ST_IsValid(NEW.geometry)) THEN + -- Performance: We just can't handle re-indexing for country level changes + IF st_area(NEW.geometry) < 1 THEN + -- mark items within the geometry for re-indexing + -- RAISE WARNING 'placex poly insert: % % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type; + + -- work around bug in postgis, this may have been fixed in 2.0.0 (see http://trac.osgeo.org/postgis/ticket/547) + update placex set indexed_status = 2 where (st_covers(NEW.geometry, placex.geometry) OR ST_Intersects(NEW.geometry, placex.geometry)) + AND rank_search > NEW.rank_search and indexed_status = 0 and ST_geometrytype(placex.geometry) = 'ST_Point' and (rank_search < 28 or name is not null or (NEW.rank_search >= 16 and addr_place is not null)); + update placex set indexed_status = 2 where (st_covers(NEW.geometry, placex.geometry) OR ST_Intersects(NEW.geometry, placex.geometry)) + AND rank_search > NEW.rank_search and indexed_status = 0 and ST_geometrytype(placex.geometry) != 'ST_Point' and (rank_search < 28 or name is not null or (NEW.rank_search >= 16 and addr_place is not null)); + END IF; + ELSE + -- mark nearby items for re-indexing, where 'nearby' depends on the features rank_search and is a complete guess :( diameter := 0; - ELSEIF NEW.rank_search < 18 THEN - diameter := 0.1; - ELSEIF NEW.rank_search < 20 THEN - diameter := 0.05; - ELSEIF NEW.rank_search = 21 THEN - diameter := 0.001; - ELSEIF NEW.rank_search < 24 THEN - diameter := 0.02; - ELSEIF NEW.rank_search < 26 THEN - diameter := 0.002; -- 100 to 200 meters - ELSEIF NEW.rank_search < 28 THEN - diameter := 0.001; -- 50 to 100 meters - END IF; - IF diameter > 0 THEN --- RAISE WARNING 'placex point insert: % % % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type,diameter; - IF NEW.rank_search >= 26 THEN - -- roads may cause reparenting for >27 rank places - update placex set indexed_status = 2 where indexed_status = 0 and rank_search > NEW.rank_search and ST_DWithin(placex.geometry, NEW.geometry, diameter); - ELSEIF NEW.rank_search >= 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 > NEW.rank_search and ST_DWithin(placex.geometry, NEW.geometry, 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 > NEW.rank_search and ST_DWithin(placex.geometry, NEW.geometry, diameter) and (rank_search < 28 or name is not null); + -- 16 = city, anything higher than city is effectively ignored (polygon required!) + IF NEW.type='postcode' THEN + diameter := 0.05; + ELSEIF NEW.rank_search < 16 THEN + diameter := 0; + ELSEIF NEW.rank_search < 18 THEN + diameter := 0.1; + ELSEIF NEW.rank_search < 20 THEN + diameter := 0.05; + ELSEIF NEW.rank_search = 21 THEN + diameter := 0.001; + ELSEIF NEW.rank_search < 24 THEN + diameter := 0.02; + ELSEIF NEW.rank_search < 26 THEN + diameter := 0.002; -- 100 to 200 meters + ELSEIF NEW.rank_search < 28 THEN + diameter := 0.001; -- 50 to 100 meters + END IF; + IF diameter > 0 THEN + -- RAISE WARNING 'placex point insert: % % % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type,diameter; + IF NEW.rank_search >= 26 THEN + -- roads may cause reparenting for >27 rank places + update placex set indexed_status = 2 where indexed_status = 0 and rank_search > NEW.rank_search and ST_DWithin(placex.geometry, NEW.geometry, diameter); + ELSEIF NEW.rank_search >= 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 > NEW.rank_search and ST_DWithin(placex.geometry, NEW.geometry, 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 > NEW.rank_search and ST_DWithin(placex.geometry, NEW.geometry, diameter) and (rank_search < 28 or name is not null); + END IF; END IF; END IF; - END IF; -- add to tables for special search @@ -1288,6 +1274,7 @@ DECLARE search_maxrank INTEGER; address_maxrank INTEGER; address_street_word_id INTEGER; + address_street_word_ids INTEGER[]; parent_place_id_rank BIGINT; isin TEXT[]; @@ -1357,13 +1344,26 @@ BEGIN NEW.centroid := null; -- reclaculate country and partition - IF NEW.rank_search >= 4 THEN - --NEW.calculated_country_code := lower(get_country_code(NEW.geometry, NEW.country_code)); - NEW.calculated_country_code := lower(get_country_code(place_centroid)); + IF NEW.rank_search = 4 THEN + -- for countries, believe the mapped country code, + -- so that we remain in the right partition if the boundaries + -- suddenly expand. + NEW.partition := get_partition(lower(NEW.country_code)); + IF NEW.partition = 0 THEN + NEW.calculated_country_code := lower(get_country_code(place_centroid)); + NEW.partition := get_partition(NEW.calculated_country_code); + ELSE + NEW.calculated_country_code := lower(NEW.country_code); + END IF; ELSE - NEW.calculated_country_code := NULL; + IF NEW.rank_search > 4 THEN + --NEW.calculated_country_code := lower(get_country_code(NEW.geometry, NEW.country_code)); + NEW.calculated_country_code := lower(get_country_code(place_centroid)); + ELSE + NEW.calculated_country_code := NULL; + END IF; + NEW.partition := get_partition(NEW.calculated_country_code); END IF; - NEW.partition := get_partition(place_centroid, NEW.calculated_country_code); NEW.geometry_sector := geometry_sector(NEW.partition, place_centroid); -- Adding ourselves to the list simplifies address calculations later @@ -1431,7 +1431,7 @@ BEGIN IF NEW.parent_place_id IS NULL AND relation.members[i+1] = 'street' THEN --RAISE WARNING 'node in relation %',relation; SELECT place_id from placex where osm_type='W' and osm_id = substring(relation.members[i],2,200)::bigint - and rank_search = 26 INTO NEW.parent_place_id; + and rank_search = 26 and name is not null INTO NEW.parent_place_id; END IF; END LOOP; END IF; @@ -1460,7 +1460,7 @@ BEGIN IF NEW.parent_place_id IS NULL AND relation.members[i+1] = 'street' THEN --RAISE WARNING 'node in way that is in a relation %',relation; SELECT place_id from placex where osm_type='W' and osm_id = substring(relation.members[i],2,200)::bigint - and rank_search = 26 INTO NEW.parent_place_id; + and rank_search = 26 and name is not null INTO NEW.parent_place_id; END IF; END LOOP; END IF; @@ -1510,7 +1510,7 @@ BEGIN IF NEW.parent_place_id IS NULL AND relation.members[i+1] = 'street' THEN --RAISE WARNING 'way that is in a relation %',relation; SELECT place_id from placex where osm_type='W' and osm_id = substring(relation.members[i],2,200)::bigint - and rank_search = 26 INTO NEW.parent_place_id; + and rank_search = 26 and name is not null INTO NEW.parent_place_id; END IF; END LOOP; END IF; @@ -1520,18 +1520,18 @@ BEGIN --RAISE WARNING 'x3 %',NEW.parent_place_id; IF NEW.parent_place_id IS NULL AND NEW.street IS NOT NULL THEN - address_street_word_id := get_name_id(make_standard_name(NEW.street)); - IF address_street_word_id IS NOT NULL THEN - FOR location IN SELECT * from getNearestNamedRoadFeature(NEW.partition, place_centroid, address_street_word_id) LOOP + address_street_word_ids := get_name_ids(make_standard_name(NEW.street)); + IF address_street_word_ids IS NOT NULL THEN + FOR location IN SELECT * from getNearestNamedRoadFeature(NEW.partition, place_centroid, address_street_word_ids) LOOP NEW.parent_place_id := location.place_id; END LOOP; END IF; END IF; IF NEW.parent_place_id IS NULL AND NEW.addr_place IS NOT NULL THEN - address_street_word_id := get_name_id(make_standard_name(NEW.addr_place)); - IF address_street_word_id IS NOT NULL THEN - FOR location IN SELECT * from getNearestNamedPlaceFeature(NEW.partition, place_centroid, address_street_word_id) LOOP + address_street_word_ids := get_name_id(make_standard_name(NEW.addr_place)); + IF address_street_word_ids IS NOT NULL THEN + FOR location IN SELECT * from getNearestNamedPlaceFeature(NEW.partition, place_centroid, address_street_word_ids) LOOP NEW.parent_place_id := location.place_id; END LOOP; END IF; @@ -1615,17 +1615,13 @@ BEGIN END IF; -- merge in extra tags - IF NOT linkedPlacex.extratags IS NULL THEN - NEW.extratags := linkedPlacex.extratags || NEW.extratags; - END IF; - - IF NOT NEW.extratags ? linkedPlacex.class THEN - NEW.extratags := NEW.extratags || hstore(linkedPlacex.class, linkedPlacex.type); - END IF; + NEW.extratags := hstore(linkedPlacex.class, linkedPlacex.type) || coalesce(linkedPlacex.extratags, ''::hstore) || coalesce(NEW.extratags, ''::hstore); -- mark the linked place (excludes from search results) UPDATE placex set linked_place_id = NEW.place_id where place_id = linkedPlacex.place_id; + -- keep a note of the node id in case we need it for wikipedia in a bit + linked_node_id := linkedPlacex.osm_id; END LOOP; END LOOP; @@ -1654,13 +1650,7 @@ BEGIN END IF; -- merge in extra tags - IF NOT linkedPlacex.extratags IS NULL THEN - NEW.extratags := linkedPlacex.extratags || NEW.extratags; - END IF; - - IF NOT NEW.extratags ? linkedPlacex.class THEN - NEW.extratags := NEW.extratags || hstore(linkedPlacex.class, linkedPlacex.type); - END IF; + NEW.extratags := hstore(linkedPlacex.class, linkedPlacex.type) || coalesce(linkedPlacex.extratags, ''::hstore) || coalesce(NEW.extratags, ''::hstore); -- mark the linked place (excludes from search results) UPDATE placex set linked_place_id = NEW.place_id where place_id = linkedPlacex.place_id; @@ -1702,11 +1692,7 @@ BEGIN name_vector := make_keywords(NEW.name); -- merge in extra tags - NEW.extratags := linkedPlacex.extratags || NEW.extratags; - - IF NOT NEW.extratags ? linkedPlacex.class THEN - NEW.extratags := NEW.extratags || hstore(linkedPlacex.class, linkedPlacex.type); - END IF; + NEW.extratags := hstore(linkedPlacex.class, linkedPlacex.type) || coalesce(linkedPlacex.extratags, ''::hstore) || coalesce(NEW.extratags, ''::hstore); -- mark the linked place (excludes from search results) UPDATE placex set linked_place_id = NEW.place_id where place_id = linkedPlacex.place_id; @@ -2003,15 +1989,18 @@ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION place_delete() RETURNS TRIGGER AS $$ DECLARE - placeid BIGINT; + has_rank BOOLEAN; BEGIN --DEBUG: RAISE WARNING 'delete: % % % %',OLD.osm_type,OLD.osm_id,OLD.class,OLD.type; -- deleting large polygons can have a massive effect on the system - require manual intervention to let them through IF st_area(OLD.geometry) > 2 and st_isvalid(OLD.geometry) THEN - insert into import_polygon_delete values (OLD.osm_type,OLD.osm_id,OLD.class,OLD.type); - RETURN NULL; + SELECT bool_or(not (rank_address = 0 or rank_address > 26)) as ranked FROM placex WHERE osm_type = OLD.osm_type and osm_id = OLD.osm_id and class = OLD.class and type = OLD.type INTO has_rank; + IF has_rank THEN + insert into import_polygon_delete values (OLD.osm_type,OLD.osm_id,OLD.class,OLD.type); + RETURN NULL; + END IF; END IF; -- mark for delete @@ -2045,6 +2034,8 @@ BEGIN -- Just block these - lots and pointless IF NEW.class in ('landuse','natural') and NEW.name is null THEN + -- if the name tag was removed, older versions might still be lurking in the place table + DELETE FROM place where osm_type = NEW.osm_type and osm_id = NEW.osm_id and class = NEW.class and type = NEW.type; RETURN null; END IF; @@ -2092,7 +2083,7 @@ BEGIN -- To paraphrase, if there isn't an existing item, OR if the admin level has changed IF existingplacex.osm_type IS NULL OR - coalesce(existingplacex.admin_level, 15) != coalesce(NEW.admin_level, 15) + (coalesce(existingplacex.admin_level, 15) != coalesce(NEW.admin_level, 15) AND existingplacex.class = 'boundary' AND existingplacex.type = 'administrative') THEN IF existingplacex.osm_type IS NOT NULL THEN @@ -2256,6 +2247,16 @@ BEGIN geometry = NEW.geometry where osm_type = NEW.osm_type and osm_id = NEW.osm_id and class = NEW.class and type = NEW.type; + IF NEW.class in ('place','boundary') AND NEW.type in ('postcode','postal_code') THEN + IF NEW.postcode IS NULL THEN + -- postcode was deleted, no longer retain in placex + DELETE FROM placex where place_id = existingplacex.place_id; + RETURN NULL; + END IF; + + NEW.name := hstore('ref', NEW.postcode); + END IF; + update placex set name = NEW.name, housenumber = NEW.housenumber, @@ -2806,7 +2807,7 @@ DECLARE BEGIN place_centroid := ST_Centroid(pointgeo); - out_partition := get_partition(place_centroid, in_countrycode); + out_partition := get_partition(in_countrycode); out_parent_place_id := null; address_street_word_id := get_name_id(make_standard_name(in_street)); @@ -3080,6 +3081,7 @@ DECLARE 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