1 -- SPDX-License-Identifier: GPL-2.0-only
 
   3 -- This file is part of Nominatim. (https://nominatim.org)
 
   5 -- Copyright (C) 2022 by the Nominatim developer community.
 
   6 -- For a full list of authors see the git log.
 
   8 -- Functions for address interpolation objects in location_property_osmline.
 
  10 -- Splits the line at the given point and returns the two parts
 
  11 -- in a multilinestring.
 
  12 CREATE OR REPLACE FUNCTION split_line_on_node(line GEOMETRY, point GEOMETRY)
 
  16   RETURN ST_Split(ST_Snap(line, point, 0.0005), point);
 
  19 LANGUAGE plpgsql IMMUTABLE;
 
  22 CREATE OR REPLACE FUNCTION get_interpolation_address(in_address HSTORE, wayid BIGINT)
 
  29   IF akeys(in_address) != ARRAY['interpolation'] THEN
 
  33   SELECT nodes INTO waynodes FROM planet_osm_ways WHERE id = wayid;
 
  35     SELECT placex.address, placex.osm_id FROM placex
 
  36      WHERE osm_type = 'N' and osm_id = ANY(waynodes)
 
  37            and placex.address is not null
 
  38            and (placex.address ? 'street' or placex.address ? 'place')
 
  39            and indexed_status < 100
 
  41     -- mark it as a derived address
 
  42     RETURN location.address || in_address || hstore('_inherited', '');
 
  48 LANGUAGE plpgsql STABLE;
 
  52 -- find the parent road of the cut road parts
 
  53 CREATE OR REPLACE FUNCTION get_interpolation_parent(token_info JSONB,
 
  55                                                     centroid GEOMETRY, geom GEOMETRY)
 
  59   parent_place_id BIGINT;
 
  62   parent_place_id := find_parent_for_address(token_info, partition, centroid);
 
  64   IF parent_place_id is null THEN
 
  65     FOR location IN SELECT place_id FROM placex
 
  66         WHERE ST_DWithin(geom, placex.geometry, 0.001) and placex.rank_search = 26
 
  67         ORDER BY (ST_distance(placex.geometry, ST_LineInterpolatePoint(geom,0))+
 
  68                   ST_distance(placex.geometry, ST_LineInterpolatePoint(geom,0.5))+
 
  69                   ST_distance(placex.geometry, ST_LineInterpolatePoint(geom,1))) ASC limit 1
 
  71       parent_place_id := location.place_id;
 
  75   IF parent_place_id is null THEN
 
  79   RETURN parent_place_id;
 
  82 LANGUAGE plpgsql STABLE;
 
  85 CREATE OR REPLACE FUNCTION osmline_reinsert(node_id BIGINT, geom GEOMETRY)
 
  91    SELECT w.id FROM planet_osm_ways w, location_property_osmline p
 
  92      WHERE p.linegeo && geom and p.osm_id = w.id and p.indexed_status = 0
 
  93            and node_id = any(w.nodes) INTO existingline;
 
  95    IF existingline.id is not NULL THEN
 
  96        DELETE FROM location_property_osmline WHERE osm_id = existingline.id;
 
  97        INSERT INTO location_property_osmline (osm_id, address, linegeo)
 
  98          SELECT osm_id, address, geometry FROM place
 
  99            WHERE osm_type = 'W' and osm_id = existingline.id;
 
 108 CREATE OR REPLACE FUNCTION osmline_insert()
 
 112   NEW.place_id := nextval('seq_place');
 
 113   NEW.indexed_date := now();
 
 115   IF NEW.indexed_status IS NULL THEN
 
 116       IF NEW.address is NULL OR NOT NEW.address ? 'interpolation'
 
 117          OR NEW.address->'interpolation' NOT IN ('odd', 'even', 'all') THEN
 
 118           -- other interpolation types than odd/even/all (e.g. numeric ones) are not supported
 
 122       NEW.indexed_status := 1; --STATUS_NEW
 
 123       NEW.country_code := lower(get_country_code(NEW.linegeo));
 
 125       NEW.partition := get_partition(NEW.country_code);
 
 126       NEW.geometry_sector := geometry_sector(NEW.partition, NEW.linegeo);
 
 135 CREATE OR REPLACE FUNCTION osmline_update()
 
 139   place_centroid GEOMETRY;
 
 149   interpol_postcode TEXT;
 
 153   IF OLD.indexed_status = 100 THEN
 
 154     delete from location_property_osmline where place_id = OLD.place_id;
 
 158   IF NEW.indexed_status != 0 OR OLD.indexed_status = 0 THEN
 
 162   NEW.interpolationtype = NEW.address->'interpolation';
 
 164   place_centroid := ST_PointOnSurface(NEW.linegeo);
 
 165   NEW.parent_place_id = get_interpolation_parent(NEW.token_info, NEW.partition,
 
 166                                                  place_centroid, NEW.linegeo);
 
 168   interpol_postcode := token_normalized_postcode(NEW.address->'postcode');
 
 170   NEW.token_info := token_strip_info(NEW.token_info);
 
 171   IF NEW.address ? '_inherited' THEN
 
 172     NEW.address := hstore('interpolation', NEW.interpolationtype);
 
 175   -- if the line was newly inserted, split the line as necessary
 
 176   IF OLD.indexed_status = 1 THEN
 
 177       select nodes from planet_osm_ways where id = NEW.osm_id INTO waynodes;
 
 179       IF array_upper(waynodes, 1) IS NULL THEN
 
 183       linegeo := NEW.linegeo;
 
 186       FOR nodeidpos in 1..array_upper(waynodes, 1) LOOP
 
 188         select osm_id, address, geometry
 
 189           from place where osm_type = 'N' and osm_id = waynodes[nodeidpos]::BIGINT
 
 190                            and address is not NULL and address ? 'housenumber' limit 1 INTO nextnode;
 
 191         --RAISE NOTICE 'Nextnode.place_id: %s', nextnode.place_id;
 
 192         IF nextnode.osm_id IS NOT NULL THEN
 
 193           --RAISE NOTICE 'place_id is not null';
 
 194           IF nodeidpos > 1 and nodeidpos < array_upper(waynodes, 1) THEN
 
 195             -- Make sure that the point is actually on the line. That might
 
 196             -- be a bit paranoid but ensures that the algorithm still works
 
 197             -- should osm2pgsql attempt to repair geometries.
 
 198             splitline := split_line_on_node(linegeo, nextnode.geometry);
 
 199             sectiongeo := ST_GeometryN(splitline, 1);
 
 200             linegeo := ST_GeometryN(splitline, 2);
 
 202             sectiongeo = linegeo;
 
 204           endnumber := substring(nextnode.address->'housenumber','[0-9]+')::integer;
 
 206           IF startnumber IS NOT NULL AND endnumber IS NOT NULL
 
 207              AND startnumber != endnumber
 
 208              AND ST_GeometryType(sectiongeo) = 'ST_LineString' THEN
 
 210             IF (startnumber > endnumber) THEN
 
 211               housenum := endnumber;
 
 212               endnumber := startnumber;
 
 213               startnumber := housenum;
 
 214               sectiongeo := ST_Reverse(sectiongeo);
 
 217             -- determine postcode
 
 218             postcode := coalesce(interpol_postcode,
 
 219                                  token_normalized_postcode(prevnode.address->'postcode'),
 
 220                                  token_normalized_postcode(nextnode.address->'postcode'),
 
 223             IF postcode is NULL THEN
 
 224                 SELECT token_normalized_postcode(placex.postcode)
 
 225                   FROM placex WHERE place_id = NEW.parent_place_id INTO postcode;
 
 227             IF postcode is NULL THEN
 
 228                 postcode := get_nearest_postcode(NEW.country_code, nextnode.geometry);
 
 231             IF NEW.startnumber IS NULL THEN
 
 232                 NEW.startnumber := startnumber;
 
 233                 NEW.endnumber := endnumber;
 
 234                 NEW.linegeo := sectiongeo;
 
 235                 NEW.postcode := postcode;
 
 237               insert into location_property_osmline
 
 238                      (linegeo, partition, osm_id, parent_place_id,
 
 239                       startnumber, endnumber, interpolationtype,
 
 240                       address, postcode, country_code,
 
 241                       geometry_sector, indexed_status)
 
 242               values (sectiongeo, NEW.partition, NEW.osm_id, NEW.parent_place_id,
 
 243                       startnumber, endnumber, NEW.interpolationtype,
 
 244                       NEW.address, postcode,
 
 245                       NEW.country_code, NEW.geometry_sector, 0);
 
 249           -- early break if we are out of line string,
 
 250           -- might happen when a line string loops back on itself
 
 251           IF ST_GeometryType(linegeo) != 'ST_LineString' THEN
 
 255           startnumber := substring(nextnode.address->'housenumber','[0-9]+')::integer;
 
 256           prevnode := nextnode;
 
 261   -- marking descendants for reparenting is not needed, because there are
 
 262   -- actually no descendants for interpolation lines