]> git.openstreetmap.org Git - nominatim.git/commitdiff
Merge pull request #1321 from mtmail/interpolating-0-housenumbers
authorSarah Hoffmann <lonvia@denofr.de>
Fri, 19 Apr 2019 16:29:43 +0000 (18:29 +0200)
committerGitHub <noreply@github.com>
Fri, 19 Apr 2019 16:29:43 +0000 (18:29 +0200)
Support housenumber=0 in interpolations

17 files changed:
.travis.yml
docs/admin/Faq.md
lib/AddressDetails.php
lib/DB.php
lib/ReverseGeocode.php
lib/SearchDescription.php
lib/setup/SetupClass.php
lib/setup_functions.php
lib/template/details-html.php
lib/template/details-json.php
sql/functions.sql
test/bdd/api/search/postcode.feature
utils/export.php
utils/setup.php
utils/update.php
vagrant/install-on-travis-ci.sh
website/hierarchy.php

index fe4e0cbd09b00738d57173ce1a45c271a89dc0e8..259b9d5b95d3f6578f8b660d173025234e635bd1 100644 (file)
@@ -11,6 +11,8 @@ git:
 env:
   - TEST_SUITE=tests
   - TEST_SUITE=monaco
+before_install:
+  - phpenv global 7.1
 install:
   - vagrant/install-on-travis-ci.sh
 before_script:
@@ -19,7 +21,7 @@ script:
   - cd $TRAVIS_BUILD_DIR/
   - if [[ $TEST_SUITE == "tests" ]]; then phpcs --report-width=120 . ; fi
   - cd $TRAVIS_BUILD_DIR/test/php
-  - if [[ $TEST_SUITE == "tests" ]]; then phpunit ./ ; fi
+  - if [[ $TEST_SUITE == "tests" ]]; then /usr/bin/phpunit ./ ; fi
   - cd $TRAVIS_BUILD_DIR/test/bdd
   - # behave --format=progress3 api
   - if [[ $TEST_SUITE == "tests" ]]; then behave -DREMOVE_TEMPLATE=1 --format=progress3 db ; fi
index 1e874c789bf5b210985a542080ecd41829590fe0..db5e101c83dbfafc579ad03ea693b029343fdb66 100644 (file)
@@ -81,6 +81,14 @@ If you are using a flatnode file, then it may also be that the underlying
 filesystem does not fully support 'mmap'. A notable candidate is virtualbox's
 vboxfs.
 
+### nominatim UPDATE failed: ERROR: buffer 179261 is not owned by resource owner Portal
+
+Several users [reported this](https://github.com/openstreetmap/Nominatim/issues/1168) during the initial import of the database. It's 
+something Postgresql internal Nominatim doesn't control. And Postgresql forums
+suggest it's threading related but definitely some kind of crash of a process.
+Users reported either rebooting the server, different hardware or just trying
+the import again worked. 
+
 ### The website shows: "Could not get word tokens"
 
 The server cannot access your database. Add `&debug=1` to your URL
@@ -104,11 +112,8 @@ However, you can solve this the quick and dirty way by commenting out that line
 
 ### "must be an array or an object that implements Countable" warning in /usr/share/pear/DB.php
 
-As reported starting PHP 7.2. This external DB library is no longer maintained and will be replaced in future Nominatim versions. In the meantime you'd have to manually change the line near 774 from
-`if (!count($dsn)) {` to  `if (!$dsn && !count($dsn))`. [More details](https://github.com/openstreetmap/Nominatim/issues/1184)
-
-
-
+The warning started with PHP 7.2. Make sure you have at least [version 1.9.3 of PEAR DB](https://github.com/pear/DB/releases)
+installed.
 
 ### Website reports "DB Error: insufficient permissions"
 
index 7b30d915843d413f0517eaa239467b98bd86e98a..b62335d1f61ace06277a7aebf23423dbba7ef3cc 100644 (file)
@@ -31,7 +31,7 @@ class AddressDetails
 
     private static function isAddress($aLine)
     {
-        return $aLine['isaddress'] == 't' || $aLine['type'] == 'country_code';
+        return $aLine['isaddress'] || $aLine['type'] == 'country_code';
     }
 
     public function getAddressDetails($bAll = false)
@@ -49,7 +49,7 @@ class AddressDetails
         $sPrevResult = '';
 
         foreach ($this->aAddressLines as $aLine) {
-            if ($aLine['isaddress'] == 't' && $sPrevResult != $aLine['localname']) {
+            if ($aLine['isaddress'] && $sPrevResult != $aLine['localname']) {
                 $sPrevResult = $aLine['localname'];
                 $aParts[] = $sPrevResult;
             }
index 033e23f7f593512229cc3e6752a6031090ca4962..51fd49fc228329c87e0055a0741d529f20bb1aa2 100644 (file)
@@ -74,12 +74,7 @@ class DB
     public function getRow($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed')
     {
         try {
-            if (isset($aInputVars)) {
-                $stmt = $this->connection->prepare($sSQL);
-                $stmt->execute($aInputVars);
-            } else {
-                $stmt = $this->connection->query($sSQL);
-            }
+            $stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage);
             $row = $stmt->fetch();
         } catch (\PDOException $e) {
             throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL);
@@ -98,12 +93,7 @@ class DB
     public function getOne($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed')
     {
         try {
-            if (isset($aInputVars)) {
-                $stmt = $this->connection->prepare($sSQL);
-                $stmt->execute($aInputVars);
-            } else {
-                $stmt = $this->connection->query($sSQL);
-            }
+            $stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage);
             $row = $stmt->fetch(\PDO::FETCH_NUM);
             if ($row === false) return false;
         } catch (\PDOException $e) {
@@ -123,12 +113,7 @@ class DB
     public function getAll($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed')
     {
         try {
-            if (isset($aInputVars)) {
-                $stmt = $this->connection->prepare($sSQL);
-                $stmt->execute($aInputVars);
-            } else {
-                $stmt = $this->connection->query($sSQL);
-            }
+            $stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage);
             $rows = $stmt->fetchAll();
         } catch (\PDOException $e) {
             throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL);
@@ -148,12 +133,8 @@ class DB
     {
         $aVals = array();
         try {
-            if (isset($aInputVars)) {
-                $stmt = $this->connection->prepare($sSQL);
-                $stmt->execute($aInputVars);
-            } else {
-                $stmt = $this->connection->query($sSQL);
-            }
+            $stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage);
+
             while ($val = $stmt->fetchColumn(0)) { // returns first column or false
                 $aVals[] = $val;
             }
@@ -174,12 +155,8 @@ class DB
     public function getAssoc($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed')
     {
         try {
-            if (isset($aInputVars)) {
-                $stmt = $this->connection->prepare($sSQL);
-                $stmt->execute($aInputVars);
-            } else {
-                $stmt = $this->connection->query($sSQL);
-            }
+            $stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage);
+
             $aList = array();
             while ($aRow = $stmt->fetch(\PDO::FETCH_NUM)) {
                 $aList[$aRow[0]] = $aRow[1];
@@ -190,6 +167,27 @@ class DB
         return $aList;
     }
 
+    /**
+     * Executes query. Returns a PDO statement to iterate over.
+     *
+     * @param string  $sSQL
+     *
+     * @return PDOStatement
+     */
+    public function getQueryStatement($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed')
+    {
+        try {
+            if (isset($aInputVars)) {
+                $stmt = $this->connection->prepare($sSQL);
+                $stmt->execute($aInputVars);
+            } else {
+                $stmt = $this->connection->query($sSQL);
+            }
+        } catch (\PDOException $e) {
+            throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL);
+        }
+        return $stmt;
+    }
 
     /**
      * St. John's Way => 'St. John\'s Way'
@@ -229,12 +227,6 @@ class DB
         return 'ARRAY['.join(',', $a).']';
     }
 
-    public function getLastError()
-    {
-        // https://secure.php.net/manual/en/pdo.errorinfo.php
-        return $this->connection->errorInfo();
-    }
-
     /**
      * Check if a table exists in the database. Returns true if it does.
      *
index ff20691a3912457ed99b3746da3f27e93e6f9cb9..5cecfd33a8606934fa3109929c5ad84313805f21 100644 (file)
@@ -36,8 +36,8 @@ class ReverseGeocode
                       13 => 18,
                       14 => 22, // Suburb
                       15 => 22,
-                      16 => 26, // Street, TODO: major street?
-                      17 => 26,
+                      16 => 26, // major street
+                      17 => 27, // minor street
                       18 => 30, // or >, Building
                       19 => 30, // or >, Building
                      );
@@ -246,12 +246,7 @@ class ReverseGeocode
             $sSQL .= ' placex';
             $sSQL .= '   WHERE ST_DWithin('.$sPointSQL.', geometry, '.$fSearchDiam.')';
             $sSQL .= '   AND';
-            // only streets
-            if ($iMaxRank == 26) {
-                $sSQL .= ' rank_address = 26';
-            } else {
-                $sSQL .= ' rank_address between 26 and '.$iMaxRank;
-            }
+            $sSQL .= ' rank_address between 26 and '.$iMaxRank;
             $sSQL .= ' and (name is not null or housenumber is not null';
             $sSQL .= ' or rank_address between 26 and 27)';
             $sSQL .= ' and (rank_address between 26 and 27';
@@ -304,7 +299,7 @@ class ReverseGeocode
                     // radius ?
                     $sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, 0.001)';
                     $sSQL .= ' AND parent_place_id = '.$iPlaceID;
-                    $sSQL .= ' and rank_address != 28';
+                    $sSQL .= ' and rank_address > 28';
                     $sSQL .= ' and ST_GeometryType(geometry) != \'ST_LineString\'';
                     $sSQL .= ' and (name is not null or housenumber is not null)';
                     $sSQL .= ' and class not in (\'boundary\')';
index 358e6969577f97ac52031c7bd10689572c405d6f..506d4202253f61f909c46f3d1c08275b2c12faa9 100644 (file)
@@ -588,6 +588,9 @@ class SearchDescription
 
         $sSQL .= "p.postcode = '".reset($this->aName)."'";
         $sSQL .= $this->countryCodeSQL(' AND p.country_code');
+        if ($this->oContext->bViewboxBounded) {
+            $sSQL .= ' AND ST_Intersects('.$this->oContext->sqlViewboxSmall.', geometry)';
+        }
         $sSQL .= $this->oContext->excludeSQL(' AND p.place_id');
         $sSQL .= " LIMIT $iLimit";
 
index 9fcec2f01e63c994f939134c3f5864e7dac6c39e..c14190c3a1be66020216defca9686262ac44f3f0 100755 (executable)
@@ -144,9 +144,7 @@ class SetupFunctions
         }
 
         // Try accessing the C module, so we know early if something is wrong
-        if (!checkModulePresence()) {
-            fail('error loading nominatim.so module');
-        }
+        checkModulePresence(); // raises exception on failure
 
         if (!file_exists(CONST_ExtraDataPath.'/country_osm_grid.sql.gz')) {
             echo 'Error: you need to download the country_osm_grid first:';
@@ -227,11 +225,9 @@ class SetupFunctions
     {
         info('Create Functions');
 
-        // Try accessing the C module, so we know eif something is wrong
-        // update.php calls this function
-        if (!checkModulePresence()) {
-            fail('error loading nominatim.so module');
-        }
+        // Try accessing the C module, so we know early if something is wrong
+        checkModulePresence(); // raises exception on failure
+
         $this->createSqlFunctions();
     }
 
index 89736ae0515c53039403014a3180e9413e09e18d..43f30a090dfff4939f4f511e786e2615d467c1c1 100755 (executable)
@@ -17,8 +17,9 @@ function checkInFile($sOSMFile)
 
 function checkModulePresence()
 {
-    // Try accessing the C module, so we know early if something is wrong
-    // and can simply error out.
+    // Try accessing the C module, so we know early if something is wrong.
+    // Raises Nominatim\DatabaseError on failure
+
     $sModulePath = CONST_Database_Module_Path;
     $sSQL = "CREATE FUNCTION nominatim_test_import_func(text) RETURNS text AS '";
     $sSQL .= $sModulePath . "/nominatim.so', 'transliteration' LANGUAGE c IMMUTABLE STRICT";
@@ -26,15 +27,5 @@ function checkModulePresence()
 
     $oDB = new \Nominatim\DB();
     $oDB->connect();
-
-    $bResult = true;
-    try {
-        $oDB->exec($sSQL);
-    } catch (\Nominatim\DatabaseError $e) {
-        echo "\nERROR: Failed to load nominatim module. Reason:\n";
-        echo $oDB->getLastError()[2] . "\n\n";
-        $bResult = false;
-    }
-
-    return $bResult;
+    $oDB->exec($sSQL, null, 'Database server failed to load '.$sModulePath.'/nominatim.so module');
 }
index 01583e5f789499a27a28dbdb4d9e3048c72b761e..9b76efc12f90977a4b91927a75be8e35bb950dc2 100644 (file)
@@ -61,7 +61,7 @@
 
 
     function _one_row($aAddressLine){
-        $bNotUsed = (isset($aAddressLine['isaddress']) && $aAddressLine['isaddress'] == 'f');
+        $bNotUsed = isset($aAddressLine['isaddress']) && !$aAddressLine['isaddress'];
 
         echo '<tr class="' . ($bNotUsed?'notused':'') . '">'."\n";
         echo '  <td class="name">'.(trim($aAddressLine['localname'])?$aAddressLine['localname']:'<span class="noname">No Name</span>')."</td>\n";
                     if ($aPointDetails['calculated_importance']) {
                         kv('Importance'    , $aPointDetails['calculated_importance'].($aPointDetails['importance']?'':' (estimated)') );
                     }
-                    kv('Coverage'        , ($aPointDetails['isarea']=='t'?'Polygon':'Point') );
+                    kv('Coverage'        , ($aPointDetails['isarea']?'Polygon':'Point') );
                     kv('Centre Point'    , $aPointDetails['lat'].','.$aPointDetails['lon'] );
                     kv('OSM'             , osmLink($aPointDetails) );
                     if ($aPointDetails['wikipedia'])
index 06554aba68f2143c6a24b46ce9cdb39e9cfc3362..4afb1b0bc4e717dcd7609d8e5630c6500b566da9 100644 (file)
@@ -33,7 +33,7 @@ if ($aPointDetails['icon']) {
 $aPlaceDetails['rank_address'] = (int) $aPointDetails['rank_address'];
 $aPlaceDetails['rank_search'] = (int) $aPointDetails['rank_search'];
 
-$aPlaceDetails['isarea'] = ($aPointDetails['isarea'] == 't');
+$aPlaceDetails['isarea'] = $aPointDetails['isarea'];
 $aPlaceDetails['centroid'] = array(
                               'type' => 'Point',
                               'coordinates' => array( (float) $aPointDetails['lon'], (float) $aPointDetails['lat'] )
index 73790353059a683cdfec0db2f101e1dbc5935f4e..8ecb8c2c1aa1da18a20d31afae360534127476c2 100644 (file)
@@ -2318,6 +2318,7 @@ DECLARE
   searchhousename HSTORE;
   searchrankaddress INTEGER;
   searchpostcode TEXT;
+  postcode_isaddress BOOL;
   searchclass TEXT;
   searchtype TEXT;
   countryname HSTORE;
@@ -2325,6 +2326,8 @@ BEGIN
   -- The place ein question might not have a direct entry in place_addressline.
   -- Look for the parent of such places then and save if in for_place_id.
 
+  postcode_isaddress := true;
+
   -- first query osmline (interpolation lines)
   IF in_housenumber >= 0 THEN
     SELECT parent_place_id, country_code, in_housenumber::text, 30, postcode,
@@ -2441,7 +2444,10 @@ BEGIN
       searchcountrycode := location.country_code;
     END IF;
     IF location.type in ('postcode', 'postal_code') THEN
-      location.isaddress := FALSE;
+      postcode_isaddress := false;
+      IF location.osm_type != 'R' THEN
+        location.isaddress := FALSE;
+      END IF;
     END IF;
     countrylocation := ROW(location.place_id, location.osm_type, location.osm_id,
                            location.name, location.class, location.type,
@@ -2485,7 +2491,7 @@ BEGIN
 
   IF searchpostcode IS NOT NULL THEN
     location := ROW(null, null, null, hstore('ref', searchpostcode), 'place',
-                    'postcode', null, true, true, 5, 0)::addressline;
+                    'postcode', null, false, postcode_isaddress, 5, 0)::addressline;
     RETURN NEXT location;
   END IF;
 
index 63c8646946cb2d1170879b17c6ff0a9268340650..e70495f63890f15c958bb75a86ec072963fca68f 100644 (file)
@@ -26,6 +26,18 @@ Feature: Searches with postcodes
             | country_code |
             | li           |
 
+    Scenario: Postcode search with bounded viewbox restriction
+        When sending json search query "9486" with address
+          | bounded | viewbox |
+          | 1       | 9.55,47.20,9.58,47.22 |
+        Then result addresses contain
+            | postcode |
+            | 9486     |
+        When sending json search query "9486" with address
+          | bounded | viewbox                 |
+          | 1       | 5.00,20.00,6.00,21.00 |
+        Then exactly 0 results are returned
+
     Scenario: Postcode search with structured query
         When sending json search query "" with address
             | postalcode | country |
index 9d3037aa9c408a2175ba149b0405b8c5ca7fae41..ef55aab27ecb4b48514f782a95af44bcc2e18965 100644 (file)
         $sOsmId = $aCMDResult['restrict-to-osm-relation'];
     }
     if ($sOsmType) {
-        $sSQL = 'select place_id from placex where';
-        $sSQL .= ' osm_type = '.$oDB->getDBQuoted($sOsmType);
-        $sSQL .= ' and osm_id = '.$sOsmId;
-        $sParentId = $oDB->getOne($sSQL);
+        $sSQL = 'select place_id from placex where osm_type = :osm_type and osm_id = :osm_id';
+        $sParentId = $oDB->getOne($sSQL, array('osm_type' => $sOsmType, 'osm_id' => $sOsmId));
         if (!$sParentId) fail('Could not find place '.$sOsmType.' '.$sOsmId);
     }
     if ($sParentId) {
     // Iterate over placeids
     // to get further hierarchical information
     //var_dump($sPlacexSQL);
-    $aRes =& $oDB->query($sPlacexSQL);
+    $oResults = $oDB->getQueryStatement($sPlacexSQL);
     $fOutstream = fopen('php://output', 'w');
-    while ($aRes->fetchInto($aRow)) {
-    //var_dump($aRow);
+    while ($aRow = $oResults->fetch()) {
+        //var_dump($aRow);
         $iPlaceID = $aRow['place_id'];
-        $sSQL = "select rank_address,get_name_by_language(name,$sLanguagePrefArraySQL) as localname from get_addressdata($iPlaceID, -1)";
+        $sSQL = "select rank_address,get_name_by_language(name,$sLanguagePrefArraySQL) as localname from get_addressdata(:place_id, -1)";
         $sSQL .= ' WHERE isaddress';
         $sSQL .= ' order by rank_address desc,isaddress desc';
-        $aAddressLines = $oDB->getAll($sSQL);
+        $aAddressLines = $oDB->getAll($sSQL, array('place_id' => $iPlaceID));
 
         $aOutput = array_fill(0, $iNumCol, '');
         // output address parts
                 $sSQL = 'select array_agg(px.postcode) from placex px join place_addressline pa ';
                 $sSQL .= 'on px.place_id = pa.address_place_id ';
                 $sSQL .= 'where pa.cached_rank_address in (5,11) ';
-                $sSQL .= 'and pa.place_id in (select place_id from place_addressline where address_place_id in ('.substr($aRow['place_ids'], 1, -1).')) ';
+                $sSQL .= 'and pa.place_id in (select place_id from place_addressline where address_place_id in (:first_place_id)) ';
                 $sSQL .= 'group by postcode order by count(*) desc limit 1';
-                $sRes = $oDB->getOne($sSQL);
+                $sRes = $oDB->getOne($sSQL, array('first_place_id' => substr($aRow['place_ids'], 1, -1)));
 
                 $aOutput[$aColumnMapping['postcode']] = substr($sRes, 1, -1);
             } else {
index 66b719205705d2b68be4dafc6fbf7c6ccdce9109..8ad96a9524eac81360106412b00c3070ea1458a0 100644 (file)
@@ -84,9 +84,7 @@ if ($aCMDResult['setup-db'] || $aCMDResult['all']) {
 }
 
 // Try accessing the C module, so we know early if something is wrong
-if (!checkModulePresence()) {
-    fail('error loading nominatim.so module');
-}
+checkModulePresence(); // raises exception on failure
 
 if ($aCMDResult['import-data'] || $aCMDResult['all']) {
     $bDidSomething = true;
index a18c17211861d39695771d94e734ebf5859b4727..c3620b063c80fa06101626691a4dd6dc55770b98 100644 (file)
@@ -352,7 +352,7 @@ if ($aResult['import-osmosis'] || $aResult['import-osmosis-all']) {
         $sBatchEnd = $aLastState['lastimportdate'];
         $iEndSequence = $aLastState['sequence_id'];
 
-        if ($aLastState['indexed'] == 't') {
+        if ($aLastState['indexed']) {
             // Sleep if the update interval has not yet been reached.
             $fNextUpdate = $aLastState['unix_ts'] + CONST_Replication_Update_Interval;
             if ($fNextUpdate > $fStartTime) {
index fa2a43e0e9a24ef643813c45281c13e59701a2d0..229a7ef1de807fa2765bf134b2abbe262731ff1d 100755 (executable)
@@ -26,6 +26,9 @@ pip3 install --quiet behave nose pytidylib psycopg2-binary
 composer global require "squizlabs/php_codesniffer=*"
 sudo ln -s /home/travis/.config/composer/vendor/bin/phpcs /usr/bin/
 
+composer global require "phpunit/phpunit=7.*"
+sudo ln -s /home/travis/.config/composer/vendor/bin/phpunit /usr/bin/
+
 sudo -u postgres createuser -S www-data
 
 # Make sure that system servers can read from the home directory:
index b31b85d1090954ed3f681e720ff695a13c61b084..87e8a0afbb258fdaefd03d2169baac6d5522fcbc 100644 (file)
@@ -121,7 +121,7 @@ if (!empty($aParentOfLines)) {
             echo '<div class="line">';
             echo '<span class="name">'.(trim($aAddressLine['localname'])?$aAddressLine['localname']:'<span class="noname">No Name</span>').'</span>';
             echo ' (';
-            echo '<span class="area">'.($aAddressLine['isarea']=='t'?'Polygon':'Point').'</span>';
+            echo '<span class="area">'.($aAddressLine['isarea']?'Polygon':'Point').'</span>';
             if ($sOSMType) echo ', <span class="osm"><span class="label"></span>'.$sOSMType.' '.osmLink($aAddressLine).'</span>';
             echo ', <a href="hierarchy.php?place_id='.$aAddressLine['place_id'].'">GOTO</a>';
             echo ', '.$aAddressLine['area'];