]> git.openstreetmap.org Git - nominatim.git/commitdiff
improve place node search when no areas found
authorSarah Hoffmann <lonvia@denofr.de>
Sat, 4 Aug 2018 16:44:17 +0000 (18:44 +0200)
committerSarah Hoffmann <lonvia@denofr.de>
Sat, 4 Aug 2018 19:51:23 +0000 (21:51 +0200)
Only look for place nodes in a certain radius according
to the rank_search of the place node.

lib/ReverseGeocode.php
sql/functions.sql
test/bdd/api/reverse/queries.feature
test/bdd/api/search/params.feature
test/bdd/steps/queries.py

index 8e5e8bd1de59dd1d13c502d169faf5fcb4153481..5648fedf26d83b2c053c924a0ad9ac24e45b35f9 100644 (file)
@@ -106,17 +106,21 @@ class ReverseGeocode
         if ($aPoly) {
             $sCountryCode = $aPoly['country_code'];
 
-            $sSQL = 'SELECT place_id, ST_distance('.$sPointSQL.', geometry) as distance';
+            // look for place nodes with the given country code
+            $sSQL = 'SELECT place_id FROM';
+            $sSQL .= ' (SELECT place_id, rank_search,';
+            $sSQL .= '         ST_distance('.$sPointSQL.', geometry) as distance';
             $sSQL .= ' FROM placex';
             $sSQL .= ' WHERE osm_type = \'N\'';
             $sSQL .= ' AND country_code = \''.$sCountryCode.'\'';
-            $sSQL .= ' AND rank_address > 0';
-            $sSQL .= ' AND rank_address <= ' .min(25, $iMaxRank);
+            $sSQL .= ' AND rank_search > 4';
+            $sSQL .= ' AND rank_search <= ' .min(25, $iMaxRank);
             $sSQL .= ' AND type != \'postcode\'';
             $sSQL .= ' AND name IS NOT NULL ';
             $sSQL .= ' and indexed_status = 0 and linked_place_id is null';
-            $sSQL .= ' AND ST_DWithin('.$sPointSQL.', geometry, 1.0)';
-            $sSQL .= ' ORDER BY distance ASC, rank_address DESC';
+            $sSQL .= ' AND ST_DWithin('.$sPointSQL.', geometry, 5.0)) p ';
+            $sSQL .= 'WHERE distance <= reverse_place_diameter(rank_search)';
+            $sSQL .= ' ORDER BY rank_search DESC, distance ASC';
             $sSQL .= ' LIMIT 1';
 
             if (CONST_Debug) var_dump($sSQL);
@@ -127,6 +131,23 @@ class ReverseGeocode
             if ($aPlacNode) {
                 return $aPlacNode;
             }
+
+            // still nothing, then return the country object
+            $sSQL = 'SELECT place_id, ST_distance('.$sPointSQL.', centroid) as distance';
+            $sSQL .= ' FROM placex';
+            $sSQL .= ' WHERE country_code = \''.$sCountryCode.'\'';
+            $sSQL .= ' AND rank_search = 4 AND rank_address = 4';
+            $sSQL .= ' AND class in (\'boundary\',  \'place\')';
+            $sSQL .= ' ORDER BY distance ASC';
+
+            if (CONST_Debug) var_dump($sSQL);
+            $aPlacNode = chksql(
+                $this->oDB->getRow($sSQL),
+                'Could not determine place node.'
+            );
+            if ($aPlacNode) {
+                return $aPlacNode;
+            }
         }
     }
 
@@ -138,13 +159,13 @@ class ReverseGeocode
         // polygon search begins at suburb-level
         if ($iMaxRank > 25) $iMaxRank = 25;
         // no polygon search over country-level
-        if ($iMaxRank < 4) $iMaxRank = 4;
+        if ($iMaxRank < 5) $iMaxRank = 5;
         // search for polygon
         $sSQL = 'SELECT place_id, parent_place_id, rank_address, rank_search FROM';
         $sSQL .= '(select place_id, parent_place_id, rank_address, rank_search, country_code, geometry';
         $sSQL .= ' FROM placex';
         $sSQL .= ' WHERE ST_GeometryType(geometry) in (\'ST_Polygon\', \'ST_MultiPolygon\')';
-        $sSQL .= ' AND rank_address Between 4 AND ' .$iMaxRank;
+        $sSQL .= ' AND rank_address Between 5 AND ' .$iMaxRank;
         $sSQL .= ' AND geometry && '.$sPointSQL;
         $sSQL .= ' AND type != \'postcode\' ';
         $sSQL .= ' AND name is not null';
@@ -165,49 +186,27 @@ class ReverseGeocode
             $iPlaceID = $aPoly['place_id'];
 
             if ($iRankAddress != $iMaxRank) {
-            //search diameter for the place node search
-                if ($iMaxRank <= 4) {
-                    $fSearchDiam = 4;
-                } elseif ($iMaxRank <= 8) {
-                    $fSearchDiam = 2;
-                } elseif ($iMaxRank <= 10) {
-                    $fSearchDiam = 1;
-                } elseif ($iMaxRank <= 12) {
-                    $fSearchDiam = 0.8;
-                } elseif ($iMaxRank <= 17) {
-                    $fSearchDiam = 0.6;
-                } elseif ($iMaxRank <= 18) {
-                    $fSearchDiam = 0.2;
-                } elseif ($iMaxRank <= 25) {
-                    $fSearchDiam = 0.1;
-                }
-
-                $sSQL = 'SELECT place_id';
-                $sSQL .= ' FROM (';
-                $sSQL .= ' SELECT place_id, rank_address,country_code, geometry,';
+                $sSQL = 'SELECT place_id FROM ';
+                $sSQL .= '(SELECT place_id, rank_search, country_code, geometry,';
                 $sSQL .= ' ST_distance('.$sPointSQL.', geometry) as distance';
                 $sSQL .= ' FROM placex';
                 $sSQL .= ' WHERE osm_type = \'N\'';
-                if ($iRankAddress = 16) {
-                // using rank_search because of a better differentiation for place nodes at rank_address 16
-                    $sSQL .= ' AND rank_search > '.$iRankSearch;
-                    $sSQL .= ' AND rank_search <= ' .$iMaxRank;
-                    $sSQL .= ' AND class = \'place\'';
-                } else {
-                    $sSQL .= ' AND rank_address > '.$iRankAddress;
-                    $sSQL .= ' AND rank_address <= ' .$iMaxRank;
-                }
-                $sSQL .= ' AND ST_DWithin('.$sPointSQL.', geometry, '.$fSearchDiam.')';
+                // using rank_search because of a better differentiation
+                // for place nodes at rank_address 16
+                $sSQL .= ' AND rank_search > '.$iRankSearch;
+                $sSQL .= ' AND rank_search <= ' .$iMaxRank;
+                $sSQL .= ' AND class = \'place\'';
                 $sSQL .= ' AND type != \'postcode\'';
                 $sSQL .= ' AND name IS NOT NULL ';
-                $sSQL .= ' and indexed_status = 0 and linked_place_id is null';
+                $sSQL .= ' AND indexed_status = 0 AND linked_place_id is null';
                 // preselection through bbox
                 $sSQL .= ' AND (SELECT geometry FROM placex WHERE place_id = '.$iPlaceID.') && geometry';
                 $sSQL .= ' ORDER BY distance ASC,';
                 $sSQL .= ' rank_address DESC';
                 $sSQL .= ' limit 500) as a';
                 $sSQL .= ' WHERE ST_CONTAINS((SELECT geometry FROM placex WHERE place_id = '.$iPlaceID.'), geometry )';
-                $sSQL .= ' ORDER BY distance ASC, rank_address DESC';
+                $sSQL .= ' AND distance <= reverse_place_diameter(rank_search)';
+                $sSQL .= ' ORDER BY distance ASC, rank_search DESC';
                 $sSQL .= ' LIMIT 1';
 
                 if (CONST_Debug) var_dump($sSQL);
index ada9fb50007c0d4577efa04442681488b1eb54e9..7c62bc9318f29da3d8b53212176f0fdb3e83c4b1 100644 (file)
@@ -256,6 +256,28 @@ END;
 $$
 LANGUAGE plpgsql IMMUTABLE;
 
+CREATE OR REPLACE FUNCTION reverse_place_diameter(rank_search SMALLINT)
+  RETURNS FLOAT
+  AS $$
+BEGIN
+  IF rank_search <= 4 THEN
+    RETURN 5.0;
+  ELSIF rank_search <= 8 THEN
+    RETURN 1.8;
+  ELSIF rank_search <= 12 THEN
+    RETURN 0.6;
+  ELSIF rank_search <= 17 THEN
+    RETURN 0.16;
+  ELSIF rank_search <= 18 THEN
+    RETURN 0.08;
+  ELSIF rank_search <= 19 THEN
+    RETURN 0.04;
+  END IF;
+
+  RETURN 0.02;
+END;
+$$
+LANGUAGE plpgsql IMMUTABLE;
 
 CREATE OR REPLACE FUNCTION get_postcode_rank(country_code VARCHAR(2), postcode TEXT,
                                       OUT rank_search SMALLINT, OUT rank_address SMALLINT)
index a6c1dd34671608edebdd49aadc335c65c52d8cbf..e06b1775fa2d69306833f11630229d0073c9e0f0 100644 (file)
@@ -48,8 +48,14 @@ Feature: Reverse geocoding
 
     Scenario: Location off the coast
         When sending jsonv2 reverse coordinates 54.046489113,8.5546870529
+        Then results contain
+         | display_name |
+         | Freie und Hansestadt Hamburg, Deutschland |
+
+    Scenario: When slightly outside town, the town is not shown
+        When sending jsonv2 reverse coordinates -32.122,-56.114
          | zoom |
-         | 5 |
+         | 15 |
         Then results contain
-         | error |
-         | Unable to geocode |
+         | display_name |
+         | Tacuarembó, Uruguay |
index fcd2b603cf0b054c607795720456c94f989548bc..feacd5f95586bf3ba1d6b489144c4b4842762f9d 100644 (file)
@@ -111,7 +111,7 @@ Feature: Search queries
         When sending json search query "restaurant"
          | bounded | viewbox |
          | 1       | 9.93027,53.61634,10.10073,53.54500 |
-        Then result has bounding box in 53.54500,53.61634,9.93027,10.10073
+        Then result has centroid in 53.54500,53.61634,9.93027,10.10073
 
     Scenario: Prefer results within viewbox
         When sending json search query "25 de Mayo" with address
index 62bc295eea0ca72bec9d5a1cbb753b94b2cda41a..fd13dd13b0f348c58dd725a6ff892b499e569de6 100644 (file)
@@ -593,6 +593,27 @@ def step_impl(context, lid, coords):
         assert_greater_equal(bbox[2], coord[2])
         assert_less_equal(bbox[3], coord[3])
 
+@then(u'result (?P<lid>\d+ )?has centroid in (?P<coords>[\d,.-]+)')
+def step_impl(context, lid, coords):
+    if lid is None:
+        context.execute_steps("then at least 1 result is returned")
+        bboxes = zip(context.response.property_list('lat'),
+                     context.response.property_list('lon'))
+    else:
+        context.execute_steps("then more than %sresults are returned" % lid)
+        res = context.response.result[int(lid)]
+        bboxes = [ (res['lat'], res['lon']) ]
+
+    coord = [ float(x) for x in coords.split(',') ]
+
+    for lat, lon in bboxes:
+        lat = float(lat)
+        lon = float(lon)
+        assert_greater_equal(lat, coord[0])
+        assert_less_equal(lat, coord[1])
+        assert_greater_equal(lon, coord[2])
+        assert_less_equal(lon, coord[3])
+
 @then(u'there are(?P<neg> no)? duplicates')
 def check_for_duplicates(context, neg):
     context.execute_steps("then at least 1 result is returned")