From 041b3edf891b26bd3e043bb3cd7ad52d05d384d9 Mon Sep 17 00:00:00 2001 From: Brian Quinion Date: Sun, 1 Apr 2012 01:40:50 +0100 Subject: [PATCH] Adds code to merge place polygon and points using: label relation member admin_center, admin_centre relation member (with same name) exact name, search_rank and location match Adding this requires a new column and index: SELECT AddGeometryColumn('placex', 'centroid', 4326, 'GEOMETRY', 2); CREATE INDEX idx_placex_linked_place_id ON placex USING BTREE (linked_place_id); --- lib/template/search-html.php | 4 +- settings/settings.php | 1 + sql/functions.sql | 137 +++++++++++++++++++++++++++++++++++ sql/tables.sql | 1 + website/search.php | 7 +- 5 files changed, 148 insertions(+), 2 deletions(-) diff --git a/lib/template/search-html.php b/lib/template/search-html.php index 09b7021f..f66ee705 100644 --- a/lib/template/search-html.php +++ b/lib/template/search-html.php @@ -208,7 +208,7 @@ form{ map.panTo(lonLat, ); } - function panToLatLonZoom(lat,lon, zoom) { + function panToLatLonZoom(lat, lon, zoom) { var lonLat = new OpenLayers.LonLat(lon, lat).transform(new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject()); if (zoom != map.getZoom()) map.setCenter(lonLat, zoom); @@ -220,6 +220,8 @@ form{ var proj_EPSG4326 = new OpenLayers.Projection("EPSG:4326"); var proj_map = map.getProjectionObject(); map.zoomToExtent(new OpenLayers.Bounds(minlon,minlat,maxlon,maxlat).transform(proj_EPSG4326, proj_map)); + var lonLat = new OpenLayers.LonLat(lon, lat).transform(new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject()); + map.panTo(lonLat, ); var pointList = []; var style = { diff --git a/settings/settings.php b/settings/settings.php index 66d7bed6..30739f43 100644 --- a/settings/settings.php +++ b/settings/settings.php @@ -27,6 +27,7 @@ @define('CONST_Default_Zoom', 2); @define('CONST_Search_AreaPolygons_Enabled', true); + @define('CONST_Search_AreaPolygons', true); @define('CONST_Suggestions_Enabled', false); diff --git a/sql/functions.sql b/sql/functions.sql index 7e9f957b..1baecfc0 100644 --- a/sql/functions.sql +++ b/sql/functions.sql @@ -1189,6 +1189,9 @@ DECLARE location RECORD; way RECORD; relation RECORD; + relation_members TEXT[]; + relMember RECORD; + linkedplacex RECORD; search_diameter FLOAT; search_prevdiameter FLOAT; search_maxrank INTEGER; @@ -1240,6 +1243,7 @@ BEGIN DELETE FROM place_boundingbox where place_id = NEW.place_id; result := deleteRoad(NEW.partition, NEW.place_id); result := deleteLocationArea(NEW.partition, NEW.place_id); + UPDATE placex set linked_place_id = null where linked_place_id = NEW.place_id; END IF; -- reclaculate country and partition (should probably have a country_code and calculated_country_code as seperate fields) @@ -1257,6 +1261,7 @@ BEGIN -- Speed up searches - just use the centroid of the feature -- cheaper but less acurate place_centroid := ST_Centroid(NEW.geometry); + NEW.centroid := null; -- Thought this wasn't needed but when we add new languages to the country_name table -- we need to update the existing names @@ -1452,6 +1457,93 @@ BEGIN -- RAISE WARNING ' INDEXING: %',NEW; + IF NEW.osm_type = 'R' AND NEW.rank_search < 26 THEN + + -- see if we have any special relation members + select members from planet_osm_rels where id = NEW.osm_id INTO relation_members; + + FOR relMember IN select get_osm_rel_members(relation_members,ARRAY['label']) as member LOOP + + select * from placex where osm_type = upper(substring(relMember.member,1,1)) + and osm_id = substring(relMember.member,2,10000)::integer order by rank_search desc limit 1 into linkedPlacex; + + -- If we don't already have one use this as the centre point of the geometry + IF NEW.centroid IS NULL THEN + NEW.centroid := coalesce(linkedPlacex.centroid,st_centroid(linkedPlacex.geometry)); + END IF; + + -- merge in the label name, re-init word vector + NEW.name := linkedPlacex.name || NEW.name; + name_vector := make_keywords(NEW.name); + + -- merge in extra tags + NEW.extratags := linkedPlacex.extratags || NEW.extratags; + + -- mark the linked place (excludes from search results) + UPDATE placex set linked_place_id = NEW.place_id where place_id = linkedPlacex.place_id; + DELETE from search_name where place_id = linkedPlacex.place_id; + + END LOOP; + + FOR relMember IN select get_osm_rel_members(relation_members,ARRAY['admin_center','admin_centre']) as member LOOP + + select * from placex where osm_type = upper(substring(relMember.member,1,1)) + and osm_id = substring(relMember.member,2,10000)::integer order by rank_search desc limit 1 into linkedPlacex; + + IF NEW.name->'name' = linkedPlacex.name->'name' THEN + -- If we don't already have one use this as the centre point of the geometry + IF NEW.centroid IS NULL THEN + NEW.centroid := coalesce(linkedPlacex.centroid,st_centroid(linkedPlacex.geometry)); + END IF; + + -- merge in the name, re-init word vector + NEW.name := linkedPlacex.name || NEW.name; + name_vector := make_keywords(NEW.name); + + -- merge in extra tags + NEW.extratags := linkedPlacex.extratags || NEW.extratags; + + -- mark the linked place (excludes from search results) + UPDATE placex set linked_place_id = NEW.place_id where place_id = linkedPlacex.place_id; + DELETE from search_name where place_id = linkedPlacex.place_id; + END IF; + + END LOOP; + + -- not found one yet? how about doing a name search + IF NEW.centroid IS NULL THEN + FOR linkedPlacex IN select placex.* from search_name join placex using (place_id) WHERE + search_name.name_vector @> ARRAY[getorcreate_name_id(make_standard_name(NEW.name->'name'))] + AND search_name.search_rank = NEW.rank_search + AND search_name.place_id != NEW.place_id + AND osm_type = 'N' + AND NEW.name->'name' = placex.name->'name' + AND st_contains(NEW.geometry, placex.geometry) + LOOP + -- If we don't already have one use this as the centre point of the geometry + IF NEW.centroid IS NULL THEN + NEW.centroid := coalesce(linkedPlacex.centroid,st_centroid(linkedPlacex.geometry)); + END IF; + + -- merge in the name, re-init word vector + NEW.name := linkedPlacex.name || NEW.name; + name_vector := make_keywords(NEW.name); + + -- merge in extra tags + NEW.extratags := linkedPlacex.extratags || NEW.extratags; + + -- mark the linked place (excludes from search results) + UPDATE placex set linked_place_id = NEW.place_id where place_id = linkedPlacex.place_id; + DELETE from search_name where place_id = linkedPlacex.place_id; + END LOOP; + END IF; + + IF NEW.centroid IS NOT NULL THEN + place_centroid := NEW.centroid + END IF; + + END IF; + NEW.parent_place_id = 0; parent_place_id_rank = 0; @@ -1576,6 +1668,11 @@ BEGIN -- INSERT INTO search_name values (NEW.place_id, NEW.rank_search, NEW.rank_search, 0, NEW.country_code, name_vector, nameaddress_vector, place_centroid); END IF; + -- If we've not managed to pick up a better one - default centroid + IF NEW.centroid IS NULL THEN + NEW.centroid := place_centroid; + END IF; + END IF; RETURN NEW; @@ -1590,6 +1687,9 @@ DECLARE classtable TEXT; BEGIN + update placex set linked_place_id = null where linked_place_id = OLD.place_id; + update placex set indexed_status = 2 where linked_place_id = OLD.place_id and indexed_status = 0; + IF OLD.rank_address < 30 THEN -- mark everything linked to this place for re-indexing @@ -2545,3 +2645,40 @@ BEGIN END; $$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION get_osm_rel_members(members TEXT[], member TEXT) RETURNS TEXT[] + AS $$ +DECLARE + result TEXT[]; + i INTEGER; +BEGIN + + FOR i IN 1..ARRAY_UPPER(members,1) BY 2 LOOP + IF members[i+1] = member THEN + result := result || members[i]; + END IF; + END LOOP; + + return result; +END; +$$ +LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION get_osm_rel_members(members TEXT[], memberLabels TEXT[]) RETURNS SETOF TEXT + AS $$ +DECLARE + i INTEGER; +BEGIN + + FOR i IN 1..ARRAY_UPPER(members,1) BY 2 LOOP + IF members[i+1] = ANY(memberLabels) THEN + RETURN NEXT members[i]; + END IF; + END LOOP; + + RETURN; +END; +$$ +LANGUAGE plpgsql; + + diff --git a/sql/tables.sql b/sql/tables.sql index 795b9277..a3e50953 100644 --- a/sql/tables.sql +++ b/sql/tables.sql @@ -215,6 +215,7 @@ SELECT AddGeometryColumn('placex', 'geometry', 4326, 'GEOMETRY', 2); SELECT AddGeometryColumn('placex', 'centroid', 4326, 'GEOMETRY', 2); CREATE UNIQUE INDEX idx_place_id ON placex USING BTREE (place_id); CREATE INDEX idx_placex_osmid ON placex USING BTREE (osm_type, osm_id); +CREATE INDEX idx_placex_linked_place_id ON placex USING BTREE (linked_place_id); CREATE INDEX idx_placex_rank_search ON placex USING BTREE (rank_search, geometry_sector); CREATE INDEX idx_placex_geometry ON placex USING GIST (geometry); diff --git a/website/search.php b/website/search.php index ae2c3963..c00c4cfd 100755 --- a/website/search.php +++ b/website/search.php @@ -1139,7 +1139,7 @@ } foreach($aSearchResults as $iResNum => $aResult) { - if (CONST_Search_AreaPolygons || true) + if (CONST_Search_AreaPolygons) { // Get the bounding box and outline polygon $sSQL = "select place_id,numfeatures,area,outline,"; @@ -1148,6 +1148,7 @@ $sSQL .= "ST_AsText(outline) as outlinestring from get_place_boundingbox_quick(".$aResult['place_id'].")"; $sSQL = "select place_id,0 as numfeatures,st_area(geometry) as area,"; + $sSQL .= "ST_Y(centroid) as centrelat,ST_X(centroid) as centrelon,"; $sSQL .= "ST_Y(ST_PointN(ExteriorRing(ST_Box2D(geometry)),4)) as minlat,ST_Y(ST_PointN(ExteriorRing(ST_Box2D(geometry)),2)) as maxlat,"; $sSQL .= "ST_X(ST_PointN(ExteriorRing(ST_Box2D(geometry)),1)) as minlon,ST_X(ST_PointN(ExteriorRing(ST_Box2D(geometry)),3)) as maxlon,"; $sSQL .= "ST_AsText(geometry) as outlinestring from placex where place_id = ".$aResult['place_id'].' and st_geometrytype(ST_Box2D(geometry)) = \'ST_Polygon\''; @@ -1158,6 +1159,10 @@ } if ($aPointPolygon['place_id']) { + if ($aPointPolygon['centrelon'] !== null && $aPointPolygon['centrelat'] !== null ) { + $aResult['lat'] = $aPointPolygon['centrelat']; + $aResult['lon'] = $aPointPolygon['centrelon']; + } // Translate geometary string to point array if (preg_match('#POLYGON\\(\\(([- 0-9.,]+)#',$aPointPolygon['outlinestring'],$aMatch)) {