RETURN nearcountry.country_code;
END LOOP;
- -- WorldBoundaries data (second fallback - think there might be something broken in this data)
--- FOR nearcountry IN select country_code from country where st_covers(geometry, place_centre) limit 1
--- LOOP
--- RETURN nearcountry.country_code;
--- END LOOP;
-
---RAISE WARNING 'near country: %', ST_AsText(place_centre);
-
- -- Still not in a country - try nearest within ~12 miles of a country
--- FOR nearcountry IN select country_code from country where st_distance(geometry, place_centre) < 0.5
--- order by st_distance(geometry, place) limit 1
--- LOOP
--- RETURN nearcountry.country_code;
--- END LOOP;
-
RETURN NULL;
END;
$$
CREATE OR REPLACE FUNCTION create_interpolation(wayid BIGINT, interpolationtype TEXT) RETURNS INTEGER
AS $$
DECLARE
-
+
newpoints INTEGER;
waynodes BIGINT[];
nodeid BIGINT;
originalnumberrange INTEGER;
housenum INTEGER;
linegeo GEOMETRY;
+ splitline GEOMETRY;
+ sectiongeo GEOMETRY;
search_place_id BIGINT;
defpostalcode TEXT;
- havefirstpoint BOOLEAN;
- linestr TEXT;
BEGIN
- newpoints := 0;
- IF interpolationtype = 'odd' OR interpolationtype = 'even' OR interpolationtype = 'all' THEN
-
- select postcode from placex where osm_type = 'W' and osm_id = wayid INTO defpostalcode;
- select nodes from planet_osm_ways where id = wayid INTO waynodes;
---RAISE WARNING 'interpolation % % %',wayid,interpolationtype,waynodes;
- IF array_upper(waynodes, 1) IS NOT NULL THEN
-
- havefirstpoint := false;
-
- FOR nodeidpos in 1..array_upper(waynodes, 1) LOOP
-
- -- If there is a place of a type other than place/house, use that because
- -- it is guaranteed to be the original node. For place/house types use the
- -- one with the smallest id because the original node was created first.
- -- Ignore all nodes marked for deletion. (Might happen when the type changes.)
- select place_id from placex where osm_type = 'N' and osm_id = waynodes[nodeidpos]::BIGINT and indexed_status < 100 order by (type = 'house'),place_id limit 1 INTO search_place_id;
- IF search_place_id IS NULL THEN
- -- if no such node exists, create a record of the right type
- select * from placex where osm_type = 'N' and osm_id = waynodes[nodeidpos]::BIGINT and type = 'house' limit 1 INTO nextnode;
- select ST_SetSRID(ST_Point(lon::float/10000000,lat::float/10000000),4326) from planet_osm_nodes where id = waynodes[nodeidpos] INTO nextnode.geometry;
- IF nextnode.geometry IS NULL THEN
- -- we don't have any information about this point, most likely
- -- because an excerpt was updated and the node never imported
- -- because the interpolation is outside the region of the excerpt.
- -- Give up.
- RETURN newpoints;
- END IF;
- ELSE
- select * from placex where place_id = search_place_id INTO nextnode;
- END IF;
+ IF interpolationtype = 'odd' OR interpolationtype = 'even' THEN
+ stepsize := 2;
+ ELSEIF interpolationtype = 'all' THEN
+ stepsize := 1;
+ ELSEIF interpolationtype ~ '^\d+$' THEN
+ stepsize := interpolationtype::INTEGER;
+ ELSE
+ RETURN 0;
+ END IF;
---RAISE WARNING 'interpolation node % % % ',nextnode.housenumber,ST_X(nextnode.geometry),ST_Y(nextnode.geometry);
-
- IF havefirstpoint THEN
+ select nodes from planet_osm_ways where id = wayid INTO waynodes;
- -- add point to the line string
- linestr := linestr||','||ST_X(nextnode.geometry)||' '||ST_Y(nextnode.geometry);
- endnumber := ('0'||substring(nextnode.housenumber,'[0-9]+'))::integer;
+ IF array_upper(waynodes, 1) IS NULL THEN
+ RETURN 0;
+ END IF;
- IF startnumber IS NOT NULL and startnumber > 0 AND endnumber IS NOT NULL and endnumber > 0 AND @(startnumber - endnumber) < 1000 THEN
+ select postcode, geometry from placex where osm_type = 'W' and osm_id = wayid
+ INTO defpostalcode, linegeo;
---RAISE WARNING 'interpolation end % % ',nextnode.place_id,endnumber;
+ IF ST_GeometryType(linegeo) != 'ST_LineString' THEN
+ RETURN 0;
+ END IF;
- IF startnumber != endnumber THEN
+ startnumber := NULL;
+ newpoints := 0;
- linestr := linestr || ')';
---RAISE WARNING 'linestr %',linestr;
- linegeo := ST_GeomFromText(linestr,4326);
- linestr := 'LINESTRING('||ST_X(nextnode.geometry)||' '||ST_Y(nextnode.geometry);
- IF (startnumber > endnumber) THEN
- housenum := endnumber;
- endnumber := startnumber;
- startnumber := housenum;
- linegeo := ST_Reverse(linegeo);
- END IF;
- orginalstartnumber := startnumber;
- originalnumberrange := endnumber - startnumber;
-
--- Too much broken data worldwide for this test to be worth using
--- IF originalnumberrange > 500 THEN
--- RAISE WARNING 'Number block of % while processing % %', originalnumberrange, prevnode, nextnode;
--- END IF;
-
- IF (interpolationtype = 'odd' AND startnumber%2 = 0) OR (interpolationtype = 'even' AND startnumber%2 = 1) THEN
- startnumber := startnumber + 1;
- stepsize := 2;
- ELSE
- IF (interpolationtype = 'odd' OR interpolationtype = 'even') THEN
- startnumber := startnumber + 2;
- stepsize := 2;
- ELSE -- everything else assumed to be 'all'
- startnumber := startnumber + 1;
- stepsize := 1;
- END IF;
- END IF;
- endnumber := endnumber - 1;
- delete from placex where osm_type = 'N' and osm_id = prevnode.osm_id and type = 'house' and place_id != prevnode.place_id;
- FOR housenum IN startnumber..endnumber BY stepsize LOOP
- -- this should really copy postcodes but it puts a huge burdon on the system for no big benefit
- -- ideally postcodes should move up to the way
- insert into placex (osm_type, osm_id, class, type, admin_level, housenumber, street, addr_place, isin, postcode,
- country_code, parent_place_id, rank_address, rank_search, indexed_status, geometry)
- values ('N',prevnode.osm_id, 'place', 'house', prevnode.admin_level, housenum, prevnode.street, prevnode.addr_place, prevnode.isin, coalesce(prevnode.postcode, defpostalcode),
- prevnode.country_code, prevnode.parent_place_id, prevnode.rank_address, prevnode.rank_search, 1, ST_LineInterpolatePoint(linegeo, (housenum::float-orginalstartnumber::float)/originalnumberrange::float));
- newpoints := newpoints + 1;
---RAISE WARNING 'interpolation number % % ',prevnode.place_id,housenum;
- END LOOP;
- END IF;
- havefirstpoint := false;
- END IF;
+ FOR nodeidpos in 1..array_upper(waynodes, 1) LOOP
+
+ -- If there is a place of a type other than place/house, use that because
+ -- it is guaranteed to be the original node. For place/house types use the
+ -- one with the smallest id because the original node was created first.
+ -- Ignore all nodes marked for deletion. (Might happen when the type changes.)
+ select place_id from placex where osm_type = 'N' and osm_id = waynodes[nodeidpos]::BIGINT and indexed_status < 100 order by (type = 'house'),place_id limit 1 INTO search_place_id;
+ IF search_place_id IS NOT NULL THEN
+ select * from placex where place_id = search_place_id INTO nextnode;
+
+ IF nodeidpos > 1 and nodeidpos < array_upper(waynodes, 1) THEN
+ -- Make sure that the point is actually on the line. That might
+ -- be a bit paranoid but ensures that the algorithm still works
+ -- should osm2pgsql attempt to repair geometries.
+ splitline := split_line_on_node(linegeo, nextnode.geometry);
+ sectiongeo := ST_GeometryN(splitline, 1);
+ linegeo := ST_GeometryN(splitline, 2);
+ ELSE
+ sectiongeo = linegeo;
END IF;
+ endnumber := substring(nextnode.housenumber,'[0-9]+')::integer;
- IF NOT havefirstpoint THEN
- startnumber := ('0'||substring(nextnode.housenumber,'[0-9]+'))::integer;
- IF startnumber IS NOT NULL AND startnumber > 0 THEN
- havefirstpoint := true;
- linestr := 'LINESTRING('||ST_X(nextnode.geometry)||' '||ST_Y(nextnode.geometry);
- prevnode := nextnode;
+ IF startnumber IS NOT NULL AND endnumber IS NOT NULL
+ AND @(startnumber - endnumber) < 1000 AND startnumber != endnumber
+ AND ST_GeometryType(sectiongeo) = 'ST_LineString' THEN
+
+ IF (startnumber > endnumber) THEN
+ housenum := endnumber;
+ endnumber := startnumber;
+ startnumber := housenum;
+ sectiongeo := ST_Reverse(sectiongeo);
+ END IF;
+ orginalstartnumber := startnumber;
+ originalnumberrange := endnumber - startnumber;
+
+ startnumber := startnumber + stepsize;
+ -- correct for odd/even
+ IF (interpolationtype = 'odd' AND startnumber%2 = 0) OR (interpolationtype = 'even' AND startnumber%2 = 1) THEN
+ startnumber := startnumber - 1;
END IF;
---RAISE WARNING 'interpolation start % % ',nextnode.place_id,startnumber;
+ endnumber := endnumber - 1;
+
+ delete from placex where osm_type = 'N' and osm_id = prevnode.osm_id and type = 'house' and place_id != prevnode.place_id;
+ FOR housenum IN startnumber..endnumber BY stepsize LOOP
+ -- this should really copy postcodes but it puts a huge burden on
+ -- the system for no big benefit ideally postcodes should move up to the way
+ insert into placex (osm_type, osm_id, class, type, admin_level,
+ housenumber, street, addr_place, isin, postcode,
+ country_code, parent_place_id, rank_address, rank_search,
+ indexed_status, geometry)
+ values ('N', prevnode.osm_id, 'place', 'house', prevnode.admin_level,
+ housenum, prevnode.street, prevnode.addr_place, prevnode.isin, coalesce(prevnode.postcode, defpostalcode),
+ prevnode.country_code, prevnode.parent_place_id, prevnode.rank_address, prevnode.rank_search,
+ 1, ST_LineInterpolatePoint(sectiongeo, (housenum::float-orginalstartnumber::float)/originalnumberrange::float));
+ newpoints := newpoints + 1;
+--RAISE WARNING 'interpolation number % % ',prevnode.place_id,housenum;
+ END LOOP;
END IF;
- END LOOP;
+
+ -- early break if we are out of line string,
+ -- might happen when a line string loops back on itself
+ IF ST_GeometryType(linegeo) != 'ST_LineString' THEN
+ RETURN newpoints;
+ END IF;
+
+ startnumber := substring(nextnode.housenumber,'[0-9]+')::integer;
+ prevnode := nextnode;
END IF;
- END IF;
+ END LOOP;
--RAISE WARNING 'interpolation points % ',newpoints;
NEW.rank_address := 5;
END IF;
+ ELSEIF NEW.calculated_country_code = 'sg' THEN
+
+ IF NEW.postcode ~ '^([0-9]{6})$' THEN
+ NEW.rank_search := 25;
+ NEW.rank_address := 11;
+ END IF;
+
ELSEIF NEW.calculated_country_code = 'de' THEN
IF NEW.postcode ~ '^([0-9]{5})$' THEN
END IF;
ELSEIF NEW.class = 'landuse' AND ST_GeometryType(NEW.geometry) in ('ST_Polygon','ST_MultiPolygon') THEN
NEW.rank_search := 22;
- NEW.rank_address := NEW.rank_search;
+ IF NEW.type in ('residential', 'farm', 'farmyard', 'industrial', 'commercial', 'allotments', 'retail') THEN
+ NEW.rank_address := NEW.rank_search;
+ ELSE
+ NEW.rank_address := 0;
+ END IF;
ELSEIF NEW.class = 'natural' and NEW.type in ('peak','volcano','mountain_range') THEN
NEW.rank_search := 18;
NEW.rank_address := 0;
-- 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;
- NEW.rank_address := NEW.rank_search;
+ NEW.rank_address := 0;
ELSEIF NEW.class = 'highway' AND NEW.name is NULL AND
NEW.type in ('service','cycleway','path','footway','steps','bridleway','track','byway','motorway_link','primary_link','trunk_link','secondary_link','tertiary_link') THEN
-- RAISE WARNING 'unnamed minor feature %',NEW.osm_id;
result := deleteSearchName(NEW.partition, NEW.place_id);
DELETE FROM place_addressline WHERE place_id = NEW.place_id;
- DELETE FROM place_boundingbox where place_id = NEW.place_id;
result := deleteRoad(NEW.partition, NEW.place_id);
result := deleteLocationArea(NEW.partition, NEW.place_id, NEW.rank_search);
UPDATE placex set linked_place_id = null where linked_place_id = NEW.place_id;
place_centroid := ST_PointOnSurface(NEW.geometry);
NEW.centroid := null;
- -- reclaculate country and partition
+ -- recalculate country and partition
IF NEW.rank_search = 4 THEN
-- for countries, believe the mapped country code,
-- so that we remain in the right partition if the boundaries
-- waterway ways are linked when they are part of a relation and have the same class/type
IF NEW.osm_type = 'R' and NEW.class = 'waterway' THEN
- FOR relation IN select * from planet_osm_rels r where r.id = NEW.osm_id
+ FOR relation_members IN select members from planet_osm_rels r where r.id = NEW.osm_id and r.parts != array[]::bigint[]
LOOP
- FOR i IN relation.way_off+1..relation.rel_off LOOP
- IF relation.members[2*i] in ('', 'main_stream') THEN
- --DEBUG: RAISE WARNING 'waterway parent %, child %/%', NEW.osm_id, i, relation.parts[i];
- FOR location IN SELECT * FROM placex WHERE osm_type = 'W' and osm_id = relation.parts[i] and class = NEW.class and type = NEW.type
+ FOR i IN 1..array_upper(relation_members, 1) BY 2 LOOP
+ IF relation_members[i+1] in ('', 'main_stream', 'side_stream') AND substring(relation_members[i],1,1) = 'w' THEN
+ --DEBUG: RAISE WARNING 'waterway parent %, child %/%', NEW.osm_id, i, relation.members[i];
+ FOR linked_node_id IN SELECT place_id FROM placex
+ WHERE osm_type = 'W' and osm_id = substring(relation_members[i],2,200)::bigint
+ and class = NEW.class and type = NEW.type
+ and ( relation_members[i+1] != 'side_stream' or NEW.name->'name' = name->'name')
LOOP
- UPDATE placex SET linked_place_id = NEW.place_id WHERE place_id = location.place_id;
+ UPDATE placex SET linked_place_id = NEW.place_id WHERE place_id = linked_node_id;
END LOOP;
END IF;
END LOOP;
-- Handle a place changing type by removing the old data
-- My generated 'place' types are causing havok because they overlap with real keys
-- TODO: move them to their own special purpose key/class to avoid collisions
- IF existing.osm_type IS NULL AND (NEW.type not in ('postcode','house','houses')) THEN
- DELETE FROM place where osm_type = NEW.osm_type and osm_id = NEW.osm_id and class = NEW.class and type not in ('postcode','house','houses');
+ IF existing.osm_type IS NULL THEN
+ DELETE FROM place where osm_type = NEW.osm_type and osm_id = NEW.osm_id and class = NEW.class;
END IF;
--DEBUG: RAISE WARNING 'Existing: %',existing.osm_id;
$$
LANGUAGE plpgsql IMMUTABLE;
-CREATE OR REPLACE FUNCTION get_connected_ways(way_ids INTEGER[]) RETURNS SETOF planet_osm_ways
- AS $$
-DECLARE
- searchnodes INTEGER[];
- location RECORD;
- j INTEGER;
-BEGIN
-
- searchnodes := '{}';
- FOR j IN 1..array_upper(way_ids, 1) LOOP
- FOR location IN
- select nodes from planet_osm_ways where id = way_ids[j] LIMIT 1
- LOOP
- IF not (ARRAY[location.nodes] <@ searchnodes) THEN
- searchnodes := searchnodes || location.nodes;
- END IF;
- END LOOP;
- END LOOP;
-
- RETURN QUERY select * from planet_osm_ways where nodes && searchnodes and NOT ARRAY[id] <@ way_ids;
-END;
-$$
-LANGUAGE plpgsql IMMUTABLE;
+--CREATE OR REPLACE FUNCTION get_connected_ways(way_ids INTEGER[]) RETURNS SETOF planet_osm_ways
+-- AS $$
+--DECLARE
+-- searchnodes INTEGER[];
+-- location RECORD;
+-- j INTEGER;
+--BEGIN
+--
+-- searchnodes := '{}';
+-- FOR j IN 1..array_upper(way_ids, 1) LOOP
+-- FOR location IN
+-- select nodes from planet_osm_ways where id = way_ids[j] LIMIT 1
+-- LOOP
+-- IF not (ARRAY[location.nodes] <@ searchnodes) THEN
+-- searchnodes := searchnodes || location.nodes;
+-- END IF;
+-- END LOOP;
+-- END LOOP;
+--
+-- RETURN QUERY select * from planet_osm_ways where nodes && searchnodes and NOT ARRAY[id] <@ way_ids;
+--END;
+--$$
+--LANGUAGE plpgsql IMMUTABLE;
CREATE OR REPLACE FUNCTION get_address_postcode(for_place_id BIGINT) RETURNS TEXT
AS $$
$$
LANGUAGE plpgsql;
-CREATE OR REPLACE FUNCTION get_place_boundingbox(search_place_id BIGINT) RETURNS place_boundingbox
- AS $$
-DECLARE
- result place_boundingbox;
- numfeatures integer;
-BEGIN
- select * from place_boundingbox into result where place_id = search_place_id;
- IF result.place_id IS NULL THEN
--- remove isaddress = true because if there is a matching polygon it always wins
- select count(*) from place_addressline where address_place_id = search_place_id into numfeatures;
- insert into place_boundingbox select place_id,
- ST_Y(ST_PointN(ExteriorRing(ST_Box2D(geometry)),4)),ST_Y(ST_PointN(ExteriorRing(ST_Box2D(geometry)),2)),
- ST_X(ST_PointN(ExteriorRing(ST_Box2D(geometry)),1)),ST_X(ST_PointN(ExteriorRing(ST_Box2D(geometry)),3)),
- numfeatures, ST_Area(geometry),
- geometry as area from location_area where place_id = search_place_id;
- select * from place_boundingbox into result where place_id = search_place_id;
- END IF;
- IF result.place_id IS NULL THEN
--- TODO 0.0001
- insert into place_boundingbox select address_place_id,
- min(ST_Y(ST_Centroid(geometry))) as minlon,max(ST_Y(ST_Centroid(geometry))) as maxlon,
- min(ST_X(ST_Centroid(geometry))) as minlat,max(ST_X(ST_Centroid(geometry))) as maxlat,
- count(*), ST_Area(ST_Buffer(ST_Convexhull(ST_Collect(geometry)),0.0001)) as area,
- ST_Buffer(ST_Convexhull(ST_Collect(geometry)),0.0001) as boundary
- from (select * from place_addressline where address_place_id = search_place_id order by cached_rank_address limit 4000) as place_addressline join placex using (place_id)
- where address_place_id = search_place_id
--- and (isaddress = true OR place_id = search_place_id)
- and (st_length(geometry) < 0.01 or place_id = search_place_id)
- group by address_place_id limit 1;
- select * from place_boundingbox into result where place_id = search_place_id;
- END IF;
- return result;
-END;
-$$
-LANGUAGE plpgsql;
-
--- don't do the operation if it would be slow
-CREATE OR REPLACE FUNCTION get_place_boundingbox_quick(search_place_id BIGINT) RETURNS place_boundingbox
- AS $$
-DECLARE
- result place_boundingbox;
- numfeatures integer;
- rank integer;
-BEGIN
- select * from place_boundingbox into result where place_id = search_place_id;
- IF result IS NULL AND rank > 14 THEN
- select count(*) from place_addressline where address_place_id = search_place_id and isaddress = true into numfeatures;
- insert into place_boundingbox select place_id,
- ST_Y(ST_PointN(ExteriorRing(ST_Box2D(geometry)),4)),ST_Y(ST_PointN(ExteriorRing(ST_Box2D(geometry)),2)),
- ST_X(ST_PointN(ExteriorRing(ST_Box2D(geometry)),1)),ST_X(ST_PointN(ExteriorRing(ST_Box2D(geometry)),3)),
- numfeatures, ST_Area(geometry),
- geometry as area from location_area where place_id = search_place_id;
- select * from place_boundingbox into result where place_id = search_place_id;
- END IF;
- IF result IS NULL THEN
- select rank_search from placex where place_id = search_place_id into rank;
- IF rank > 20 THEN
--- TODO 0.0001
- insert into place_boundingbox select address_place_id,
- min(ST_Y(ST_Centroid(geometry))) as minlon,max(ST_Y(ST_Centroid(geometry))) as maxlon,
- min(ST_X(ST_Centroid(geometry))) as minlat,max(ST_X(ST_Centroid(geometry))) as maxlat,
- count(*), ST_Area(ST_Buffer(ST_Convexhull(ST_Collect(geometry)),0.0001)) as area,
- ST_Buffer(ST_Convexhull(ST_Collect(geometry)),0.0001) as boundary
- from place_addressline join placex using (place_id)
- where address_place_id = search_place_id
- and (isaddress = true OR place_id = search_place_id)
- and (st_length(geometry) < 0.01 or place_id = search_place_id)
- group by address_place_id limit 1;
- select * from place_boundingbox into result where place_id = search_place_id;
- END IF;
- END IF;
- return result;
-END;
-$$
-LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION update_place(search_place_id BIGINT) RETURNS BOOLEAN
AS $$
DECLARE
- result place_boundingbox;
numfeatures integer;
BEGIN
update placex set
wiki_article := regexp_replace(wiki_article,E'^(.*?)([a-z]{2,3}).wikipedia.org/wiki/',E'\\2:');
wiki_article := regexp_replace(wiki_article,E'^(.*?)([a-z]{2,3}).wikipedia.org/w/index.php\\?title=',E'\\2:');
wiki_article := regexp_replace(wiki_article,E'^(.*?)/([a-z]{2,3})/wiki/',E'\\2:');
- wiki_article := regexp_replace(wiki_article,E'^(.*?)([a-z]{2,3})[=:]',E'\\2:');
+ --wiki_article := regexp_replace(wiki_article,E'^(.*?)([a-z]{2,3})[=:]',E'\\2:');
wiki_article := replace(wiki_article,' ','_');
- wiki_article_title := trim(split_part(wiki_article, ':', 2));
- IF wiki_article_title IS NULL OR wiki_article_title = '' THEN
+ IF strpos(wiki_article, ':') IN (3,4) THEN
+ wiki_article_language := lower(trim(split_part(wiki_article, ':', 1)));
+ wiki_article_title := trim(substr(wiki_article, strpos(wiki_article, ':')+1));
+ ELSE
wiki_article_title := trim(wiki_article);
wiki_article_language := CASE WHEN langs[i] = 'english' THEN 'en' WHEN langs[i] = 'country' THEN get_country_language_code(country_code) ELSE langs[i] END;
- ELSE
- wiki_article_language := lower(trim(split_part(wiki_article, ':', 1)));
END IF;
select wikipedia_article.language,wikipedia_article.title,wikipedia_article.importance