]> git.openstreetmap.org Git - nominatim.git/commitdiff
Merge remote-tracking branch 'upstream/master'
authorSarah Hoffmann <lonvia@denofr.de>
Sun, 18 Oct 2020 07:49:27 +0000 (09:49 +0200)
committerSarah Hoffmann <lonvia@denofr.de>
Sun, 18 Oct 2020 07:49:27 +0000 (09:49 +0200)
lib/ReverseGeocode.php
settings/address-levels.json
sql/functions/placex_triggers.sql
sql/functions/postcode_triggers.sql
sql/functions/utils.sql
sql/partition-functions.src.sql
test/bdd/db/import/addressing.feature
test/bdd/db/import/rank_computation.feature
website/reverse.php

index 56596b032936ffef30b94941d2a40a0c9ef76961..032f442eb03b48051e14ee977efc54883373c3bd 100644 (file)
@@ -54,6 +54,7 @@ class ReverseGeocode
      */
     protected function lookupInterpolation($sPointSQL, $fSearchDiam)
     {
+        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,';
@@ -62,6 +63,7 @@ class ReverseGeocode
         $sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', linegeo, '.$fSearchDiam.')';
         $sSQL .= ' and indexed_status = 0 and startnumber is not NULL ';
         $sSQL .= ' ORDER BY distance ASC limit 1';
+        Debug::printSQL($sSQL);
 
         return $this->oDB->getRow(
             $sSQL,
@@ -88,16 +90,20 @@ class ReverseGeocode
 
     protected function lookupInCountry($sPointSQL, $iMaxRank)
     {
+        Debug::newFunction('lookupInCountry');
         // searches for polygon in table country_osm_grid which contains the searchpoint
         // and searches for the nearest place node to the searchpoint in this polygon
         $sSQL = 'SELECT country_code FROM country_osm_grid';
         $sSQL .= ' WHERE ST_CONTAINS(geometry, '.$sPointSQL.') LIMIT 1';
+        Debug::printSQL($sSQL);
 
         $sCountryCode = $this->oDB->getOne(
             $sSQL,
             null,
             'Could not determine country polygon containing the point.'
         );
+        Debug::printVar('Country code', $sCountryCode);
+
         if ($sCountryCode) {
             if ($iMaxRank > 4) {
                 // look for place nodes with the given country code
@@ -115,9 +121,11 @@ class ReverseGeocode
                 $sSQL .= 'WHERE distance <= reverse_place_diameter(rank_search)';
                 $sSQL .= ' ORDER BY rank_search DESC, distance ASC';
                 $sSQL .= ' LIMIT 1';
+                Debug::printSQL($sSQL);
 
-                if (CONST_Debug) var_dump($sSQL);
                 $aPlace = $this->oDB->getRow($sSQL, null, 'Could not determine place node.');
+                Debug::printVar('Country node', $aPlace);
+
                 if ($aPlace) {
                     return new Result($aPlace['place_id']);
                 }
@@ -131,9 +139,10 @@ class ReverseGeocode
             $sSQL .= ' AND class in (\'boundary\',  \'place\')';
             $sSQL .= ' AND linked_place_id is null';
             $sSQL .= ' ORDER BY distance ASC';
+            Debug::printSQL($sSQL);
 
-            if (CONST_Debug) var_dump($sSQL);
             $aPlace = $this->oDB->getRow($sSQL, null, 'Could not determine place node.');
+            Debug::printVar('Country place', $aPlace);
             if ($aPlace) {
                 return new Result($aPlace['place_id']);
             }
@@ -156,6 +165,7 @@ class ReverseGeocode
      */
     protected function lookupPolygon($sPointSQL, $iMaxRank)
     {
+        Debug::newFunction('lookupPolygon');
         // polygon search begins at suburb-level
         if ($iMaxRank > 25) $iMaxRank = 25;
         // no polygon search over country-level
@@ -173,8 +183,10 @@ class ReverseGeocode
         $sSQL .= ' ORDER BY rank_address DESC LIMIT 50 ) as a';
         $sSQL .= ' WHERE ST_CONTAINS(geometry, '.$sPointSQL.' )';
         $sSQL .= ' ORDER BY rank_address DESC LIMIT 1';
+        Debug::printSQL($sSQL);
 
         $aPoly = $this->oDB->getRow($sSQL, null, 'Could not determine polygon containing the point.');
+        Debug::printVar('Polygon result', $aPoly);
 
         if ($aPoly) {
         // if a polygon is found, search for placenodes begins ...
@@ -206,11 +218,12 @@ class ReverseGeocode
                 $sSQL .= ' AND distance <= reverse_place_diameter(rank_search)';
                 $sSQL .= ' ORDER BY distance ASC, rank_search DESC';
                 $sSQL .= ' LIMIT 1';
+                Debug::printSQL($sSQL);
 
-                if (CONST_Debug) var_dump($sSQL);
-                $aPlacNode = $this->oDB->getRow($sSQL, null, 'Could not determine place node.');
-                if ($aPlacNode) {
-                    return $aPlacNode;
+                $aPlaceNode = $this->oDB->getRow($sSQL, null, 'Could not determine place node.');
+                Debug::printVar('Nearest place node', $aPlaceNode);
+                if ($aPlaceNode) {
+                    return $aPlaceNode;
                 }
             }
         }
@@ -228,6 +241,7 @@ class ReverseGeocode
 
     public function lookupPoint($sPointSQL, $bDoInterpolation = true)
     {
+        Debug::newFunction('lookupPoint');
         // starts if the search is on POI or street level,
         // searches for the nearest POI or street,
         // if a street is found and a POI is searched for,
@@ -257,10 +271,11 @@ class ReverseGeocode
             $sSQL .= ' and (ST_GeometryType(geometry) not in (\'ST_Polygon\',\'ST_MultiPolygon\') ';
             $sSQL .= ' OR ST_DWithin('.$sPointSQL.', centroid, '.$fSearchDiam.'))';
             $sSQL .= ' ORDER BY distance ASC limit 1';
-            if (CONST_Debug) var_dump($sSQL);
+            Debug::printSQL($sSQL);
+
             $aPlace = $this->oDB->getRow($sSQL, null, 'Could not determine closest place.');
 
-            if (CONST_Debug) var_dump($aPlace);
+            Debug::printVar('POI/street level result', $aPlace);
             if ($aPlace) {
                 $iPlaceID = $aPlace['place_id'];
                 $oResult = new Result($iPlaceID);
@@ -280,6 +295,7 @@ class ReverseGeocode
                 }
 
                 $aHouse = $this->lookupInterpolation($sPointSQL, $fDistance);
+                Debug::printVar('Interpolation result', $aPlace);
 
                 if ($aHouse) {
                     $oResult = new Result($aHouse['place_id'], Result::TABLE_OSMLINE);
@@ -306,10 +322,12 @@ class ReverseGeocode
                     $sSQL .= ' and class not in (\'boundary\')';
                     $sSQL .= ' and indexed_status = 0 and linked_place_id is null';
                     $sSQL .= ' ORDER BY distance ASC limit 1';
-                    if (CONST_Debug) var_dump($sSQL);
+                    Debug::printSQL($sSQL);
+
                     $aStreet = $this->oDB->getRow($sSQL, null, 'Could not determine closest place.');
+                    Debug::printVar('Closest POI result', $aStreet);
+
                     if ($aStreet) {
-                        if (CONST_Debug) var_dump($aStreet);
                         $oResult = new Result($aStreet['place_id']);
                     }
                 }
@@ -327,10 +345,12 @@ class ReverseGeocode
                     $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';
-                    if (CONST_Debug) var_dump($sSQL);
+                    Debug::printSQL($sSQL);
+
                     $aPlaceTiger = $this->oDB->getRow($sSQL, null, 'Could not determine closest Tiger place.');
+                    Debug::printVar('Tiger house number result', $aPlaceTiger);
+
                     if ($aPlaceTiger) {
-                        if (CONST_Debug) var_dump('found Tiger housenumber', $aPlaceTiger);
                         $oResult = new Result($aPlaceTiger['place_id'], Result::TABLE_TIGER);
                         $oResult->iHouseNumber = closestHouseNumber($aPlaceTiger);
                     }
@@ -343,6 +363,8 @@ class ReverseGeocode
             // lower than street level ($iMaxRank < 26 )
             $oResult = $this->lookupLargeArea($sPointSQL, $iMaxRank);
         }
+
+        Debug::printVar('Final result', $oResult);
         return $oResult;
     }
 }
index 090c9365ea7ef15e556bc08b3e6ba1fa0bab7fe6..574e0dd041ef590fe3e100b1c7258d0f3743e655 100644 (file)
           "administrative10" : 24
       }
   }
+},
+{ "countries" : ["ru"],
+  "tags" : {
+      "place" : {
+          "municipality" : 18
+      },
+      "boundary" : {
+          "administrative7" : [13, 0],
+          "administrative8" : 14
+      }
+  }
 }
 ]
 
index 1f664a4a5531924a68e16709b8de4f33373af5c9..9bc3469b10d31e420f31292f07dcf96a7d974762 100644 (file)
@@ -259,21 +259,16 @@ CREATE OR REPLACE FUNCTION insert_addresslines(obj_place_id BIGINT,
                                                OUT nameaddress_vector INT[])
   AS $$
 DECLARE
-  current_rank_address INTEGER := 0;
-  location_distance FLOAT := 0;
-  location_parent GEOMETRY := NULL;
-  parent_place_id_rank SMALLINT := 0;
+  address_havelevel BOOLEAN[];
 
   location_isaddress BOOLEAN;
-
-  address_havelevel BOOLEAN[];
-  location_keywords INT[];
+  current_boundary GEOMETRY := NULL;
+  current_node_area GEOMETRY := NULL;
 
   location RECORD;
   addr_item RECORD;
 
   isin_tokens INT[];
-  isin TEXT[];
 BEGIN
   parent_place_id := 0;
   nameaddress_vector := '{}'::int[];
@@ -302,77 +297,71 @@ BEGIN
   END IF;
 
   ---- now compute the address terms
-  FOR i IN 1..28 LOOP
+  FOR i IN 1..maxrank LOOP
     address_havelevel[i] := false;
   END LOOP;
 
   FOR location IN
-    SELECT * FROM getNearFeatures(partition, geometry, maxrank, isin_tokens)
+    SELECT * FROM getNearFeatures(partition, geometry, maxrank)
+    ORDER BY rank_address, isin_tokens && keywords desc, isguess asc,
+             distance *
+               CASE WHEN rank_address = 16 AND rank_search = 15 THEN 0.2
+                    WHEN rank_address = 16 AND rank_search = 16 THEN 0.25
+                    WHEN rank_address = 16 AND rank_search = 18 THEN 0.5
+                    ELSE 1 END ASC
   LOOP
-    IF location.rank_address != current_rank_address THEN
-      current_rank_address := location.rank_address;
-      IF location.isguess THEN
-        location_distance := location.distance * 1.5;
-      ELSE
-        IF location.rank_address <= 12 THEN
-          -- for county and above, if we have an area consider that exact
-          -- (It would be nice to relax the constraint for places close to
-          --  the boundary but we'd need the exact geometry for that. Too
-          --  expensive.)
-          location_distance = 0;
-        ELSE
-          -- Below county level remain slightly fuzzy.
-          location_distance := location.distance * 0.5;
-        END IF;
+    -- Ignore all place nodes that do not fit in a lower level boundary.
+    CONTINUE WHEN location.isguess
+                  and current_boundary is not NULL
+                  and not ST_Contains(current_boundary, location.centroid);
+
+    -- If this is the first item in the rank, then assume it is the address.
+    location_isaddress := not address_havelevel[location.rank_address];
+
+    -- Further sanity checks to ensure that the address forms a sane hierarchy.
+    IF location_isaddress THEN
+      IF location.isguess and current_node_area is not NULL THEN
+        location_isaddress := ST_Contains(current_node_area, location.centroid);
+      END IF;
+      IF not location.isguess and current_boundary is not NULL
+         and location.rank_address != 11 AND location.rank_address != 5 THEN
+        location_isaddress := ST_Contains(current_boundary, location.centroid);
       END IF;
-    ELSE
-      CONTINUE WHEN location.keywords <@ location_keywords;
     END IF;
 
-    IF location.distance < location_distance OR NOT location.isguess THEN
-      location_keywords := location.keywords;
+    IF location_isaddress THEN
+      address_havelevel[location.rank_address] := true;
+      parent_place_id := location.place_id;
 
-      location_isaddress := NOT address_havelevel[location.rank_address];
-      --DEBUG: RAISE WARNING 'should be address: %, is guess: %, rank: %', location_isaddress, location.isguess, location.rank_address;
-      IF location_isaddress AND location.isguess AND location_parent IS NOT NULL THEN
-          location_isaddress := ST_Contains(location_parent, location.centroid);
+      -- Set postcode if we have one.
+      -- (Returned will be the highest ranking one.)
+      IF location.postcode is not NULL THEN
+        postcode = location.postcode;
       END IF;
 
-      --DEBUG: RAISE WARNING '% isaddress: %', location.place_id, location_isaddress;
-      -- Add it to the list of search terms
-      IF NOT %REVERSE-ONLY% THEN
-          nameaddress_vector := array_merge(nameaddress_vector,
-                                            location.keywords::integer[]);
-      END IF;
-
-      INSERT INTO place_addressline (place_id, address_place_id, fromarea,
-                                     isaddress, distance, cached_rank_address)
-        VALUES (obj_place_id, location.place_id, true,
-                location_isaddress, location.distance, location.rank_address);
-
-      IF location_isaddress THEN
-        -- add postcode if we have one
-        -- (If multiple postcodes are available, we end up with the highest ranking one.)
-        IF location.postcode is not null THEN
-            postcode = location.postcode;
-        END IF;
-
-        address_havelevel[location.rank_address] := true;
-        -- add a hack against postcode ranks
-        IF NOT location.isguess
-           AND location.rank_address != 11 AND location.rank_address != 5
-        THEN
+      -- Recompute the areas we need for hierarchy sanity checks.
+      IF location.rank_address != 11 AND location.rank_address != 5 THEN
+        IF location.isguess THEN
+          current_node_area := place_node_fuzzy_area(location.centroid,
+                                                     location.rank_search);
+        ELSE
+          current_node_area := NULL;
           SELECT p.geometry FROM placex p
-            WHERE p.place_id = location.place_id INTO location_parent;
-        END IF;
-
-        IF location.rank_address > parent_place_id_rank THEN
-          parent_place_id = location.place_id;
-          parent_place_id_rank = location.rank_address;
+              WHERE p.place_id = location.place_id INTO current_boundary;
         END IF;
       END IF;
     END IF;
 
+    -- Add it to the list of search terms
+    IF NOT %REVERSE-ONLY% THEN
+      nameaddress_vector := array_merge(nameaddress_vector,
+                                        location.keywords::integer[]);
+    END IF;
+
+    INSERT INTO place_addressline (place_id, address_place_id, fromarea,
+                                     isaddress, distance, cached_rank_address)
+        VALUES (obj_place_id, location.place_id, not location.isguess,
+                location_isaddress, location.distance, location.rank_address);
   END LOOP;
 END;
 $$
@@ -518,7 +507,7 @@ BEGIN
       FROM placex
       WHERE osm_type = 'R' and class = 'boundary' and type = 'administrative'
             and admin_level < in_level
-            and geometry && geom and ST_Covers(geometry, geom)
+            and geometry ~ geom and _ST_Covers(geometry, geom)
       ORDER BY admin_level desc LIMIT 1;
   END IF;
 
@@ -620,6 +609,7 @@ BEGIN
   IF NEW.class = 'boundary' and NEW.type = 'administrative'
      and NEW.osm_type = 'R' and NEW.rank_address > 0
   THEN
+    -- First, check that admin boundaries do not overtake each other rank-wise.
     parent_address_level := get_parent_address_level(NEW.centroid, NEW.admin_level);
     IF parent_address_level >= NEW.rank_address THEN
       IF parent_address_level >= 24 THEN
@@ -628,12 +618,24 @@ BEGIN
         NEW.rank_address := parent_address_level + 2;
       END IF;
     END IF;
-  -- If a place node is contained in a admin boundary with the same address level
-  -- and has not been linked, then make the node a subpart by increasing the
-  -- address rank (city level and above).
+    -- Second check that the boundary is not completely contained in a
+    -- place area with a higher address rank
+    FOR location IN
+      SELECT rank_address FROM placex
+      WHERE class = 'place' and rank_address < 24
+            and rank_address > NEW.rank_address
+            and geometry && NEW.geometry
+            and ST_Relate(geometry, NEW.geometry, 'T*T***FF*') -- contains but not equal
+      ORDER BY rank_address desc LIMIT 1
+    LOOP
+        NEW.rank_address := location.rank_address + 2;
+    END LOOP;
   ELSEIF NEW.class = 'place' and NEW.osm_type = 'N'
      and NEW.rank_address between 16 and 23
   THEN
+    -- If a place node is contained in a admin boundary with the same address level
+    -- and has not been linked, then make the node a subpart by increasing the
+    -- address rank (city level and above).
     FOR location IN
         SELECT rank_address FROM placex
         WHERE osm_type = 'R' and class = 'boundary' and type = 'administrative'
@@ -799,7 +801,8 @@ BEGIN
           IF NEW.rank_search <= 25 and NEW.rank_address > 0 THEN
             result := add_location(NEW.place_id, NEW.country_code, NEW.partition,
                                    name_vector, NEW.rank_search, NEW.rank_address,
-                                   upper(trim(NEW.address->'postcode')), NEW.geometry);
+                                   upper(trim(NEW.address->'postcode')), NEW.geometry,
+                                   NEW.centroid);
             --DEBUG: RAISE WARNING 'Place added to location table';
           END IF;
 
@@ -906,8 +909,9 @@ BEGIN
   END IF;
 
   SELECT * FROM insert_addresslines(NEW.place_id, NEW.partition,
-                                    CASE WHEN NEW.rank_address = 0
-                                      THEN NEW.rank_search ELSE NEW.rank_address END,
+                                    CASE WHEN NEW.rank_address = 0 THEN NEW.rank_search
+                                         WHEN NEW.rank_address > 25 THEN 25::smallint
+                                         ELSE NEW.rank_address END,
                                     NEW.address,
                                     CASE WHEN NEW.rank_search >= 26
                                              AND NEW.rank_search < 30
@@ -929,7 +933,7 @@ BEGIN
   IF NEW.name IS NOT NULL THEN
 
     IF NEW.rank_search <= 25 and NEW.rank_address > 0 THEN
-      result := add_location(NEW.place_id, NEW.country_code, NEW.partition, name_vector, NEW.rank_search, NEW.rank_address, upper(trim(NEW.address->'postcode')), NEW.geometry);
+      result := add_location(NEW.place_id, NEW.country_code, NEW.partition, name_vector, NEW.rank_search, NEW.rank_address, upper(trim(NEW.address->'postcode')), NEW.geometry, NEW.centroid);
       --DEBUG: RAISE WARNING 'added to location (full)';
     END IF;
 
index 96788d65ab2d6a9157d2ed377969bb9589402a40..515b76664ad4cf6de31f52c7fed1f121485e849f 100644 (file)
@@ -27,8 +27,8 @@ BEGIN
     NEW.parent_place_id = 0;
     FOR location IN
       SELECT place_id
-        FROM getNearFeatures(partition, NEW.geometry, NEW.rank_search, '{}'::int[])
-        WHERE NOT isguess ORDER BY rank_address DESC LIMIT 1
+        FROM getNearFeatures(partition, NEW.geometry, NEW.rank_search)
+        WHERE NOT isguess ORDER BY rank_address DESC, distance asc LIMIT 1
     LOOP
         NEW.parent_place_id = location.place_id;
     END LOOP;
index ae841bf25d23346b9780a3a12e4ee93526877f95..0a49eef52fc16ee1068642e73ac337880e2d1ed1 100644 (file)
@@ -272,21 +272,27 @@ END;
 $$
 LANGUAGE plpgsql;
 
-CREATE OR REPLACE FUNCTION near_feature_rank_distance(rank_search INTEGER)
-  RETURNS FLOAT
+-- Create a bounding box with an extent computed from the radius (in meters)
+-- which in turn is derived from the given search rank.
+CREATE OR REPLACE FUNCTION place_node_fuzzy_area(geom GEOMETRY, rank_search INTEGER)
+  RETURNS GEOMETRY
   AS $$
+DECLARE
+  radius FLOAT := 500;
 BEGIN
   IF rank_search <= 16 THEN -- city
-    RETURN 15000;
+    radius := 15000;
   ELSIF rank_search <= 18 THEN -- town
-    RETURN 4000;
+    radius := 4000;
   ELSIF rank_search <= 19 THEN -- village
-    RETURN 2000;
+    radius := 2000;
   ELSIF rank_search  <= 20 THEN -- hamlet
-    RETURN 1000;
+    radius := 1000;
   END IF;
 
-  RETURN 500;
+  RETURN ST_Envelope(ST_Collect(
+                     ST_Project(geom, radius, 0.785398)::geometry,
+                     ST_Project(geom, radius, 3.9269908)::geometry));
 END;
 $$
 LANGUAGE plpgsql IMMUTABLE;
@@ -295,13 +301,12 @@ LANGUAGE plpgsql IMMUTABLE;
 CREATE OR REPLACE FUNCTION add_location(place_id BIGINT, country_code varchar(2),
                                         partition INTEGER, keywords INTEGER[],
                                         rank_search INTEGER, rank_address INTEGER,
-                                        in_postcode TEXT, geometry GEOMETRY)
+                                        in_postcode TEXT, geometry GEOMETRY,
+                                        centroid GEOMETRY)
   RETURNS BOOLEAN
   AS $$
 DECLARE
   locationid INTEGER;
-  centroid GEOMETRY;
-  radius FLOAT;
   secgeo GEOMETRY;
   postcode TEXT;
 BEGIN
@@ -314,21 +319,13 @@ BEGIN
   END IF;
 
   IF ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon') THEN
-    centroid := ST_Centroid(geometry);
-
     FOR secgeo IN select split_geometry(geometry) AS geom LOOP
       PERFORM insertLocationAreaLarge(partition, place_id, country_code, keywords, rank_search, rank_address, false, postcode, centroid, secgeo);
     END LOOP;
 
   ELSEIF ST_GeometryType(geometry) = 'ST_Point' THEN
-    radius := near_feature_rank_distance(rank_search);
-    --DEBUG: RAISE WARNING 'adding % radius %', place_id, radius;
-
-    -- Create a bounding box with an extent computed from the radius (in meters).
-    secgeo := ST_Envelope(ST_Collect(
-                            ST_Project(geometry, radius, 0.785398)::geometry,
-                            ST_Project(geometry, radius, 3.9269908)::geometry));
-    PERFORM insertLocationAreaLarge(partition, place_id, country_code, keywords, rank_search, rank_address, true, postcode, geometry, secgeo);
+    secgeo := place_node_fuzzy_area(geometry, rank_search);
+    PERFORM insertLocationAreaLarge(partition, place_id, country_code, keywords, rank_search, rank_address, true, postcode, centroid, secgeo);
 
   END IF;
 
index e2342a9c1aa5e67035f3bb822b718771fb346b6e..97520f99b8441d5fec6f61248afa3572a4d3cb00 100644 (file)
@@ -32,7 +32,7 @@ BEGIN
 END
 $$ LANGUAGE plpgsql IMMUTABLE;
 
-create or replace function getNearFeatures(in_partition INTEGER, feature GEOMETRY, maxrank INTEGER, isin_tokens INT[]) RETURNS setof nearfeaturecentr AS $$
+create or replace function getNearFeatures(in_partition INTEGER, feature GEOMETRY, maxrank INTEGER) RETURNS setof nearfeaturecentr AS $$
 DECLARE
   r nearfeaturecentr%rowtype;
 BEGIN
@@ -46,13 +46,6 @@ BEGIN
         AND is_relevant_geometry(ST_Relate(geometry, feature), ST_GeometryType(feature))
         AND rank_address < maxrank
       GROUP BY place_id, keywords, rank_address, rank_search, isguess, postcode, centroid
-      ORDER BY rank_address, isin_tokens && keywords desc, isguess asc,
-        ST_Distance(feature, centroid) *
-          CASE 
-               WHEN rank_address = 16 AND rank_search = 15 THEN 0.2 -- capital city
-               WHEN rank_address = 16 AND rank_search = 16 THEN 0.25 -- city
-               WHEN rank_address = 16 AND rank_search = 17 THEN 0.5 -- town
-               ELSE 1 END ASC -- everything else
     LOOP
       RETURN NEXT r;
     END LOOP;
index 0d4798e8ec95d7ba9e4ad41263280fb70a16be6d..52f966d807ffb38c140a764e301f05c05965d4a0 100644 (file)
@@ -2,6 +2,138 @@
 Feature: Address computation
     Tests for filling of place_addressline
 
+    Scenario: place nodes are added to the address when they are close enough
+        Given the 0.002 grid
+            | 2 |  |  |  |  |  | 1 |  | 3 |
+        And the named places
+            | osm | class | type     | geometry |
+            | N1  | place | square   | 1 |
+            | N2  | place | hamlet   | 2 |
+            | N3  | place | hamlet   | 3 |
+        When importing
+        Then place_addressline contains
+            | object | address | fromarea |
+            | N1     | N3      | False |
+        Then place_addressline doesn't contain
+            | object | address |
+            | N1     | N2      |
+
+    Scenario: given two place nodes, the closer one wins for the address
+        Given the grid
+            | 2 |  |  | 1 |  | 3 |
+        And the named places
+            | osm | class | type     | geometry |
+            | N1  | place | square   | 1 |
+            | N2  | place | hamlet   | 2 |
+            | N3  | place | hamlet   | 3 |
+        When importing
+        Then place_addressline contains
+            | object | address | fromarea | isaddress |
+            | N1     | N3      | False    | True |
+            | N1     | N2      | False    | False |
+
+    Scenario: boundaries around the place are added to the address
+        Given the grid
+            | 1 |    | 4 | | 7 | 10 |
+            | 2 |    | 5 | | 8 | 11 |
+            |   |    |   | |   |    |
+            |   |    |   | |   |    |
+            |   |    | 6 | | 9 |    |
+            |   | 99 |   | |   |    |
+            | 3 |    |   | |   | 12 |
+        And the named places
+            | osm | class    | type           | admin | geometry |
+            | R1  | boundary | administrative | 3     | (1,2,3,12,11,10,7,8,9,6,5,4,1) |
+            | R2  | boundary | administrative | 4     | (2,3,12,11,8,9,6,5,2) |
+            | N1  | place    | square         | 15    | 99 |
+        When importing
+        Then place_addressline contains
+            | object | address | isaddress |
+            | N1     | R1      | True |
+            | N1     | R2      | True |
+
+    Scenario: with boundaries of same rank the one with the closer centroid is prefered
+        Given the grid
+            | 1 |   |   | 3 |  | 5 |
+            |   | 9 |   |   |  |   |
+            | 2 |   |   | 4 |  | 6 |
+        And the named places
+            | osm | class    | type           | admin | geometry |
+            | R1  | boundary | administrative | 8     | (1,2,4,3,1) |
+            | R2  | boundary | administrative | 8     | (1,2,6,5,1) |
+            | N1  | place    | square         | 15    | 9 |
+        When importing
+        Then place_addressline contains
+            | object | address | isaddress |
+            | N1     | R1      | True |
+            | N1     | R2      | False |
+
+    Scenario: boundary areas are preferred over place nodes in the address
+        Given the grid
+            | 1 |   |   |   |   |   | 3 |
+            |   | 5 |   |   |   |   |   |
+            |   | 6 |   |   |   |   |   |
+            | 2 |   |   |   |   |   | 4 |
+        And the named places
+            | osm | class    | type    | admin | geometry |
+            | N1  | place    | square  | 15    | 5 |
+            | N2  | place    | city    | 15    | 6 |
+            | R1  | place    | city    | 8     | (1,2,4,3,1) |
+        When importing
+        Then place_addressline contains
+            | object | address | isaddress | cached_rank_address |
+            | N1     | R1      | True      | 16                  |
+            | N1     | N2      | False     | 16                  |
+
+    Scenario: place nodes outside a smaller ranked area are ignored
+        Given the grid
+            | 1 |   | 2 |   |
+            |   | 7 |   | 9 |
+            | 4 |   | 3 |   |
+        And the named places
+            | osm | class    | type    | admin | geometry |
+            | N1  | place    | square  | 15    | 7 |
+            | N2  | place    | city    | 15    | 9 |
+            | R1  | place    | city    | 8     | (1,2,3,4,1) |
+        When importing
+        Then place_addressline contains
+            | object | address | isaddress | cached_rank_address |
+            | N1     | R1      | True      | 16                  |
+        And place_addressline doesn't contain
+            | object | address |
+            | N1     | N2      |
+
+
+    Scenario: place nodes close enough to smaller ranked place nodes are included
+        Given the 0.002 grid
+            | 2 |   | 3 | 1 |
+        And the named places
+            | osm | class | type     | geometry |
+            | N1  | place | square   | 1 |
+            | N2  | place | hamlet   | 2 |
+            | N3  | place | quarter  | 3 |
+        When importing
+        Then place_addressline contains
+            | object | address | fromarea | isaddress |
+            | N1     | N2      | False    | True      |
+            | N1     | N3      | False    | True      |
+
+
+    Scenario: place nodes too far away from a smaller ranked place nodes are marked non-address
+        Given the 0.002 grid
+            | 2 |  |  | 1 |  | 3 |
+        And the named places
+            | osm | class | type     | geometry |
+            | N1  | place | square   | 1 |
+            | N2  | place | hamlet   | 2 |
+            | N3  | place | quarter  | 3 |
+        When importing
+        Then place_addressline contains
+            | object | address | fromarea | isaddress |
+            | N1     | N2      | False    | True      |
+            | N1     | N3      | False    | False     |
+
+
     # github #121
     Scenario: Roads crossing boundaries should contain both states
         Given the grid
@@ -60,7 +192,7 @@ Feature: Address computation
             | object | address |
             | W1     | W11     |
 
-    Scenario: Roads should not contain boundaries they touch in a end point
+    Scenario: Roads should not contain boundaries they touch in a middle point
         Given the grid
             | 1 |   |   | 2 |   | 3 |
             |   | 7 |   | 8 |   |   |
@@ -151,3 +283,18 @@ Feature: Address computation
         Then place_addressline contains
             | object | address |
             | W93    | R4      |
+
+    Scenario: squares do not appear in the address of a street
+        Given the grid
+            |   | 1 |   | 2 |   |
+            | 8 |   |   |   | 9 |
+            |   | 4 |   | 3 |   |
+        And the named places
+            | osm | class    | type           | geometry |
+            | W1  | highway  | residential    | 8, 9     |
+            | W2  | place    | square         | (1, 2, 3 ,4, 1) |
+        When importing
+        Then place_addressline doesn't contain
+            | object | address |
+            | W1     | W2      |
+
index 4f1cc6c015c7f109a1082a2946d4b89ecb8be03f..cea4d973acbf16625a99b4f3a7e205778b1ad96e 100644 (file)
@@ -2,7 +2,7 @@
 Feature: Rank assignment
     Tests for assignment of search and address ranks.
 
-    Scenario: Ranks for place nodes are assinged according to thier type
+    Scenario: Ranks for place nodes are assigned according to their type
         Given the named places
           | osm  | class     | type      |
           | N1   | foo       | bar       |
@@ -113,3 +113,36 @@ Feature: Rank assignment
           | R20    | 12          | 22 |
           | R21    | 14          | 24 |
           | R22    | 16          | 25 |
+
+    Scenario: admin levels contained in a place area must not overtake address ranks
+        Given the named places
+            | osm | class    | type           | admin | geometry |
+            | R10 | place    | city           | 15    | (0 0, 0 2, 2 0, 0 0) |
+            | R20 | boundary | administrative | 6     | (0 0, 0 1, 1 0, 0 0) |
+        When importing
+        Then placex contains
+            | object | rank_search | rank_address |
+            | R10    | 16          | 16           |
+            | R20    | 12          | 18           |
+
+    Scenario: admin levels overlapping a place area are not demoted
+        Given the named places
+            | osm | class    | type           | admin | geometry |
+            | R10 | place    | city           | 15    | (0 0, 0 2, 2 0, 0 0) |
+            | R20 | boundary | administrative | 6     | (-1 0, 0 1, 1 0, -1 0) |
+        When importing
+        Then placex contains
+            | object | rank_search | rank_address |
+            | R10    | 16          | 16           |
+            | R20    | 12          | 12           |
+
+    Scenario: admin levels with equal area as a place area are not demoted
+        Given the named places
+            | osm | class    | type           | admin | geometry |
+            | R10 | place    | city           | 15    | (0 0, 0 2, 2 0, 0 0) |
+            | R20 | boundary | administrative | 6     | (0 0, 0 2, 2 0, 0 0) |
+        When importing
+        Then placex contains
+            | object | rank_search | rank_address |
+            | R10    | 16          | 16           |
+            | R20    | 12          | 12           |
index 29340a921fd4aa9ebbd77e783d174e5ccf2012a9..b05d752a304ba784128e590e27b7afa29d70500c 100644 (file)
@@ -38,7 +38,6 @@ if ($sOsmType && $iOsmId > 0) {
     $oReverseGeocode->setZoom($iZoom);
 
     $oLookup = $oReverseGeocode->lookup($fLat, $fLon);
-    if (CONST_Debug) var_dump($oLookup);
 
     if ($oLookup) {
         $aPlaces = $oPlaceLookup->lookup(array($oLookup->iId => $oLookup));