Merge pull request #2597 from lonvia/reorganise-interpolations

Reorganise interpolation code
This commit is contained in:
Sarah Hoffmann
2022-01-28 08:40:08 +01:00
committed by GitHub
21 changed files with 390 additions and 324 deletions

View File

@@ -40,7 +40,7 @@ jobs:
ubuntu: [18, 20]
include:
- ubuntu: 18
postgresql: 9.5
postgresql: 9.6
postgis: 2.5
pytest: pytest
php: 7.2

View File

@@ -79,7 +79,7 @@ When running the import you may get a version mismatch:
pg_config seems to use bad includes sometimes when multiple versions
of PostgreSQL are available in the system. Make sure you remove the
server development libraries (`postgresql-server-dev-9.5` on Ubuntu)
server development libraries (`postgresql-server-dev-13` on Ubuntu)
and recompile (`cmake .. && make`).

View File

@@ -41,7 +41,7 @@ For compiling:
For running Nominatim:
* [PostgreSQL](https://www.postgresql.org) (9.5+ will work, 11+ strongly recommended)
* [PostgreSQL](https://www.postgresql.org) (9.6+ will work, 11+ strongly recommended)
* [PostGIS](https://postgis.net) (2.2+ will work, 3.0+ strongly recommended)
* [Python 3](https://www.python.org/) (3.6+)
* [Psycopg2](https://www.psycopg.org) (2.7+)

View File

@@ -348,7 +348,9 @@ class PlaceLookup
$sSQL .= ' null::text AS extra_place ';
$sSQL .= ' FROM (';
$sSQL .= ' SELECT place_id, '; // interpolate the Tiger housenumbers here
$sSQL .= ' ST_LineInterpolatePoint(linegeo, (housenumber_for_place-startnumber::float)/(endnumber-startnumber)::float) AS centroid, ';
$sSQL .= ' CASE WHEN startnumber != endnumber';
$sSQL .= ' THEN ST_LineInterpolatePoint(linegeo, (housenumber_for_place-startnumber::float)/(endnumber-startnumber)::float)';
$sSQL .= ' ELSE ST_LineInterpolatePoint(linegeo, 0.5) END AS centroid, ';
$sSQL .= ' parent_place_id, ';
$sSQL .= ' housenumber_for_place';
$sSQL .= ' FROM (';
@@ -405,7 +407,7 @@ class PlaceLookup
$sSQL .= ' CASE '; // interpolate the housenumbers here
$sSQL .= ' WHEN startnumber != endnumber ';
$sSQL .= ' THEN ST_LineInterpolatePoint(linegeo, (housenumber_for_place-startnumber::float)/(endnumber-startnumber)::float) ';
$sSQL .= ' ELSE ST_LineInterpolatePoint(linegeo, 0.5) ';
$sSQL .= ' ELSE linegeo ';
$sSQL .= ' END as centroid, ';
$sSQL .= ' parent_place_id, ';
$sSQL .= ' housenumber_for_place ';

View File

@@ -64,8 +64,8 @@ class ReverseGeocode
{
Debug::newFunction('lookupInterpolation');
$sSQL = 'SELECT place_id, parent_place_id, 30 as rank_search,';
$sSQL .= ' ST_LineLocatePoint(linegeo,'.$sPointSQL.') as fraction,';
$sSQL .= ' startnumber, endnumber, interpolationtype,';
$sSQL .= ' (endnumber - startnumber) * ST_LineLocatePoint(linegeo,'.$sPointSQL.') as fhnr,';
$sSQL .= ' startnumber, endnumber, step,';
$sSQL .= ' ST_Distance(linegeo,'.$sPointSQL.') as distance';
$sSQL .= ' FROM location_property_osmline';
$sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', linegeo, '.$fSearchDiam.')';
@@ -327,9 +327,9 @@ class ReverseGeocode
&& $this->iMaxRank >= 28
) {
$sSQL = 'SELECT place_id,parent_place_id,30 as rank_search,';
$sSQL .= 'ST_LineLocatePoint(linegeo,'.$sPointSQL.') as fraction,';
$sSQL .= 'ST_distance('.$sPointSQL.', linegeo) as distance,';
$sSQL .= 'startnumber,endnumber,interpolationtype';
$sSQL .= ' (endnumber - startnumber) * ST_LineLocatePoint(linegeo,'.$sPointSQL.') as fhnr,';
$sSQL .= ' startnumber, endnumber, step,';
$sSQL .= ' ST_Distance('.$sPointSQL.', linegeo) as distance';
$sSQL .= ' FROM location_property_tiger WHERE parent_place_id = '.$oResult->iId;
$sSQL .= ' AND ST_DWithin('.$sPointSQL.', linegeo, 0.001)';
$sSQL .= ' ORDER BY distance ASC limit 1';
@@ -341,7 +341,11 @@ class ReverseGeocode
if ($aPlaceTiger) {
$aPlace = $aPlaceTiger;
$oResult = new Result($aPlaceTiger['place_id'], Result::TABLE_TIGER);
$oResult->iHouseNumber = closestHouseNumber($aPlaceTiger);
$iRndNum = max(0, round($aPlaceTiger['fhnr'] / $aPlaceTiger['step']) * $aPlaceTiger['step']);
$oResult->iHouseNumber = $aPlaceTiger['startnumber'] + $iRndNum;
if ($oResult->iHouseNumber > $aPlaceTiger['endnumber']) {
$oResult->iHouseNumber = $aPlaceTiger['endnumber'];
}
$iRankAddress = 30;
}
}
@@ -363,7 +367,11 @@ class ReverseGeocode
if ($aHouse) {
$oResult = new Result($aHouse['place_id'], Result::TABLE_OSMLINE);
$oResult->iHouseNumber = closestHouseNumber($aHouse);
$iRndNum = max(0, round($aHouse['fhnr'] / $aHouse['step']) * $aHouse['step']);
$oResult->iHouseNumber = $aHouse['startnumber'] + $iRndNum;
if ($oResult->iHouseNumber > $aHouse['endnumber']) {
$oResult->iHouseNumber = $aHouse['endnumber'];
}
$aPlace = $aHouse;
}
}

View File

@@ -769,18 +769,9 @@ class SearchDescription
// if nothing found, search in the interpolation line table
$sSQL = 'SELECT distinct place_id FROM location_property_osmline';
$sSQL .= ' WHERE startnumber is not NULL';
$sSQL .= ' AND parent_place_id in ('.$sRoadPlaceIDs.') AND (';
if ($iHousenumber % 2 == 0) {
// If housenumber is even, look for housenumber in streets
// with interpolationtype even or all.
$sSQL .= "interpolationtype='even'";
} else {
// Else look for housenumber with interpolationtype odd or all.
$sSQL .= "interpolationtype='odd'";
}
$sSQL .= " or interpolationtype='all') and ";
$sSQL .= $iHousenumber.'>=startnumber and ';
$sSQL .= $iHousenumber.'<=endnumber';
$sSQL .= ' and parent_place_id in ('.$sRoadPlaceIDs.')';
$sSQL .= ' and ('.$iHousenumber.' - startnumber) % step = 0';
$sSQL .= ' and '.$iHousenumber.' between startnumber and endnumber';
$sSQL .= $this->oContext->excludeSQL(' AND place_id');
Debug::printSQL($sSQL);
@@ -795,15 +786,9 @@ class SearchDescription
// If nothing found then search in Tiger data (location_property_tiger)
if (CONST_Use_US_Tiger_Data && $sRoadPlaceIDs && $bIsIntHouseNumber && empty($aResults)) {
$sSQL = 'SELECT place_id FROM location_property_tiger';
$sSQL .= ' WHERE parent_place_id in ('.$sRoadPlaceIDs.') and (';
if ($iHousenumber % 2 == 0) {
$sSQL .= "interpolationtype='even'";
} else {
$sSQL .= "interpolationtype='odd'";
}
$sSQL .= " or interpolationtype='all') and ";
$sSQL .= $iHousenumber.'>=startnumber and ';
$sSQL .= $iHousenumber.'<=endnumber';
$sSQL .= ' WHERE parent_place_id in ('.$sRoadPlaceIDs.')';
$sSQL .= ' and ('.$iHousenumber.' - startnumber) % step = 0';
$sSQL .= ' and '.$iHousenumber.' between startnumber and endnumber';
$sSQL .= $this->oContext->excludeSQL(' AND place_id');
Debug::printSQL($sSQL);

View File

@@ -206,26 +206,6 @@ function parseLatLon($sQuery)
return array($sFound, $fQueryLat, $fQueryLon);
}
function closestHouseNumber($aRow)
{
$fHouse = $aRow['startnumber']
+ ($aRow['endnumber'] - $aRow['startnumber']) * $aRow['fraction'];
switch ($aRow['interpolationtype']) {
case 'odd':
$iHn = (int)($fHouse/2) * 2 + 1;
break;
case 'even':
$iHn = (int)(round($fHouse/2)) * 2;
break;
default:
$iHn = (int)(round($fHouse));
break;
}
return max(min($aRow['endnumber'], $iHn), $aRow['startnumber']);
}
if (!function_exists('array_key_last')) {
function array_key_last(array $array)
{

View File

@@ -7,17 +7,6 @@
-- Functions for address interpolation objects in location_property_osmline.
-- Splits the line at the given point and returns the two parts
-- in a multilinestring.
CREATE OR REPLACE FUNCTION split_line_on_node(line GEOMETRY, point GEOMETRY)
RETURNS GEOMETRY
AS $$
BEGIN
RETURN ST_Split(ST_Snap(line, point, 0.0005), point);
END;
$$
LANGUAGE plpgsql IMMUTABLE;
CREATE OR REPLACE FUNCTION get_interpolation_address(in_address HSTORE, wayid BIGINT)
RETURNS HSTORE
@@ -64,9 +53,13 @@ BEGIN
IF parent_place_id is null THEN
FOR location IN SELECT place_id FROM placex
WHERE ST_DWithin(geom, placex.geometry, 0.001) and placex.rank_search = 26
ORDER BY (ST_distance(placex.geometry, ST_LineInterpolatePoint(geom,0))+
ORDER BY CASE WHEN ST_GeometryType(geom) = 'ST_Line' THEN
(ST_distance(placex.geometry, ST_LineInterpolatePoint(geom,0))+
ST_distance(placex.geometry, ST_LineInterpolatePoint(geom,0.5))+
ST_distance(placex.geometry, ST_LineInterpolatePoint(geom,1))) ASC limit 1
ST_distance(placex.geometry, ST_LineInterpolatePoint(geom,1)))
ELSE ST_distance(placex.geometry, geom) END
ASC
LIMIT 1
LOOP
parent_place_id := location.place_id;
END LOOP;
@@ -82,24 +75,38 @@ $$
LANGUAGE plpgsql STABLE;
CREATE OR REPLACE FUNCTION osmline_reinsert(node_id BIGINT, geom GEOMETRY)
RETURNS BOOLEAN
CREATE OR REPLACE FUNCTION reinsert_interpolation(way_id BIGINT, addr HSTORE,
geom GEOMETRY)
RETURNS INT
AS $$
DECLARE
existingline RECORD;
existing BIGINT[];
BEGIN
SELECT w.id FROM planet_osm_ways w, location_property_osmline p
WHERE p.linegeo && geom and p.osm_id = w.id and p.indexed_status = 0
and node_id = any(w.nodes) INTO existingline;
-- Get the existing entry from the interpolation table.
SELECT array_agg(place_id) INTO existing
FROM location_property_osmline WHERE osm_id = way_id;
IF existingline.id is not NULL THEN
DELETE FROM location_property_osmline WHERE osm_id = existingline.id;
INSERT INTO location_property_osmline (osm_id, address, linegeo)
SELECT osm_id, address, geometry FROM place
WHERE osm_type = 'W' and osm_id = existingline.id;
END IF;
IF existing IS NULL or array_length(existing, 1) = 0 THEN
INSERT INTO location_property_osmline (osm_id, address, linegeo)
VALUES (way_id, addr, geom);
ELSE
-- Update the interpolation table:
-- The first entry gets the original data, all other entries
-- are removed and will be recreated on indexing.
-- (An interpolation can be split up, if it has more than 2 address nodes)
UPDATE location_property_osmline
SET address = addr,
linegeo = geom,
startnumber = null,
indexed_status = 1
WHERE place_id = existing[1];
IF array_length(existing, 1) > 1 THEN
DELETE FROM location_property_osmline
WHERE place_id = any(existing[2:]);
END IF;
END IF;
RETURN true;
RETURN 1;
END;
$$
LANGUAGE plpgsql;
@@ -114,8 +121,10 @@ BEGIN
IF NEW.indexed_status IS NULL THEN
IF NEW.address is NULL OR NOT NEW.address ? 'interpolation'
OR NEW.address->'interpolation' NOT IN ('odd', 'even', 'all') THEN
-- other interpolation types than odd/even/all (e.g. numeric ones) are not supported
OR NOT (NEW.address->'interpolation' in ('odd', 'even', 'all')
or NEW.address->'interpolation' similar to '[1-9]')
THEN
-- alphabetic interpolation is not supported
RETURN NULL;
END IF;
@@ -136,18 +145,20 @@ CREATE OR REPLACE FUNCTION osmline_update()
RETURNS TRIGGER
AS $$
DECLARE
place_centroid GEOMETRY;
waynodes BIGINT[];
prevnode RECORD;
nextnode RECORD;
startnumber INTEGER;
endnumber INTEGER;
housenum INTEGER;
newstart INTEGER;
newend INTEGER;
moddiff SMALLINT;
linegeo GEOMETRY;
splitline GEOMETRY;
sectiongeo GEOMETRY;
interpol_postcode TEXT;
postcode TEXT;
stepmod SMALLINT;
BEGIN
-- deferred delete
IF OLD.indexed_status = 100 THEN
@@ -159,107 +170,139 @@ BEGIN
RETURN NEW;
END IF;
NEW.interpolationtype = NEW.address->'interpolation';
place_centroid := ST_PointOnSurface(NEW.linegeo);
NEW.parent_place_id = get_interpolation_parent(NEW.token_info, NEW.partition,
place_centroid, NEW.linegeo);
NEW.parent_place_id := get_interpolation_parent(NEW.token_info, NEW.partition,
ST_PointOnSurface(NEW.linegeo),
NEW.linegeo);
interpol_postcode := token_normalized_postcode(NEW.address->'postcode');
NEW.token_info := token_strip_info(NEW.token_info);
IF NEW.address ? '_inherited' THEN
NEW.address := hstore('interpolation', NEW.interpolationtype);
NEW.address := hstore('interpolation', NEW.address->'interpolation');
END IF;
-- if the line was newly inserted, split the line as necessary
-- If the line was newly inserted, split the line as necessary.
IF OLD.indexed_status = 1 THEN
select nodes from planet_osm_ways where id = NEW.osm_id INTO waynodes;
IF NEW.address->'interpolation' in ('odd', 'even') THEN
NEW.step := 2;
stepmod := CASE WHEN NEW.address->'interpolation' = 'odd' THEN 1 ELSE 0 END;
ELSE
NEW.step := CASE WHEN NEW.address->'interpolation' = 'all'
THEN 1
ELSE (NEW.address->'interpolation')::SMALLINT END;
stepmod := NULL;
END IF;
IF array_upper(waynodes, 1) IS NULL THEN
RETURN NEW;
SELECT nodes INTO waynodes
FROM planet_osm_ways WHERE id = NEW.osm_id;
IF array_upper(waynodes, 1) IS NULL THEN
RETURN NEW;
END IF;
linegeo := null;
SELECT null::integer as hnr INTO prevnode;
-- Go through all nodes on the interpolation line that have a housenumber.
FOR nextnode IN
SELECT DISTINCT ON (nodeidpos)
osm_id, address, geometry,
substring(address->'housenumber','[0-9]+')::integer as hnr
FROM placex, generate_series(1, array_upper(waynodes, 1)) nodeidpos
WHERE osm_type = 'N' and osm_id = waynodes[nodeidpos]::BIGINT
and address is not NULL and address ? 'housenumber'
ORDER BY nodeidpos
LOOP
RAISE WARNING 'processing point % (%)', nextnode.hnr, ST_AsText(nextnode.geometry);
IF linegeo is null THEN
linegeo := NEW.linegeo;
ELSE
splitline := ST_Split(ST_Snap(linegeo, nextnode.geometry, 0.0005), nextnode.geometry);
sectiongeo := ST_GeometryN(splitline, 1);
linegeo := ST_GeometryN(splitline, 2);
END IF;
linegeo := NEW.linegeo;
startnumber := NULL;
FOR nodeidpos in 1..array_upper(waynodes, 1) LOOP
select osm_id, address, geometry
from place where osm_type = 'N' and osm_id = waynodes[nodeidpos]::BIGINT
and address is not NULL and address ? 'housenumber' limit 1 INTO nextnode;
--RAISE NOTICE 'Nextnode.place_id: %s', nextnode.place_id;
IF nextnode.osm_id IS NOT NULL THEN
--RAISE NOTICE 'place_id is not null';
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.address->'housenumber','[0-9]+')::integer;
IF startnumber IS NOT NULL AND endnumber IS NOT NULL
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;
-- determine postcode
postcode := coalesce(interpol_postcode,
token_normalized_postcode(prevnode.address->'postcode'),
token_normalized_postcode(nextnode.address->'postcode'),
postcode);
IF postcode is NULL THEN
SELECT token_normalized_postcode(placex.postcode)
FROM placex WHERE place_id = NEW.parent_place_id INTO postcode;
END IF;
IF postcode is NULL THEN
postcode := get_nearest_postcode(NEW.country_code, nextnode.geometry);
END IF;
IF NEW.startnumber IS NULL THEN
NEW.startnumber := startnumber;
NEW.endnumber := endnumber;
NEW.linegeo := sectiongeo;
NEW.postcode := postcode;
ELSE
insert into location_property_osmline
(linegeo, partition, osm_id, parent_place_id,
startnumber, endnumber, interpolationtype,
address, postcode, country_code,
geometry_sector, indexed_status)
values (sectiongeo, NEW.partition, NEW.osm_id, NEW.parent_place_id,
startnumber, endnumber, NEW.interpolationtype,
NEW.address, postcode,
NEW.country_code, NEW.geometry_sector, 0);
END IF;
END IF;
-- 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 NEW;
END IF;
startnumber := substring(nextnode.address->'housenumber','[0-9]+')::integer;
prevnode := nextnode;
IF prevnode.hnr is not null
-- Check if there are housenumbers to interpolate between the
-- regularly mapped housenumbers.
-- (Conveniently also fails if one of the house numbers is not a number.)
and abs(prevnode.hnr - nextnode.hnr) > NEW.step
THEN
IF prevnode.hnr < nextnode.hnr THEN
startnumber := prevnode.hnr;
endnumber := nextnode.hnr;
ELSE
startnumber := nextnode.hnr;
endnumber := prevnode.hnr;
sectiongeo := ST_Reverse(sectiongeo);
END IF;
END LOOP;
-- Adjust the interpolation, so that only inner housenumbers
-- are taken into account.
IF stepmod is null THEN
newstart := startnumber + NEW.step;
ELSE
newstart := startnumber + 1;
moddiff := newstart % NEW.step - stepmod;
IF moddiff < 0 THEN
newstart := newstart + (NEW.step + moddiff);
ELSE
newstart := newstart + moddiff;
END IF;
END IF;
newend := newstart + ((endnumber - 1 - newstart) / NEW.step) * NEW.step;
-- If newstart and newend are the same, then this returns a point.
sectiongeo := ST_LineSubstring(sectiongeo,
(newstart - startnumber)::float / (endnumber - startnumber)::float,
(newend - startnumber)::float / (endnumber - startnumber)::float);
startnumber := newstart;
endnumber := newend;
-- determine postcode
postcode := coalesce(interpol_postcode,
token_normalized_postcode(prevnode.address->'postcode'),
token_normalized_postcode(nextnode.address->'postcode'),
postcode);
IF postcode is NULL THEN
SELECT token_normalized_postcode(placex.postcode)
FROM placex WHERE place_id = NEW.parent_place_id INTO postcode;
END IF;
IF postcode is NULL THEN
postcode := get_nearest_postcode(NEW.country_code, nextnode.geometry);
END IF;
-- Add the interpolation. If this is the first segment, just modify
-- the interpolation to be inserted, otherwise add an additional one
-- (marking it indexed already).
IF NEW.startnumber IS NULL THEN
NEW.startnumber := startnumber;
NEW.endnumber := endnumber;
NEW.linegeo := sectiongeo;
NEW.postcode := postcode;
ELSE
INSERT INTO location_property_osmline
(linegeo, partition, osm_id, parent_place_id,
startnumber, endnumber, step,
address, postcode, country_code,
geometry_sector, indexed_status)
VALUES (sectiongeo, NEW.partition, NEW.osm_id, NEW.parent_place_id,
startnumber, endnumber, NEW.step,
NEW.address, postcode,
NEW.country_code, NEW.geometry_sector, 0);
END IF;
-- 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 NEW;
END IF;
END IF;
prevnode := nextnode;
END LOOP;
END IF;
-- marking descendants for reparenting is not needed, because there are
-- actually no descendants for interpolation lines
RETURN NEW;
END;
$$

View File

@@ -13,8 +13,8 @@ DECLARE
country RECORD;
existing RECORD;
existingplacex RECORD;
existingline RECORD;
result BOOLEAN;
existingline BIGINT[];
interpol RECORD;
BEGIN
{% if debug %}
RAISE WARNING 'place_insert: % % % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type,st_area(NEW.geometry);
@@ -57,19 +57,7 @@ BEGIN
IF NEW.class='place' and NEW.type='houses'
and NEW.osm_type='W' and ST_GeometryType(NEW.geometry) = 'ST_LineString'
THEN
-- Get the existing entry from the interpolation table.
SELECT * INTO existingline
FROM location_property_osmline WHERE osm_id = NEW.osm_id;
-- Update the interpolation table:
-- delete all old interpolation lines with same osm_id
-- and insert the new one(s) (they can be split up, if they have > 2 nodes)
IF existingline.osm_id IS NOT NULL THEN
DELETE FROM location_property_osmline where osm_id = NEW.osm_id;
END IF;
INSERT INTO location_property_osmline (osm_id, address, linegeo)
VALUES (NEW.osm_id, NEW.address, NEW.geometry);
PERFORM reinsert_interpolation(NEW.osm_id, NEW.address, NEW.geometry);
-- Now invalidate all address nodes on the line.
-- They get their parent from the interpolation.
@@ -156,6 +144,28 @@ BEGIN
RETURN null;
END IF;
-- If an address node is part of a interpolation line and changes or is
-- newly inserted (happens when the node already existed but now gets address
-- information), then mark the interpolation line for reparenting.
-- (Already here, because interpolation lines are reindexed before nodes,
-- so in the second call it would be too late.)
IF NEW.osm_type='N'
and coalesce(existing.address, ''::hstore) != coalesce(NEW.address, ''::hstore)
THEN
FOR interpol IN
SELECT DISTINCT osm_id, address, geometry FROM place, planet_osm_ways w
WHERE NEW.geometry && place.geometry
and place.osm_type = 'W'
and exists (SELECT * FROM location_property_osmline
WHERE osm_id = place.osm_id
and indexed_status in (0, 2))
and w.id = place.osm_id and NEW.osm_id = any (w.nodes)
LOOP
PERFORM reinsert_interpolation(interpol.osm_id, interpol.address,
interpol.geometry);
END LOOP;
END IF;
-- Get the existing placex entry.
SELECT * INTO existingplacex
FROM placex
@@ -278,16 +288,6 @@ BEGIN
geometry = NEW.geometry
WHERE place_id = existingplacex.place_id;
-- If an address node which is part of a interpolation line changes
-- mark this line for reparenting.
-- (Already here, because interpolation lines are reindexed before nodes,
-- so in the second call it would be too late.)
IF NEW.osm_type='N'
and coalesce(existing.address, ''::hstore) != coalesce(NEW.address, ''::hstore)
THEN
result:= osmline_reinsert(NEW.osm_id, NEW.geometry);
END IF;
-- Invalidate linked places: they potentially get a new name and addresses.
IF existingplacex.linked_place_id is not NULL THEN
UPDATE placex x

View File

@@ -29,9 +29,9 @@ DECLARE
BEGIN
-- For POI nodes, check if the address should be derived from a surrounding
-- building.
IF p.rank_search < 30 OR p.osm_type != 'N' OR p.address is not null THEN
IF p.rank_search < 30 OR p.osm_type != 'N' THEN
result.address := p.address;
ELSE
ELSEIF p.address is null THEN
-- The additional && condition works around the misguided query
-- planner of postgis 3.0.
SELECT placex.address || hstore('_inherited', '') INTO result.address
@@ -42,6 +42,20 @@ BEGIN
and (placex.address ? 'housenumber' or placex.address ? 'street' or placex.address ? 'place')
and rank_search = 30 AND ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon')
LIMIT 1;
ELSE
result.address := p.address;
-- See if we can inherit addtional address tags from an interpolation.
-- These will become permanent.
FOR location IN
SELECT (address - 'interpolation'::text - 'housenumber'::text) as address
FROM place, planet_osm_ways w
WHERE place.osm_type = 'W' and place.address ? 'interpolation'
and place.geometry && p.geometry
and place.osm_id = w.id
and p.osm_id = any(w.nodes)
LOOP
result.address := location.address || result.address;
END LOOP;
END IF;
result.address := result.address - '_unlisted_place'::TEXT;
@@ -131,18 +145,6 @@ BEGIN
END IF;
IF parent_place_id is null and poi_osm_type = 'N' THEN
-- Is this node part of an interpolation?
FOR location IN
SELECT q.parent_place_id
FROM location_property_osmline q, planet_osm_ways x
WHERE q.linegeo && bbox and x.id = q.osm_id
and poi_osm_id = any(x.nodes)
LIMIT 1
LOOP
{% if debug %}RAISE WARNING 'Get parent from interpolation: %', location.parent_place_id;{% endif %}
RETURN location.parent_place_id;
END LOOP;
FOR location IN
SELECT p.place_id, p.osm_id, p.rank_search, p.address,
coalesce(p.centroid, ST_Centroid(p.geometry)) as centroid
@@ -626,10 +628,7 @@ BEGIN
{% if not disable_diff_updates %}
-- The following is not needed until doing diff updates, and slows the main index process down
IF NEW.osm_type = 'N' and NEW.rank_search > 28 THEN
-- might be part of an interpolation
result := osmline_reinsert(NEW.osm_id, NEW.geometry);
ELSEIF NEW.rank_address > 0 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
@@ -656,7 +655,7 @@ BEGIN
-- 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);
-- reparenting also for OSM Interpolation Lines (and for Tiger?)
update location_property_osmline set indexed_status = 2 where indexed_status = 0 and ST_DWithin(location_property_osmline.linegeo, NEW.geometry, diameter);
update location_property_osmline set indexed_status = 2 where indexed_status = 0 and startnumber is not null and ST_DWithin(location_property_osmline.linegeo, 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 address ? 'place');

View File

@@ -28,7 +28,8 @@ CREATE INDEX IF NOT EXISTS idx_placex_geometry_reverse_lookupPolygon
AND name is not null AND indexed_status = 0 AND linked_place_id is null;
CREATE INDEX IF NOT EXISTS idx_osmline_parent_place_id
ON location_property_osmline USING BTREE (parent_place_id) {{db.tablespace.search_index}};
ON location_property_osmline USING BTREE (parent_place_id) {{db.tablespace.search_index}}
WHERE parent_place_id is not null;
CREATE INDEX IF NOT EXISTS idx_osmline_parent_osm_id
ON location_property_osmline USING BTREE (osm_id) {{db.tablespace.search_index}};
@@ -48,6 +49,10 @@ CREATE INDEX IF NOT EXISTS idx_postcode_postcode
CREATE UNIQUE INDEX IF NOT EXISTS idx_place_osm_unique
ON place USING btree(osm_id, osm_type, class, type) {{db.tablespace.address_index}};
CREATE INDEX IF NOT EXISTS idx_place_interpolations
ON place USING gist(geometry) {{db.tablespace.address_index}}
WHERE osm_type = 'W' and address ? 'interpolation';
{% endif %}
-- Indices only needed for search.
@@ -62,8 +67,12 @@ CREATE INDEX IF NOT EXISTS idx_postcode_postcode
{% if postgres.has_index_non_key_column %}
CREATE INDEX IF NOT EXISTS idx_placex_housenumber
ON placex USING btree (parent_place_id) INCLUDE (housenumber) WHERE housenumber is not null;
ON placex USING btree (parent_place_id) {{db.tablespace.search_index}}
INCLUDE (housenumber)
WHERE housenumber is not null;
CREATE INDEX IF NOT EXISTS idx_osmline_parent_osm_id_with_hnr
ON location_property_osmline USING btree(parent_place_id) INCLUDE (startnumber, endnumber);
ON location_property_osmline USING btree(parent_place_id) {{db.tablespace.search_index}}
INCLUDE (startnumber, endnumber)
WHERE startnumber is not null;
{% endif %}
{% endif %}

View File

@@ -80,9 +80,9 @@ CREATE TABLE location_property_tiger (
parent_place_id BIGINT,
startnumber INTEGER,
endnumber INTEGER,
step SMALLINT,
partition SMALLINT,
linegeo GEOMETRY,
interpolationtype TEXT,
postcode TEXT);
GRANT SELECT ON location_property_tiger TO "{{config.DATABASE_WEBUSER}}";
@@ -95,10 +95,10 @@ CREATE TABLE location_property_osmline (
indexed_date TIMESTAMP,
startnumber INTEGER,
endnumber INTEGER,
step SMALLINT,
partition SMALLINT,
indexed_status SMALLINT,
linegeo GEOMETRY,
interpolationtype TEXT,
address HSTORE,
token_info JSONB, -- custom column for tokenizer use only
postcode TEXT,
@@ -106,7 +106,8 @@ CREATE TABLE location_property_osmline (
){{db.tablespace.search_data}};
CREATE UNIQUE INDEX idx_osmline_place_id ON location_property_osmline USING BTREE (place_id) {{db.tablespace.search_index}};
CREATE INDEX idx_osmline_geometry_sector ON location_property_osmline USING BTREE (geometry_sector) {{db.tablespace.address_index}};
CREATE INDEX idx_osmline_linegeo ON location_property_osmline USING GIST (linegeo) {{db.tablespace.search_index}};
CREATE INDEX idx_osmline_linegeo ON location_property_osmline USING GIST (linegeo) {{db.tablespace.search_index}}
WHERE startnumber is not null;
GRANT SELECT ON location_property_osmline TO "{{config.DATABASE_WEBUSER}}";
drop table IF EXISTS search_name;

View File

@@ -9,7 +9,7 @@
CREATE INDEX IF NOT EXISTS idx_location_property_tiger_parent_place_id_imp
ON location_property_tiger_import (parent_place_id)
{% if postgres.has_index_non_key_column %}
INCLUDE (startnumber, endnumber)
INCLUDE (startnumber, endnumber, step)
{% endif %}
{{db.tablespace.aux_index}};
CREATE UNIQUE INDEX IF NOT EXISTS idx_location_property_tiger_place_id_imp

View File

@@ -5,7 +5,15 @@
-- Copyright (C) 2022 by the Nominatim developer community.
-- For a full list of authors see the git log.
DROP TABLE IF EXISTS location_property_tiger_import;
CREATE TABLE location_property_tiger_import (linegeo GEOMETRY, place_id BIGINT, partition INTEGER, parent_place_id BIGINT, startnumber INTEGER, endnumber INTEGER, interpolationtype TEXT, postcode TEXT);
CREATE TABLE location_property_tiger_import (
linegeo GEOMETRY,
place_id BIGINT,
partition INTEGER,
parent_place_id BIGINT,
startnumber INTEGER,
endnumber INTEGER,
step SMALLINT,
postcode TEXT);
CREATE OR REPLACE FUNCTION tiger_line_import(linegeo GEOMETRY, in_startnumber INTEGER,
in_endnumber INTEGER, interpolationtype TEXT,
@@ -24,11 +32,12 @@ DECLARE
BEGIN
IF in_endnumber > in_startnumber THEN
startnumber = in_startnumber;
endnumber = in_endnumber;
startnumber := in_startnumber;
endnumber := in_endnumber;
ELSE
startnumber = in_endnumber;
endnumber = in_startnumber;
startnumber := in_endnumber;
endnumber := in_startnumber;
linegeo := ST_Reverse(linegeo);
END IF;
IF startnumber < 0 THEN
@@ -50,8 +59,10 @@ BEGIN
END IF;
-- Filter out really broken tiger data
IF numberrange > 0 AND (numberrange::float/stepsize::float > 500)
AND ST_length(linegeo)/(numberrange::float/stepsize::float) < 0.000001 THEN
IF numberrange > 0
and numberrange::float/stepsize::float > 500
and ST_length(linegeo)/(numberrange::float/stepsize::float) < 0.000001
THEN
RAISE WARNING 'Road too short for number range % to % (%)',startnumber,endnumber,
ST_length(linegeo)/(numberrange::float/stepsize::float);
RETURN 0;
@@ -74,8 +85,12 @@ BEGIN
END IF;
--insert street(line) into import table
insert into location_property_tiger_import (linegeo, place_id, partition, parent_place_id, startnumber, endnumber, interpolationtype, postcode)
values (linegeo, nextval('seq_place'), out_partition, out_parent_place_id, startnumber, endnumber, interpolationtype, in_postcode);
insert into location_property_tiger_import (linegeo, place_id, partition,
parent_place_id, startnumber, endnumber,
step, postcode)
values (linegeo, nextval('seq_place'), out_partition,
out_parent_place_id, startnumber, endnumber,
stepsize, in_postcode);
RETURN 1;
END;

View File

@@ -1,6 +1,6 @@
# just use the pgxs makefile
foreach(suffix ${PostgreSQL_ADDITIONAL_VERSIONS} "13" "12" "11" "10" "9.6" "9.5" "9.4" "9.3")
foreach(suffix ${PostgreSQL_ADDITIONAL_VERSIONS} "14" "13" "12" "11" "10" "9.6")
list(APPEND PG_CONFIG_HINTS
"/usr/pgsql-${suffix}/bin")
endforeach()

View File

@@ -216,3 +216,58 @@ def create_tiger_housenumber_index(conn, **_):
ON location_property_tiger
USING btree(parent_place_id)
INCLUDE (startnumber, endnumber) """)
@_migration(4, 0, 99, 1)
def create_interpolation_index_on_place(conn, **_):
""" Create idx_place_interpolations for lookup of interpolation lines
on updates.
"""
with conn.cursor() as cur:
cur.execute("""CREATE INDEX IF NOT EXISTS idx_place_interpolations
ON place USING gist(geometry)
WHERE osm_type = 'W' and address ? 'interpolation'""")
@_migration(4, 0, 99, 2)
def add_step_column_for_interpolation(conn, **_):
""" Add a new column 'step' to the interpolations table.
Also convers the data into the stricter format which requires that
startnumbers comply with the odd/even requirements.
"""
with conn.cursor() as cur:
# Mark invalid all interpolations with no intermediate numbers.
cur.execute("""UPDATE location_property_osmline SET startnumber = null
WHERE endnumber - startnumber <= 1 """)
# Align the start numbers where odd/even does not match.
cur.execute("""UPDATE location_property_osmline
SET startnumber = startnumber + 1,
linegeo = ST_LineSubString(linegeo,
1.0 / (endnumber - startnumber)::float,
1)
WHERE (interpolationtype = 'odd' and startnumber % 2 = 0)
or (interpolationtype = 'even' and startnumber % 2 = 1)
""")
# Mark invalid odd/even interpolations with no intermediate numbers.
cur.execute("""UPDATE location_property_osmline SET startnumber = null
WHERE interpolationtype in ('odd', 'even')
and endnumber - startnumber = 2""")
# Finally add the new column and populate it.
cur.execute("ALTER TABLE location_property_osmline ADD COLUMN step SMALLINT")
cur.execute("""UPDATE location_property_osmline
SET step = CASE WHEN interpolationtype = 'all'
THEN 1 ELSE 2 END
""")
@_migration(4, 0, 99, 3)
def add_step_column_for_tiger(conn, **_):
""" Add a new column 'step' to the tiger data table.
"""
with conn.cursor() as cur:
cur.execute("ALTER TABLE location_property_tiger ADD COLUMN step SMALLINT")
cur.execute("""UPDATE location_property_tiger
SET step = CASE WHEN interpolationtype = 'all'
THEN 1 ELSE 2 END
""")

View File

@@ -20,11 +20,11 @@ Version information for Nominatim.
# to 99 to make sure that the migration is applied when updating from a
# patch release to the next minor version. Patch releases usually shouldn't
# have migrations in them. When they are needed, then make sure that the
# migration can reapplied and set the migration version to the appropriate
# migration can be reapplied and set the migration version to the appropriate
# patch level when cherry-picking the commit with the migration.
#
# Released versions always have a database patch level of 0.
NOMINATIM_VERSION = (4, 0, 99, 1)
NOMINATIM_VERSION = (4, 0, 99, 4)
POSTGRESQL_REQUIRED_VERSION = (9, 5)
POSTGIS_REQUIRED_VERSION = (2, 2)

View File

@@ -10,7 +10,7 @@ Feature: Reverse geocoding
| way | place | house |
And result addresses contain
| house_number | road | postcode | country_code |
| 697 | Upper Kingston Road | 36067 | us |
| 707 | Upper Kingston Road | 36067 | us |
@Tiger
Scenario: No TIGER house number for zoom < 18

View File

@@ -16,23 +16,23 @@ Feature: Import of address interpolations
When importing
Then W1 expands to interpolation
| start | end | geometry |
| 2 | 6 | 1 1, 1 1.001 |
| 4 | 4 | 1 1.0005 |
Scenario: Backwards even two point interpolation line
Given the places
| osm | class | type | housenr | geometry |
| N1 | place | house | 2 | 1 1 |
| N2 | place | house | 6 | 1 1.001 |
| N2 | place | house | 8 | 1 1.003 |
And the places
| osm | class | type | addr+interpolation | geometry |
| W1 | place | houses | even | 1 1.001, 1 1 |
| W1 | place | houses | even | 1 1.003, 1 1 |
And the ways
| id | nodes |
| 1 | 2,1 |
When importing
Then W1 expands to interpolation
| start | end | geometry |
| 2 | 6 | 1 1, 1 1.001 |
| 4 | 6 | 1 1.001, 1 1.002 |
Scenario: Simple odd two point interpolation
Given the places
@@ -48,23 +48,23 @@ Feature: Import of address interpolations
When importing
Then W1 expands to interpolation
| start | end | geometry |
| 1 | 11 | 1 1, 1 1.001 |
| 3 | 9 | 1 1.0002, 1 1.0008 |
Scenario: Simple all two point interpolation
Given the places
| osm | class | type | housenr | geometry |
| N1 | place | house | 1 | 1 1 |
| N2 | place | house | 3 | 1 1.001 |
| N2 | place | house | 4 | 1 1.003 |
And the places
| osm | class | type | addr+interpolation | geometry |
| W1 | place | houses | all | 1 1, 1 1.001 |
| W1 | place | houses | all | 1 1, 1 1.003 |
And the ways
| id | nodes |
| 1 | 1,2 |
When importing
Then W1 expands to interpolation
| start | end | geometry |
| 1 | 3 | 1 1, 1 1.001 |
| 2 | 3 | 1 1.001, 1 1.002 |
Scenario: Even two point interpolation line with intermediate empty node
Given the places
@@ -80,7 +80,7 @@ Feature: Import of address interpolations
When importing
Then W1 expands to interpolation
| start | end | geometry |
| 2 | 10 | 1 1, 1 1.001, 1.001 1.001 |
| 4 | 8 | 1 1.0005, 1 1.001, 1.0005 1.001 |
Scenario: Even two point interpolation line with intermediate duplicated empty node
Given the places
@@ -96,7 +96,7 @@ Feature: Import of address interpolations
When importing
Then W1 expands to interpolation
| start | end | geometry |
| 2 | 10 | 1 1, 1 1.001, 1.001 1.001 |
| 4 | 8 | 1 1.0005, 1 1.001, 1.0005 1.001 |
Scenario: Simple even three point interpolation line
Given the places
@@ -113,8 +113,8 @@ Feature: Import of address interpolations
When importing
Then W1 expands to interpolation
| start | end | geometry |
| 2 | 10 | 1 1, 1 1.001 |
| 10 | 14 | 1 1.001, 1.001 1.001 |
| 4 | 8 | 1 1.00025, 1 1.00075 |
| 12 | 12 | 1.0005 1.001 |
Scenario: Simple even four point interpolation line
Given the places
@@ -132,9 +132,9 @@ Feature: Import of address interpolations
When importing
Then W1 expands to interpolation
| start | end | geometry |
| 2 | 10 | 1 1, 1 1.001 |
| 10 | 14 | 1 1.001, 1.001 1.001 |
| 14 | 18 | 1.001 1.001, 1.001 1.002 |
| 4 | 8 | 1 1.00025, 1 1.00075 |
| 12 | 12 | 1.0005 1.001 |
| 16 | 16 | 1.001 1.0015 |
Scenario: Reverse simple even three point interpolation line
Given the places
@@ -151,8 +151,8 @@ Feature: Import of address interpolations
When importing
Then W1 expands to interpolation
| start | end | geometry |
| 2 | 10 | 1 1, 1 1.001 |
| 10 | 14 | 1 1.001, 1.001 1.001 |
| 4 | 8 | 1 1.00025, 1 1.00075 |
| 12 | 12 | 1.0005 1.001 |
Scenario: Even three point interpolation line with odd center point
Given the places
@@ -169,8 +169,7 @@ Feature: Import of address interpolations
When importing
Then W1 expands to interpolation
| start | end | geometry |
| 2 | 7 | 1 1, 1 1.001 |
| 7 | 8 | 1 1.001, 1.001 1.001 |
| 4 | 6 | 1 1.0004, 1 1.0008 |
Scenario: Interpolation line with self-intersecting way
Given the places
@@ -187,9 +186,9 @@ Feature: Import of address interpolations
When importing
Then W1 expands to interpolation
| start | end | geometry |
| 2 | 6 | 0 0, 0 0.001 |
| 6 | 10 | 0 0.001, 0 0.002 |
| 6 | 10 | 0 0.001, 0 0.002 |
| 4 | 4 | 0 0.0005 |
| 8 | 8 | 0 0.0015 |
| 8 | 8 | 0 0.0015 |
Scenario: Interpolation line with self-intersecting way II
Given the places
@@ -205,7 +204,7 @@ Feature: Import of address interpolations
When importing
Then W1 expands to interpolation
| start | end | geometry |
| 2 | 6 | 0 0, 0 0.001 |
| 4 | 4 | 0 0.0005 |
Scenario: addr:street on interpolation way
Given the scene parallel-road
@@ -236,10 +235,10 @@ Feature: Import of address interpolations
| N4 | W3 |
Then W10 expands to interpolation
| parent_place_id | start | end |
| W2 | 2 | 6 |
| W2 | 4 | 4 |
Then W11 expands to interpolation
| parent_place_id | start | end |
| W3 | 12 | 16 |
| W3 | 14 | 14 |
When sending search query "16 Cloud Street"
Then results contain
| ID | osm_type | osm_id |
@@ -278,10 +277,10 @@ Feature: Import of address interpolations
| N4 | W3 |
Then W10 expands to interpolation
| parent_place_id | start | end |
| W2 | 2 | 6 |
| W2 | 4 | 4 |
Then W11 expands to interpolation
| parent_place_id | start | end |
| W3 | 12 | 16 |
| W3 | 14 | 14 |
When sending search query "16 Cloud Street"
Then results contain
| ID | osm_type | osm_id |
@@ -306,8 +305,8 @@ Feature: Import of address interpolations
When importing
Then W1 expands to interpolation
| start | end | geometry |
| 2 | 6 | 144.9629794 -37.7630755, 144.9630541 -37.7628174 |
| 6 | 10 | 144.9630541 -37.7628174, 144.9632341 -37.76163 |
| 4 | 4 | 144.963016 -37.762946 |
| 8 | 8 | 144.963144 -37.7622237 |
Scenario: Place with missing address information
Given the grid
@@ -326,7 +325,7 @@ Feature: Import of address interpolations
When importing
Then W1 expands to interpolation
| start | end | geometry |
| 23 | 29 | 1,2,3 |
| 25 | 27 | 0.000016 0,0.00002 0,0.000033 0 |
Scenario: Ways without node entries are ignored
Given the places
@@ -348,7 +347,7 @@ Feature: Import of address interpolations
Given the places
| osm | class | type | housenr | geometry |
| N1 | place | house | 0 | 1 1 |
| N2 | place | house | 2 | 1 1.001 |
| N2 | place | house | 10 | 1 1.001 |
And the places
| osm | class | type | addr+interpolation | geometry |
| W1 | place | houses | even | 1 1, 1 1.001 |
@@ -358,9 +357,8 @@ Feature: Import of address interpolations
When importing
Then W1 expands to interpolation
| start | end | geometry |
| 0 | 2 | 1 1, 1 1.001 |
| 2 | 8 | 1 1.0002, 1 1.0008 |
When sending jsonv2 reverse coordinates 1,1
Then results contain
| ID | osm_type | osm_id | type | display_name |
| 0 | way | 1 | house | 0 |
| 0 | node | 1 | house | 0 |

View File

@@ -26,7 +26,7 @@ Feature: Update of address interpolations
| N2 | W2 |
And W10 expands to interpolation
| parent_place_id | start | end |
| W2 | 2 | 6 |
| W2 | 4 | 4 |
Scenario: addr:street added to interpolation
Given the scene parallel-road
@@ -51,7 +51,7 @@ Feature: Update of address interpolations
| N2 | W2 |
And W10 expands to interpolation
| parent_place_id | start | end |
| W2 | 2 | 6 |
| W2 | 4 | 4 |
When updating places
| osm | class | type | addr+interpolation | street | geometry |
| W10 | place | houses | even | Cloud Street | :w-middle |
@@ -61,7 +61,7 @@ Feature: Update of address interpolations
| N2 | W3 |
And W10 expands to interpolation
| parent_place_id | start | end |
| W3 | 2 | 6 |
| W3 | 4 | 4 |
Scenario: addr:street added to housenumbers
Given the scene parallel-road
@@ -86,7 +86,7 @@ Feature: Update of address interpolations
| N2 | W2 |
And W10 expands to interpolation
| parent_place_id | start | end |
| W2 | 2 | 6 |
| W2 | 4 | 4 |
When updating places
| osm | class | type | street | housenr | geometry |
| N1 | place | house | Cloud Street| 2 | :n-middle-w |
@@ -97,7 +97,7 @@ Feature: Update of address interpolations
| N2 | W3 |
And W10 expands to interpolation
| parent_place_id | start | end |
| W3 | 2 | 6 |
| W3 | 4 | 4 |
Scenario: interpolation tag removed
Given the scene parallel-road
@@ -122,7 +122,7 @@ Feature: Update of address interpolations
| N2 | W2 |
And W10 expands to interpolation
| parent_place_id | start | end |
| W2 | 2 | 6 |
| W2 | 4 | 4 |
When marking for delete W10
Then W10 expands to no interpolation
And placex contains
@@ -152,7 +152,7 @@ Feature: Update of address interpolations
| N2 | W2 |
And W10 expands to interpolation
| parent_place_id | start | end |
| W2 | 2 | 6 |
| W2 | 4 | 4 |
When updating places
| osm | class | type | name | geometry |
| W3 | highway | unclassified | Cloud Street | :w-south |
@@ -162,7 +162,7 @@ Feature: Update of address interpolations
| N2 | W3 |
And W10 expands to interpolation
| parent_place_id | start | end |
| W3 | 2 | 6 |
| W3 | 4 | 4 |
Scenario: referenced road deleted
Given the scene parallel-road
@@ -187,7 +187,7 @@ Feature: Update of address interpolations
| N2 | W3 |
And W10 expands to interpolation
| parent_place_id | start | end |
| W3 | 2 | 6 |
| W3 | 4 | 4 |
When marking for delete W3
Then placex contains
| object | parent_place_id |
@@ -195,7 +195,7 @@ Feature: Update of address interpolations
| N2 | W2 |
And W10 expands to interpolation
| parent_place_id | start | end |
| W2 | 2 | 6 |
| W2 | 4 | 4 |
Scenario: building becomes interpolation
Given the scene building-with-parallel-streets
@@ -222,7 +222,7 @@ Feature: Update of address interpolations
Then placex has no entry for W1
And W1 expands to interpolation
| parent_place_id | start | end |
| W2 | 2 | 6 |
| W2 | 4 | 4 |
Scenario: interpolation becomes building
Given the scene building-with-parallel-streets
@@ -243,7 +243,7 @@ Feature: Update of address interpolations
Then placex has no entry for W1
And W1 expands to interpolation
| parent_place_id | start | end |
| W2 | 2 | 6 |
| W2 | 4 | 4 |
When updating places
| osm | class | type | housenr | geometry |
| W1 | place | house | 3 | :w-building |
@@ -273,7 +273,7 @@ Feature: Update of address interpolations
| W1 | place | houses | even | Cloud Street| :w-north |
Then W1 expands to interpolation
| parent_place_id | start | end |
| W2 | 2 | 6 |
| W2 | 4 | 4 |
Scenario: housenumber added in middle of interpolation
Given the grid
@@ -294,15 +294,15 @@ Feature: Update of address interpolations
| N5 | place | house | 10 |
When importing
Then W2 expands to interpolation
| parent_place_id | start | end | geometry |
| W1 | 2 | 10 | 3,4,5 |
| parent_place_id | start | end |
| W1 | 4 | 8 |
When updating places
| osm | class | type | housenr |
| N4 | place | house | 6 |
Then W2 expands to interpolation
| parent_place_id | start | end | geometry |
| W1 | 2 | 6 | 3,4 |
| W1 | 6 | 10 | 4,5 |
| parent_place_id | start | end |
| W1 | 4 | 4 |
| W1 | 8 | 8 |
@Fail
Scenario: housenumber removed in middle of interpolation
@@ -325,13 +325,13 @@ Feature: Update of address interpolations
| N5 | place | house | 10 |
When importing
Then W2 expands to interpolation
| parent_place_id | start | end | geometry |
| W1 | 2 | 6 | 3,4 |
| W1 | 6 | 10 | 4,5 |
| parent_place_id | start | end |
| W1 | 4 | 4 |
| W1 | 8 | 8 |
When marking for delete N4
Then W2 expands to interpolation
| parent_place_id | start | end | geometry |
| W1 | 2 | 10 | 3,4,5 |
| parent_place_id | start | end |
| W1 | 4 | 8 |
Scenario: Change the start housenumber
Given the grid
@@ -352,12 +352,12 @@ Feature: Update of address interpolations
| N4 | place | house | 6 |
When importing
Then W2 expands to interpolation
| parent_place_id | start | end | geometry |
| W1 | 2 | 6 | 3,4 |
| parent_place_id | start | end |
| W1 | 4 | 4 |
When updating places
| osm | class | type | housenr |
| N4 | place | house | 8 |
Then W2 expands to interpolation
| parent_place_id | start | end | geometry |
| W1 | 2 | 8 | 3,4 |
| parent_place_id | start | end |
| W1 | 4 | 6 |

View File

@@ -91,33 +91,4 @@ class LibTest extends \PHPUnit\Framework\TestCase
$this->assertEquals($sQuery, $aRes[0]);
}
}
private function closestHouseNumberEvenOddOther($startnumber, $endnumber, $fraction, $aExpected)
{
foreach (array('even', 'odd', 'other') as $itype) {
$this->assertEquals(
$aExpected[$itype],
closestHouseNumber(array(
'startnumber' => $startnumber,
'endnumber' => $endnumber,
'fraction' => $fraction,
'interpolationtype' => $itype
)),
"$startnumber => $endnumber, $fraction, $itype"
);
}
}
public function testClosestHouseNumber()
{
$this->closestHouseNumberEvenOddOther(50, 100, 0.5, array('even' => 76, 'odd' => 75, 'other' => 75));
// upper bound
$this->closestHouseNumberEvenOddOther(50, 100, 1.5, array('even' => 100, 'odd' => 100, 'other' => 100));
// lower bound
$this->closestHouseNumberEvenOddOther(50, 100, -0.5, array('even' => 50, 'odd' => 50, 'other' => 50));
// fraction 0
$this->closestHouseNumberEvenOddOther(50, 100, 0, array('even' => 50, 'odd' => 51, 'other' => 50));
// start == end
$this->closestHouseNumberEvenOddOther(50, 50, 0.5, array('even' => 50, 'odd' => 50, 'other' => 50));
}
}