]> git.openstreetmap.org Git - nominatim.git/commitdiff
Merge remote-tracking branch 'upstream/master'
authorSarah Hoffmann <lonvia@denofr.de>
Sat, 20 Oct 2018 15:32:41 +0000 (17:32 +0200)
committerSarah Hoffmann <lonvia@denofr.de>
Sat, 20 Oct 2018 15:32:41 +0000 (17:32 +0200)
18 files changed:
docs/admin/Faq.md
docs/admin/Migration.md
docs/api/Faq.md
lib/AddressDetails.php
lib/SearchDescription.php
lib/setup/SetupClass.php [new file with mode: 0755]
lib/setup_functions.php [new file with mode: 0755]
test/README.md
test/bdd/api/reverse/language.feature
test/bdd/api/reverse/queries.feature
test/bdd/api/search/queries.feature
test/php/Nominatim/AddressDetailsTest.php [new file with mode: 0644]
test/php/Nominatim/ClassTypesTest.php [new file with mode: 0644]
test/php/Nominatim/LibTest.php
test/php/fixtures/address_details_10_downing_street.json [new file with mode: 0644]
utils/setup.php
utils/update.php
vagrant/Install-on-Ubuntu-18.sh

index 46da7e21ba0b8384e9d3ff8c38804124a9cf6f3e..a0e1cafebc5849f328502a43f661f02b67ece61b 100644 (file)
@@ -93,6 +93,14 @@ However, you can solve this the quick and dirty way by commenting out that line
     sudo systemctl restart httpd
 
 
+### "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)
+
+
+
+
 ### Website reports "DB Error: insufficient permissions"
 
 The user the webserver, e.g. Apache, runs under needs to have access to the Nominatim database. You can find the user like [this](https://serverfault.com/questions/125865/finding-out-what-user-apache-is-running-as), for default Ubuntu operating system for example it's `www-data`.
index 2cc0b80b3cddcb867d1ad9a2c9d4a0be9f537eb3..012c58b328eff4064ccff1e72b63c43b83952461 100644 (file)
@@ -39,12 +39,18 @@ GRANT SELECT ON table country_osm_grid to "www-user";
 
 Replace the `www-user` with the user name of your website server if necessary.
 
-Finally, you can drop the now unused indexes:
+You can now drop the unused indexes:
 
 ```
 DROP INDEX idx_placex_reverse_geometry;
 ```
 
+Finally, update all SQL functions:
+
+```sh
+./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions
+```
+
 ## 3.0.0 -> 3.1.0
 
 ### Postcode Table
index cc6397d35c3eed9d6ee9e792fda552904be446a2..6bc5b5e831c4cb4adac8adca089aa8e283f7a32d 100644 (file)
@@ -41,3 +41,21 @@ border while the closest street is on the other. As the address details contain
 the address of the closest object found, you might sometimes get one result,
 sometimes the other for the closest point.
 
+#### 4. Can you return the continent?
+
+Nominatim assigns each map feature one country. Those outside any administrative
+boundaries are assigned a special no-country. Continents or other super-national
+administrations (e.g. European Union, NATO, Custom unions) are not supported, 
+see also [Administrative Boundary](https://wiki.openstreetmap.org/wiki/Tag:boundary%3Dadministrative#Super-national_administrations).
+
+#### 5. Can you return the timezone?
+
+See this separate OpenStreetMap-based project [Timezone Boundary Builder](https://github.com/evansiroky/timezone-boundary-builder)
+
+#### 6. I want to download a list of streets/restaurants of a city/region
+
+The [Overpass API](https://wiki.openstreetmap.org/wiki/Overpass_API) is more
+suited for these kinds of queries.
+
+That said if you installed your own Nominatim instance you can use the
+`/utils/export.php` PHP script as basis to return such lists.
index badf868de8774ab5c568b1119a51594e8c987882..e6501b572d5b59b3632a0d61e14f711f5fa729c9 100644 (file)
@@ -22,9 +22,9 @@ class AddressDetails
         }
 
         $sSQL = 'SELECT *,';
-        $sSQL .= '  get_name_by_language(name,'.$mLangPref.') as localname';
+        $sSQL .= ' get_name_by_language(name,'.$mLangPref.') as localname';
         $sSQL .= ' FROM get_addressdata('.$iPlaceID.','.$sHousenumber.')';
-        $sSQL .= ' ORDER BY rank_address desc,isaddress DESC';
+        $sSQL .= ' ORDER BY rank_address DESC, isaddress DESC';
 
         $this->aAddressLines = chksql($oDB->getAll($sSQL));
     }
@@ -40,7 +40,7 @@ class AddressDetails
             return $this->aAddressLines;
         }
 
-        return array_filter($this->aAddressLines, 'AddressDetails::isAddress');
+        return array_filter($this->aAddressLines, array(__CLASS__, 'isAddress'));
     }
 
     public function getLocaleAddress()
index 35424b529244c9071a89d808a2419e3dba00f0ab..ec14e54600ee11f106d83b54ab6510b9b98be708 100644 (file)
@@ -701,7 +701,7 @@ class SearchDescription
         }
 
         if ($this->sHouseNumber || $this->sClass) {
-            $iLimit = 20;
+            $iLimit = 40;
         }
 
         $aResults = array();
diff --git a/lib/setup/SetupClass.php b/lib/setup/SetupClass.php
new file mode 100755 (executable)
index 0000000..bd53260
--- /dev/null
@@ -0,0 +1,863 @@
+<?php
+
+namespace Nominatim\Setup;
+
+class SetupFunctions
+{
+    protected $iCacheMemory;            // set in constructor
+    protected $iInstances;              // set in constructor
+    protected $sModulePath;             // set in constructor
+    protected $aDSNInfo;                // set in constructor = DB::parseDSN(CONST_Database_DSN);
+    protected $sVerbose;                // set in constructor
+    protected $sIgnoreErrors;           // set in constructor
+    protected $bEnableDiffUpdates;      // set in constructor
+    protected $bEnableDebugStatements;  // set in constructor
+    protected $bNoPartitions;           // set in constructor
+    protected $oDB = null;              // set in setupDB (earliest) or later in loadData, importData, drop, createSqlFunctions, importTigerData
+                                                // pgsqlRunPartitionScript, calculatePostcodes, ..if no already set
+
+    public function __construct(array $aCMDResult)
+    {
+        // by default, use all but one processor, but never more than 15.
+        $this->iInstances = isset($aCMDResult['threads'])
+            ? $aCMDResult['threads']
+            : (min(16, getProcessorCount()) - 1);
+
+        if ($this->iInstances < 1) {
+            $this->iInstances = 1;
+            warn('resetting threads to '.$this->iInstances);
+        }
+
+        // Assume we can steal all the cache memory in the box (unless told otherwise)
+        if (isset($aCMDResult['osm2pgsql-cache'])) {
+            $this->iCacheMemory = $aCMDResult['osm2pgsql-cache'];
+        } else {
+            $this->iCacheMemory = getCacheMemoryMB();
+        }
+
+        $this->sModulePath = CONST_Database_Module_Path;
+        info('module path: ' . $this->sModulePath);
+
+        // prepares DB for import or update, sets the Data Source Name
+        $this->aDSNInfo = \DB::parseDSN(CONST_Database_DSN);
+        if (!isset($this->aDSNInfo['port']) || !$this->aDSNInfo['port']) $this->aDSNInfo['port'] = 5432;
+
+        // setting member variables based on command line options stored in $aCMDResult
+        $this->sVerbose = $aCMDResult['verbose'];
+
+        //setting default values which are not set by the update.php array
+        if (isset($aCMDResult['ignore-errors'])) {
+            $this->sIgnoreErrors = $aCMDResult['ignore-errors'];
+        } else {
+            $this->sIgnoreErrors = false;
+        }
+        if (isset($aCMDResult['enable-debug-statements'])) {
+            $this->bEnableDebugStatements = $aCMDResult['enable-debug-statements'];
+        } else {
+            $this->bEnableDebugStatements = false;
+        }
+        if (isset($aCMDResult['no-partitions'])) {
+            $this->bNoPartitions = $aCMDResult['no-partitions'];
+        } else {
+            $this->bNoPartitions = false;
+        }
+        if (isset($aCMDResult['enable-diff-updates'])) {
+            $this->bEnableDiffUpdates = $aCMDResult['enable-diff-updates'];
+        } else {
+            $this->bEnableDiffUpdates = false;
+        }
+    }
+
+    public function createDB()
+    {
+        info('Create DB');
+        $sDB = \DB::connect(CONST_Database_DSN, false);
+        if (!\PEAR::isError($sDB)) {
+            fail('database already exists ('.CONST_Database_DSN.')');
+        }
+
+        $sCreateDBCmd = 'createdb -E UTF-8 -p '.$this->aDSNInfo['port'].' '.$this->aDSNInfo['database'];
+        if (isset($this->aDSNInfo['username']) && $this->aDSNInfo['username']) {
+            $sCreateDBCmd .= ' -U '.$this->aDSNInfo['username'];
+        }
+
+        if (isset($this->aDSNInfo['hostspec']) && $this->aDSNInfo['hostspec']) {
+            $sCreateDBCmd .= ' -h '.$this->aDSNInfo['hostspec'];
+        }
+
+        $aProcEnv = null;
+        if (isset($this->aDSNInfo['password']) && $this->aDSNInfo['password']) {
+            $aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
+        }
+
+        $result = runWithEnv($sCreateDBCmd, $aProcEnv);
+        if ($result != 0) fail('Error executing external command: '.$sCreateDBCmd);
+    }
+
+    public function setupDB()
+    {
+        info('Setup DB');
+        $this->oDB =& getDB();
+
+        $fPostgresVersion = getPostgresVersion($this->oDB);
+        echo 'Postgres version found: '.$fPostgresVersion."\n";
+
+        if ($fPostgresVersion < 9.1) {
+            fail('Minimum supported version of Postgresql is 9.1.');
+        }
+
+        $this->pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS hstore');
+        $this->pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS postgis');
+
+        // For extratags and namedetails the hstore_to_json converter is
+        // needed which is only available from Postgresql 9.3+. For older
+        // versions add a dummy function that returns nothing.
+        $iNumFunc = chksql($this->oDB->getOne("select count(*) from pg_proc where proname = 'hstore_to_json'"));
+
+        if ($iNumFunc == 0) {
+            $this->pgsqlRunScript("create function hstore_to_json(dummy hstore) returns text AS 'select null::text' language sql immutable");
+            warn('Postgresql is too old. extratags and namedetails API not available.');
+        }
+
+
+        $fPostgisVersion = getPostgisVersion($this->oDB);
+        echo 'Postgis version found: '.$fPostgisVersion."\n";
+
+        if ($fPostgisVersion < 2.1) {
+            // Functions were renamed in 2.1 and throw an annoying deprecation warning
+            $this->pgsqlRunScript('ALTER FUNCTION st_line_interpolate_point(geometry, double precision) RENAME TO ST_LineInterpolatePoint');
+            $this->pgsqlRunScript('ALTER FUNCTION ST_Line_Locate_Point(geometry, geometry) RENAME TO ST_LineLocatePoint');
+        }
+        if ($fPostgisVersion < 2.2) {
+            $this->pgsqlRunScript('ALTER FUNCTION ST_Distance_Spheroid(geometry, geometry, spheroid) RENAME TO ST_DistanceSpheroid');
+        }
+
+        $i = chksql($this->oDB->getOne("select count(*) from pg_user where usename = '".CONST_Database_Web_User."'"));
+        if ($i == 0) {
+            echo "\nERROR: Web user '".CONST_Database_Web_User."' does not exist. Create it with:\n";
+            echo "\n          createuser ".CONST_Database_Web_User."\n\n";
+            exit(1);
+        }
+
+        // Try accessing the C module, so we know early if something is wrong
+        if (!checkModulePresence()) {
+            fail('error loading nominatim.so module');
+        }
+
+        if (!file_exists(CONST_ExtraDataPath.'/country_osm_grid.sql.gz')) {
+            echo 'Error: you need to download the country_osm_grid first:';
+            echo "\n    wget -O ".CONST_ExtraDataPath."/country_osm_grid.sql.gz https://www.nominatim.org/data/country_grid.sql.gz\n";
+            exit(1);
+        }
+        $this->pgsqlRunScriptFile(CONST_BasePath.'/data/country_name.sql');
+        $this->pgsqlRunScriptFile(CONST_BasePath.'/data/country_naturalearthdata.sql');
+        $this->pgsqlRunScriptFile(CONST_BasePath.'/data/country_osm_grid.sql.gz');
+        $this->pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_table.sql');
+
+        if (file_exists(CONST_BasePath.'/data/gb_postcode_data.sql.gz')) {
+            $this->pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_data.sql.gz');
+        } else {
+            warn('external UK postcode table not found.');
+        }
+
+        if (CONST_Use_Extra_US_Postcodes) {
+            $this->pgsqlRunScriptFile(CONST_BasePath.'/data/us_postcode.sql');
+        }
+
+        if ($this->bNoPartitions) {
+            $this->pgsqlRunScript('update country_name set partition = 0');
+        }
+
+        // the following will be needed by createFunctions later but
+        // is only defined in the subsequently called createTables
+        // Create dummies here that will be overwritten by the proper
+        // versions in create-tables.
+        $this->pgsqlRunScript('CREATE TABLE IF NOT EXISTS place_boundingbox ()');
+        $this->pgsqlRunScript('CREATE TYPE wikipedia_article_match AS ()', false);
+    }
+
+    public function importData($sOSMFile)
+    {
+        info('Import data');
+
+        $osm2pgsql = CONST_Osm2pgsql_Binary;
+        if (!file_exists($osm2pgsql)) {
+            echo "Check CONST_Osm2pgsql_Binary in your local settings file.\n";
+            echo "Normally you should not need to set this manually.\n";
+            fail("osm2pgsql not found in '$osm2pgsql'");
+        }
+
+        if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
+            $osm2pgsql .= ' --flat-nodes '.CONST_Osm2pgsql_Flatnode_File;
+        }
+
+        if (CONST_Tablespace_Osm2pgsql_Data)
+            $osm2pgsql .= ' --tablespace-slim-data '.CONST_Tablespace_Osm2pgsql_Data;
+        if (CONST_Tablespace_Osm2pgsql_Index)
+            $osm2pgsql .= ' --tablespace-slim-index '.CONST_Tablespace_Osm2pgsql_Index;
+        if (CONST_Tablespace_Place_Data)
+            $osm2pgsql .= ' --tablespace-main-data '.CONST_Tablespace_Place_Data;
+        if (CONST_Tablespace_Place_Index)
+            $osm2pgsql .= ' --tablespace-main-index '.CONST_Tablespace_Place_Index;
+        $osm2pgsql .= ' -lsc -O gazetteer --hstore --number-processes 1';
+        $osm2pgsql .= ' -C '.$this->iCacheMemory;
+        $osm2pgsql .= ' -P '.$this->aDSNInfo['port'];
+        if (isset($this->aDSNInfo['username']) && $this->aDSNInfo['username']) {
+            $osm2pgsql .= ' -U '.$this->aDSNInfo['username'];
+        }
+        if (isset($this->aDSNInfo['hostspec']) && $this->aDSNInfo['hostspec']) {
+            $osm2pgsql .= ' -H '.$this->aDSNInfo['hostspec'];
+        }
+        $aProcEnv = null;
+        if (isset($this->aDSNInfo['password']) && $this->aDSNInfo['password']) {
+            $aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
+        }
+        $osm2pgsql .= ' -d '.$this->aDSNInfo['database'].' '.$sOSMFile;
+        runWithEnv($osm2pgsql, $aProcEnv);
+        if ($this->oDB == null) $this->oDB =& getDB();
+        if (!$this->sIgnoreErrors && !chksql($this->oDB->getRow('select * from place limit 1'))) {
+            fail('No Data');
+        }
+    }
+
+    public function createFunctions()
+    {
+        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');
+        }
+        $this->createSqlFunctions();
+    }
+
+    public function createTables()
+    {
+        info('Create Tables');
+
+        $sTemplate = file_get_contents(CONST_BasePath.'/sql/tables.sql');
+        $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
+        $sTemplate = $this->replaceTablespace(
+            '{ts:address-data}',
+            CONST_Tablespace_Address_Data,
+            $sTemplate
+        );
+        $sTemplate = $this->replaceTablespace(
+            '{ts:address-index}',
+            CONST_Tablespace_Address_Index,
+            $sTemplate
+        );
+        $sTemplate = $this->replaceTablespace(
+            '{ts:search-data}',
+            CONST_Tablespace_Search_Data,
+            $sTemplate
+        );
+        $sTemplate = $this->replaceTablespace(
+            '{ts:search-index}',
+            CONST_Tablespace_Search_Index,
+            $sTemplate
+        );
+        $sTemplate = $this->replaceTablespace(
+            '{ts:aux-data}',
+            CONST_Tablespace_Aux_Data,
+            $sTemplate
+        );
+        $sTemplate = $this->replaceTablespace(
+            '{ts:aux-index}',
+            CONST_Tablespace_Aux_Index,
+            $sTemplate
+        );
+
+        $this->pgsqlRunScript($sTemplate, false);
+    }
+
+    public function createPartitionTables()
+    {
+        info('Create Partition Tables');
+
+        $sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-tables.src.sql');
+        $sTemplate = $this->replaceTablespace(
+            '{ts:address-data}',
+            CONST_Tablespace_Address_Data,
+            $sTemplate
+        );
+
+        $sTemplate = $this->replaceTablespace(
+            '{ts:address-index}',
+            CONST_Tablespace_Address_Index,
+            $sTemplate
+        );
+
+        $sTemplate = $this->replaceTablespace(
+            '{ts:search-data}',
+            CONST_Tablespace_Search_Data,
+            $sTemplate
+        );
+
+        $sTemplate = $this->replaceTablespace(
+            '{ts:search-index}',
+            CONST_Tablespace_Search_Index,
+            $sTemplate
+        );
+
+        $sTemplate = $this->replaceTablespace(
+            '{ts:aux-data}',
+            CONST_Tablespace_Aux_Data,
+            $sTemplate
+        );
+
+        $sTemplate = $this->replaceTablespace(
+            '{ts:aux-index}',
+            CONST_Tablespace_Aux_Index,
+            $sTemplate
+        );
+
+        $this->pgsqlRunPartitionScript($sTemplate);
+    }
+
+    public function createPartitionFunctions()
+    {
+        info('Create Partition Functions');
+
+        $sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-functions.src.sql');
+        $this->pgsqlRunPartitionScript($sTemplate);
+    }
+
+    public function importWikipediaArticles()
+    {
+        $sWikiArticlesFile = CONST_Wikipedia_Data_Path.'/wikipedia_article.sql.bin';
+        $sWikiRedirectsFile = CONST_Wikipedia_Data_Path.'/wikipedia_redirect.sql.bin';
+        if (file_exists($sWikiArticlesFile)) {
+            info('Importing wikipedia articles');
+            $this->pgsqlRunDropAndRestore($sWikiArticlesFile);
+        } else {
+            warn('wikipedia article dump file not found - places will have default importance');
+        }
+        if (file_exists($sWikiRedirectsFile)) {
+            info('Importing wikipedia redirects');
+            $this->pgsqlRunDropAndRestore($sWikiRedirectsFile);
+        } else {
+            warn('wikipedia redirect dump file not found - some place importance values may be missing');
+        }
+    }
+
+    public function loadData($bDisableTokenPrecalc)
+    {
+        info('Drop old Data');
+
+        if ($this->oDB == null) $this->oDB =& getDB();
+
+        if (!pg_query($this->oDB->connection, 'TRUNCATE word')) fail(pg_last_error($this->oDB->connection));
+        echo '.';
+        if (!pg_query($this->oDB->connection, 'TRUNCATE placex')) fail(pg_last_error($this->oDB->connection));
+        echo '.';
+        if (!pg_query($this->oDB->connection, 'TRUNCATE location_property_osmline')) fail(pg_last_error($this->oDB->connection));
+        echo '.';
+        if (!pg_query($this->oDB->connection, 'TRUNCATE place_addressline')) fail(pg_last_error($this->oDB->connection));
+        echo '.';
+        if (!pg_query($this->oDB->connection, 'TRUNCATE place_boundingbox')) fail(pg_last_error($this->oDB->connection));
+        echo '.';
+        if (!pg_query($this->oDB->connection, 'TRUNCATE location_area')) fail(pg_last_error($this->oDB->connection));
+        echo '.';
+        if (!pg_query($this->oDB->connection, 'TRUNCATE search_name')) fail(pg_last_error($this->oDB->connection));
+        echo '.';
+        if (!pg_query($this->oDB->connection, 'TRUNCATE search_name_blank')) fail(pg_last_error($this->oDB->connection));
+        echo '.';
+        if (!pg_query($this->oDB->connection, 'DROP SEQUENCE seq_place')) fail(pg_last_error($this->oDB->connection));
+        echo '.';
+        if (!pg_query($this->oDB->connection, 'CREATE SEQUENCE seq_place start 100000')) fail(pg_last_error($this->oDB->connection));
+        echo '.';
+
+        $sSQL = 'select distinct partition from country_name';
+        $aPartitions = chksql($this->oDB->getCol($sSQL));
+        if (!$this->bNoPartitions) $aPartitions[] = 0;
+        foreach ($aPartitions as $sPartition) {
+            if (!pg_query($this->oDB->connection, 'TRUNCATE location_road_'.$sPartition)) fail(pg_last_error($this->oDB->connection));
+            echo '.';
+        }
+
+        // used by getorcreate_word_id to ignore frequent partial words
+        $sSQL = 'CREATE OR REPLACE FUNCTION get_maxwordfreq() RETURNS integer AS ';
+        $sSQL .= '$$ SELECT '.CONST_Max_Word_Frequency.' as maxwordfreq; $$ LANGUAGE SQL IMMUTABLE';
+        if (!pg_query($this->oDB->connection, $sSQL)) {
+            fail(pg_last_error($this->oDB->connection));
+        }
+        echo ".\n";
+
+        // pre-create the word list
+        if (!$bDisableTokenPrecalc) {
+            info('Loading word list');
+            $this->pgsqlRunScriptFile(CONST_BasePath.'/data/words.sql');
+        }
+
+        info('Load Data');
+        $sColumns = 'osm_type, osm_id, class, type, name, admin_level, address, extratags, geometry';
+        $aDBInstances = array();
+        $iLoadThreads = max(1, $this->iInstances - 1);
+        for ($i = 0; $i < $iLoadThreads; $i++) {
+            $aDBInstances[$i] =& getDB(true);
+            $sSQL = "INSERT INTO placex ($sColumns) SELECT $sColumns FROM place WHERE osm_id % $iLoadThreads = $i";
+            $sSQL .= " and not (class='place' and type='houses' and osm_type='W'";
+            $sSQL .= "          and ST_GeometryType(geometry) = 'ST_LineString')";
+            $sSQL .= ' and ST_IsValid(geometry)';
+            if ($this->sVerbose) echo "$sSQL\n";
+            if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) {
+                fail(pg_last_error($aDBInstances[$i]->connection));
+            }
+        }
+
+        // last thread for interpolation lines
+        $aDBInstances[$iLoadThreads] =& getDB(true);
+        $sSQL = 'insert into location_property_osmline';
+        $sSQL .= ' (osm_id, address, linegeo)';
+        $sSQL .= ' SELECT osm_id, address, geometry from place where ';
+        $sSQL .= "class='place' and type='houses' and osm_type='W' and ST_GeometryType(geometry) = 'ST_LineString'";
+        if ($this->sVerbose) echo "$sSQL\n";
+        if (!pg_send_query($aDBInstances[$iLoadThreads]->connection, $sSQL)) {
+            fail(pg_last_error($aDBInstances[$iLoadThreads]->connection));
+        }
+
+        $bFailed = false;
+        for ($i = 0; $i <= $iLoadThreads; $i++) {
+            while (($hPGresult = pg_get_result($aDBInstances[$i]->connection)) !== false) {
+                $resultStatus = pg_result_status($hPGresult);
+                // PGSQL_EMPTY_QUERY, PGSQL_COMMAND_OK, PGSQL_TUPLES_OK,
+                // PGSQL_COPY_OUT, PGSQL_COPY_IN, PGSQL_BAD_RESPONSE,
+                // PGSQL_NONFATAL_ERROR and PGSQL_FATAL_ERROR
+                // echo 'Query result ' . $i . ' is: ' . $resultStatus . "\n";
+                if ($resultStatus != PGSQL_COMMAND_OK && $resultStatus != PGSQL_TUPLES_OK) {
+                    $resultError = pg_result_error($hPGresult);
+                    echo '-- error text ' . $i . ': ' . $resultError . "\n";
+                    $bFailed = true;
+                }
+            }
+        }
+        if ($bFailed) {
+            fail('SQL errors loading placex and/or location_property_osmline tables');
+        }
+        echo "\n";
+        info('Reanalysing database');
+        $this->pgsqlRunScript('ANALYSE');
+
+        $sDatabaseDate = getDatabaseDate($this->oDB);
+        pg_query($this->oDB->connection, 'TRUNCATE import_status');
+        if ($sDatabaseDate === false) {
+            warn('could not determine database date.');
+        } else {
+            $sSQL = "INSERT INTO import_status (lastimportdate) VALUES('".$sDatabaseDate."')";
+            pg_query($this->oDB->connection, $sSQL);
+            echo "Latest data imported from $sDatabaseDate.\n";
+        }
+    }
+
+    public function importTigerData()
+    {
+        info('Import Tiger data');
+
+        $sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_start.sql');
+        $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
+        $sTemplate = $this->replaceTablespace(
+            '{ts:aux-data}',
+            CONST_Tablespace_Aux_Data,
+            $sTemplate
+        );
+        $sTemplate = $this->replaceTablespace(
+            '{ts:aux-index}',
+            CONST_Tablespace_Aux_Index,
+            $sTemplate
+        );
+        $this->pgsqlRunScript($sTemplate, false);
+
+        $aDBInstances = array();
+        for ($i = 0; $i < $this->iInstances; $i++) {
+            $aDBInstances[$i] =& getDB(true);
+        }
+
+        foreach (glob(CONST_Tiger_Data_Path.'/*.sql') as $sFile) {
+            echo $sFile.': ';
+            $hFile = fopen($sFile, 'r');
+            $sSQL = fgets($hFile, 100000);
+            $iLines = 0;
+            while (true) {
+                for ($i = 0; $i < $this->iInstances; $i++) {
+                    if (!pg_connection_busy($aDBInstances[$i]->connection)) {
+                        while (pg_get_result($aDBInstances[$i]->connection));
+                        $sSQL = fgets($hFile, 100000);
+                        if (!$sSQL) break 2;
+                        if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) fail(pg_last_error($this->oDB->connection));
+                        $iLines++;
+                        if ($iLines == 1000) {
+                            echo '.';
+                            $iLines = 0;
+                        }
+                    }
+                }
+                usleep(10);
+            }
+            fclose($hFile);
+
+            $bAnyBusy = true;
+            while ($bAnyBusy) {
+                $bAnyBusy = false;
+                for ($i = 0; $i < $this->iInstances; $i++) {
+                    if (pg_connection_busy($aDBInstances[$i]->connection)) $bAnyBusy = true;
+                }
+                usleep(10);
+            }
+            echo "\n";
+        }
+
+        info('Creating indexes on Tiger data');
+        $sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_finish.sql');
+        $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
+        $sTemplate = $this->replaceTablespace(
+            '{ts:aux-data}',
+            CONST_Tablespace_Aux_Data,
+            $sTemplate
+        );
+        $sTemplate = $this->replaceTablespace(
+            '{ts:aux-index}',
+            CONST_Tablespace_Aux_Index,
+            $sTemplate
+        );
+        $this->pgsqlRunScript($sTemplate, false);
+    }
+
+    public function calculatePostcodes($bCMDResultAll)
+    {
+        info('Calculate Postcodes');
+        if ($this->oDB == null) $this->oDB =& getDB();
+        if (!pg_query($this->oDB->connection, 'TRUNCATE location_postcode')) {
+            fail(pg_last_error($this->oDB->connection));
+        }
+
+
+        $sSQL  = 'INSERT INTO location_postcode';
+        $sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
+        $sSQL .= "SELECT nextval('seq_place'), 1, country_code,";
+        $sSQL .= "       upper(trim (both ' ' from address->'postcode')) as pc,";
+        $sSQL .= '       ST_Centroid(ST_Collect(ST_Centroid(geometry)))';
+        $sSQL .= '  FROM placex';
+        $sSQL .= " WHERE address ? 'postcode' AND address->'postcode' NOT SIMILAR TO '%(,|;)%'";
+        $sSQL .= '       AND geometry IS NOT null';
+        $sSQL .= ' GROUP BY country_code, pc';
+
+        if (!pg_query($this->oDB->connection, $sSQL)) {
+            fail(pg_last_error($this->oDB->connection));
+        }
+
+        if (CONST_Use_Extra_US_Postcodes) {
+            // only add postcodes that are not yet available in OSM
+            $sSQL  = 'INSERT INTO location_postcode';
+            $sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
+            $sSQL .= "SELECT nextval('seq_place'), 1, 'us', postcode,";
+            $sSQL .= '       ST_SetSRID(ST_Point(x,y),4326)';
+            $sSQL .= '  FROM us_postcode WHERE postcode NOT IN';
+            $sSQL .= '        (SELECT postcode FROM location_postcode';
+            $sSQL .= "          WHERE country_code = 'us')";
+            if (!pg_query($this->oDB->connection, $sSQL)) fail(pg_last_error($this->oDB->connection));
+        }
+
+        // add missing postcodes for GB (if available)
+        $sSQL  = 'INSERT INTO location_postcode';
+        $sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
+        $sSQL .= "SELECT nextval('seq_place'), 1, 'gb', postcode, geometry";
+        $sSQL .= '  FROM gb_postcode WHERE postcode NOT IN';
+        $sSQL .= '           (SELECT postcode FROM location_postcode';
+        $sSQL .= "             WHERE country_code = 'gb')";
+        if (!pg_query($this->oDB->connection, $sSQL)) fail(pg_last_error($this->oDB->connection));
+
+        if (!$bCMDResultAll) {
+            $sSQL = "DELETE FROM word WHERE class='place' and type='postcode'";
+            $sSQL .= 'and word NOT IN (SELECT postcode FROM location_postcode)';
+            if (!pg_query($this->oDB->connection, $sSQL)) {
+                fail(pg_last_error($this->oDB->connection));
+            }
+        }
+        $sSQL = 'SELECT count(getorcreate_postcode_id(v)) FROM ';
+        $sSQL .= '(SELECT distinct(postcode) as v FROM location_postcode) p';
+
+        if (!pg_query($this->oDB->connection, $sSQL)) {
+            fail(pg_last_error($this->oDB->connection));
+        }
+    }
+
+    public function index($bIndexNoanalyse)
+    {
+        $sOutputFile = '';
+        $sBaseCmd = CONST_InstallPath.'/nominatim/nominatim -i -d '.$this->aDSNInfo['database'].' -P '
+            .$this->aDSNInfo['port'].' -t '.$this->iInstances.$sOutputFile;
+        if (isset($this->aDSNInfo['hostspec']) && $this->aDSNInfo['hostspec']) {
+            $sBaseCmd .= ' -H '.$this->aDSNInfo['hostspec'];
+        }
+        if (isset($this->aDSNInfo['username']) && $this->aDSNInfo['username']) {
+            $sBaseCmd .= ' -U '.$this->aDSNInfo['username'];
+        }
+        $aProcEnv = null;
+        if (isset($this->aDSNInfo['password']) && $this->aDSNInfo['password']) {
+            $aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
+        }
+
+        info('Index ranks 0 - 4');
+        $iStatus = runWithEnv($sBaseCmd.' -R 4', $aProcEnv);
+        if ($iStatus != 0) {
+            fail('error status ' . $iStatus . ' running nominatim!');
+        }
+        if (!$bIndexNoanalyse) $this->pgsqlRunScript('ANALYSE');
+        info('Index ranks 5 - 25');
+        $iStatus = runWithEnv($sBaseCmd.' -r 5 -R 25', $aProcEnv);
+        if ($iStatus != 0) {
+            fail('error status ' . $iStatus . ' running nominatim!');
+        }
+        if (!$bIndexNoanalyse) $this->pgsqlRunScript('ANALYSE');
+        info('Index ranks 26 - 30');
+        $iStatus = runWithEnv($sBaseCmd.' -r 26', $aProcEnv);
+        if ($iStatus != 0) {
+            fail('error status ' . $iStatus . ' running nominatim!');
+        }
+        info('Index postcodes');
+        if ($this->oDB == null) $this->oDB =& getDB();
+        $sSQL = 'UPDATE location_postcode SET indexed_status = 0';
+        if (!pg_query($this->oDB->connection, $sSQL)) fail(pg_last_error($this->oDB->connection));
+    }
+
+    public function createSearchIndices()
+    {
+        info('Create Search indices');
+
+        $sTemplate = file_get_contents(CONST_BasePath.'/sql/indices.src.sql');
+        $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
+        $sTemplate = $this->replaceTablespace(
+            '{ts:address-index}',
+            CONST_Tablespace_Address_Index,
+            $sTemplate
+        );
+        $sTemplate = $this->replaceTablespace(
+            '{ts:search-index}',
+            CONST_Tablespace_Search_Index,
+            $sTemplate
+        );
+        $sTemplate = $this->replaceTablespace(
+            '{ts:aux-index}',
+            CONST_Tablespace_Aux_Index,
+            $sTemplate
+        );
+        $this->pgsqlRunScript($sTemplate);
+    }
+
+    public function createCountryNames()
+    {
+        info('Create search index for default country names');
+
+        $this->pgsqlRunScript("select getorcreate_country(make_standard_name('uk'), 'gb')");
+        $this->pgsqlRunScript("select getorcreate_country(make_standard_name('united states'), 'us')");
+        $this->pgsqlRunScript('select count(*) from (select getorcreate_country(make_standard_name(country_code), country_code) from country_name where country_code is not null) as x');
+        $this->pgsqlRunScript("select count(*) from (select getorcreate_country(make_standard_name(name->'name'), country_code) from country_name where name ? 'name') as x");
+        $sSQL = 'select count(*) from (select getorcreate_country(make_standard_name(v),'
+            .'country_code) from (select country_code, skeys(name) as k, svals(name) as v from country_name) x where k ';
+        if (CONST_Languages) {
+            $sSQL .= 'in ';
+            $sDelim = '(';
+            foreach (explode(',', CONST_Languages) as $sLang) {
+                $sSQL .= $sDelim."'name:$sLang'";
+                $sDelim = ',';
+            }
+            $sSQL .= ')';
+        } else {
+            // all include all simple name tags
+            $sSQL .= "like 'name:%'";
+        }
+        $sSQL .= ') v';
+        $this->pgsqlRunScript($sSQL);
+    }
+
+    public function drop()
+    {
+        info('Drop tables only required for updates');
+
+        // The implementation is potentially a bit dangerous because it uses
+        // a positive selection of tables to keep, and deletes everything else.
+        // Including any tables that the unsuspecting user might have manually
+        // created. USE AT YOUR OWN PERIL.
+        // tables we want to keep. everything else goes.
+        $aKeepTables = array(
+                        '*columns',
+                        'import_polygon_*',
+                        'import_status',
+                        'place_addressline',
+                        'location_postcode',
+                        'location_property*',
+                        'placex',
+                        'search_name',
+                        'seq_*',
+                        'word',
+                        'query_log',
+                        'new_query_log',
+                        'spatial_ref_sys',
+                        'country_name',
+                        'place_classtype_*'
+                       );
+
+        if ($this->oDB = null) $this->oDB =& getDB();
+        $aDropTables = array();
+        $aHaveTables = chksql($this->oDB->getCol("SELECT tablename FROM pg_tables WHERE schemaname='public'"));
+
+        foreach ($aHaveTables as $sTable) {
+            $bFound = false;
+            foreach ($aKeepTables as $sKeep) {
+                if (fnmatch($sKeep, $sTable)) {
+                    $bFound = true;
+                    break;
+                }
+            }
+            if (!$bFound) array_push($aDropTables, $sTable);
+        }
+        foreach ($aDropTables as $sDrop) {
+            if ($this->sVerbose) echo "dropping table $sDrop\n";
+            @pg_query($this->oDB->connection, "DROP TABLE $sDrop CASCADE");
+            // ignore warnings/errors as they might be caused by a table having
+            // been deleted already by CASCADE
+        }
+
+        if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
+            if ($sVerbose) echo 'deleting '.CONST_Osm2pgsql_Flatnode_File."\n";
+            unlink(CONST_Osm2pgsql_Flatnode_File);
+        }
+    }
+
+    private function pgsqlRunDropAndRestore($sDumpFile)
+    {
+        if (!isset($this->aDSNInfo['port']) || !$this->aDSNInfo['port']) $this->aDSNInfo['port'] = 5432;
+        $sCMD = 'pg_restore -p '.$this->aDSNInfo['port'].' -d '.$this->aDSNInfo['database'].' -Fc --clean '.$sDumpFile;
+        if (isset($this->aDSNInfo['hostspec']) && $this->aDSNInfo['hostspec']) {
+            $sCMD .= ' -h '.$this->aDSNInfo['hostspec'];
+        }
+        if (isset($this->aDSNInfo['username']) && $this->aDSNInfo['username']) {
+            $sCMD .= ' -U '.$this->aDSNInfo['username'];
+        }
+        $aProcEnv = null;
+        if (isset($this->aDSNInfo['password']) && $this->aDSNInfo['password']) {
+            $aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
+        }
+        $iReturn = runWithEnv($sCMD, $aProcEnv);    // /lib/cmd.php "function runWithEnv($sCmd, $aEnv)"
+    }
+
+    private function pgsqlRunScript($sScript, $bfatal = true)
+    {
+        runSQLScript(
+            $sScript,
+            $bfatal,
+            $this->sVerbose,
+            $this->sIgnoreErrors
+        );
+    }
+
+    private function createSqlFunctions()
+    {
+        $sTemplate = file_get_contents(CONST_BasePath.'/sql/functions.sql');
+        $sTemplate = str_replace('{modulepath}', $this->sModulePath, $sTemplate);
+        if ($this->bEnableDiffUpdates) {
+            $sTemplate = str_replace('RETURN NEW; -- %DIFFUPDATES%', '--', $sTemplate);
+        }
+        if ($this->bEnableDebugStatements) {
+            $sTemplate = str_replace('--DEBUG:', '', $sTemplate);
+        }
+        if (CONST_Limit_Reindexing) {
+            $sTemplate = str_replace('--LIMIT INDEXING:', '', $sTemplate);
+        }
+        if (!CONST_Use_US_Tiger_Data) {
+            $sTemplate = str_replace('-- %NOTIGERDATA% ', '', $sTemplate);
+        }
+        if (!CONST_Use_Aux_Location_data) {
+            $sTemplate = str_replace('-- %NOAUXDATA% ', '', $sTemplate);
+        }
+        $this->pgsqlRunScript($sTemplate);
+    }
+
+    private function pgsqlRunPartitionScript($sTemplate)
+    {
+        if ($this->oDB == null) $this->oDB =& getDB();
+
+        $sSQL = 'select distinct partition from country_name';
+        $aPartitions = chksql($this->oDB->getCol($sSQL));
+        if (!$this->bNoPartitions) $aPartitions[] = 0;
+
+        preg_match_all('#^-- start(.*?)^-- end#ms', $sTemplate, $aMatches, PREG_SET_ORDER);
+        foreach ($aMatches as $aMatch) {
+            $sResult = '';
+            foreach ($aPartitions as $sPartitionName) {
+                $sResult .= str_replace('-partition-', $sPartitionName, $aMatch[1]);
+            }
+            $sTemplate = str_replace($aMatch[0], $sResult, $sTemplate);
+        }
+
+        $this->pgsqlRunScript($sTemplate);
+    }
+
+    private function pgsqlRunScriptFile($sFilename)
+    {
+        if (!file_exists($sFilename)) fail('unable to find '.$sFilename);
+
+        $sCMD = 'psql -p '.$this->aDSNInfo['port'].' -d '.$this->aDSNInfo['database'];
+        if (!$this->sVerbose) {
+            $sCMD .= ' -q';
+        }
+        if (isset($this->aDSNInfo['hostspec']) && $this->aDSNInfo['hostspec']) {
+            $sCMD .= ' -h '.$this->aDSNInfo['hostspec'];
+        }
+        if (isset($this->aDSNInfo['username']) && $this->aDSNInfo['username']) {
+            $sCMD .= ' -U '.$this->aDSNInfo['username'];
+        }
+        $aProcEnv = null;
+        if (isset($this->aDSNInfo['password']) && $this->aDSNInfo['password']) {
+            $aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
+        }
+        $ahGzipPipes = null;
+        if (preg_match('/\\.gz$/', $sFilename)) {
+            $aDescriptors = array(
+                             0 => array('pipe', 'r'),
+                             1 => array('pipe', 'w'),
+                             2 => array('file', '/dev/null', 'a')
+                            );
+            $hGzipProcess = proc_open('zcat '.$sFilename, $aDescriptors, $ahGzipPipes);
+            if (!is_resource($hGzipProcess)) fail('unable to start zcat');
+            $aReadPipe = $ahGzipPipes[1];
+            fclose($ahGzipPipes[0]);
+        } else {
+            $sCMD .= ' -f '.$sFilename;
+            $aReadPipe = array('pipe', 'r');
+        }
+        $aDescriptors = array(
+                         0 => $aReadPipe,
+                         1 => array('pipe', 'w'),
+                         2 => array('file', '/dev/null', 'a')
+                        );
+        $ahPipes = null;
+        $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes, null, $aProcEnv);
+        if (!is_resource($hProcess)) fail('unable to start pgsql');
+        // TODO: error checking
+        while (!feof($ahPipes[1])) {
+            echo fread($ahPipes[1], 4096);
+        }
+        fclose($ahPipes[1]);
+        $iReturn = proc_close($hProcess);
+        if ($iReturn > 0) {
+            fail("pgsql returned with error code ($iReturn)");
+        }
+        if ($ahGzipPipes) {
+            fclose($ahGzipPipes[1]);
+            proc_close($hGzipProcess);
+        }
+    }
+
+    private function replaceTablespace($sTemplate, $sTablespace, $sSql)
+    {
+        if ($sTablespace) {
+            $sSql = str_replace($sTemplate, 'TABLESPACE "'.$sTablespace.'"', $sSql);
+        } else {
+            $sSql = str_replace($sTemplate, '', $sSql);
+        }
+        return $sSql;
+    }
+}
diff --git a/lib/setup_functions.php b/lib/setup_functions.php
new file mode 100755 (executable)
index 0000000..b141767
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+function checkInFile($sOSMFile)
+{
+    if (!isset($sOSMFile)) {
+        fail('missing --osm-file for data import');
+    }
+
+    if (!file_exists($sOSMFile)) {
+        fail('the path supplied to --osm-file does not exist');
+    }
+
+    if (!is_readable($sOSMFile)) {
+        fail('osm-file "' . $aCMDResult['osm-file'] . '" not readable');
+    }
+}
+
+function checkModulePresence()
+{
+    // Try accessing the C module, so we know early if something is wrong
+    // and can simply error out.
+    $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";
+    $sSQL .= ';DROP FUNCTION nominatim_test_import_func(text);';
+
+    $oDB = &getDB();
+    $oResult = $oDB->query($sSQL);
+
+    $bResult = true;
+
+    if (PEAR::isError($oResult)) {
+        echo "\nERROR: Failed to load nominatim module. Reason:\n";
+        echo $oResult->userinfo . "\n\n";
+        $bResult = false;
+    }
+    return $bResult;
+}
index 0487fd404da664e8f3a5891f36306c509cc7ac40..9b777412d20830d77d19800ef2fcc798f3f32699 100644 (file)
@@ -72,12 +72,12 @@ The tests can be configured with a set of environment variables:
  * `TEMPLATE_DB` - name of template database used as a skeleton for
                    the test databases (db tests)
  * `TEST_DB` - name of test database (db tests)
- * `ABI_TEST_DB` - name of the database containing the API test data (api tests)
+ * `API_TEST_DB` - name of the database containing the API test data (api tests)
  * `DB_HOST` - (optional) hostname of database host
  * `DB_USER` - (optional) username of database login
  * `DB_PASS` - (optional) password for database login
  * `SERVER_MODULE_PATH` - (optional) path on the Postgres server to Nominatim
*                        module shared library file
                         module shared library file
  * `TEST_SETTINGS_TEMPLATE` - file to write temporary Nominatim settings to
  * `REMOVE_TEMPLATE` - if true, the template database will not be reused during
                        the next run. Reusing the base templates speeds up tests
@@ -117,8 +117,8 @@ planets are likely to work as well but you may see isolated test
 failures where the data has changed. To recreate the input data
 for the test database run:
 
-    wget https://free.nchc.org.tw/osm.planet/pbf/planet-160725.osm.pbf
-    osmconvert planet-160725.osm.pbf -B=test/testdb/testdb.polys -o=testdb.pbf
+    wget https://ftp5.gwdg.de/pub/misc/openstreetmap/planet.openstreetmap.org/pbf/planet-180924.osm.pbf
+    osmconvert planet-180924.osm.pbf -B=test/testdb/testdb.polys -o=testdb.pbf
 
 Before importing make sure to add the following to your local settings:
 
index 9bde2d4e7eafcf59862c052674adf93a24d14374..9ead4e020289399f0b9f9e2ce6125c2c87ec7a53 100644 (file)
@@ -5,7 +5,7 @@ Feature: Localization of reverse search results
         When sending json reverse coordinates 18.1147,-15.95
         Then result addresses contain
           | ID | country |
-          | 0  | Mauritanie موريتانيا |
+          | 0  | موريتانيا |
 
     Scenario: accept-language parameter
         When sending json reverse coordinates 18.1147,-15.95
index 88f3bccbc34756e3b7b8284a541d14d323012864..1973f0b94aea217d8bdd6a28e66e1dced07dd96b 100644 (file)
@@ -31,7 +31,7 @@ Feature: Reverse geocoding
           | way      | place    | house |
         And result addresses contain
           | house_number | road |
-          | 1410         | Juan Antonio Lavalleja |
+          | 1416         | Juan Antonio Lavalleja |
 
     Scenario: Address with non-numerical house number
         When sending jsonv2 reverse coordinates 53.579805460944,9.9475670458196
@@ -50,7 +50,7 @@ Feature: Reverse geocoding
         When sending jsonv2 reverse coordinates 54.046489113,8.5546870529
         Then results contain
          | display_name |
-         | Freie und Hansestadt Hamburg, Deutschland |
+         | Hamburg, Deutschland |
 
     Scenario: When slightly outside town, the town is not shown
         When sending jsonv2 reverse coordinates -32.122,-56.114
index 5ef8f7ed8bbf9139f4edfef88238089734fb5f87..832d888468a9bd8027dd4e3f249c47339ee3093f 100644 (file)
@@ -19,30 +19,32 @@ Feature: Search queries
           | accept-language |
           | de |
         Then address of result 0 is
-          | type         | value |
-          | house_number | 86 |
-          | road         | Schellingstraße |
-          | suburb       | Eilbek |
-          | postcode     | 22089 |
+          | type          | value |
+          | house_number  | 86 |
+          | road          | Schellingstraße |
+          | neighbourhood | Auenviertel |
+          | suburb        | Eilbek |
+          | postcode      | 22089 |
           | city_district | Wandsbek |
-          | state        | Hamburg |
-          | country      | Deutschland |
-          | country_code | de |
+          | state         | Hamburg |
+          | country       | Deutschland |
+          | country_code  | de |
 
     Scenario: House number interpolation odd
         When sending json search query "Schellingstr 73, Hamburg" with address
           | accept-language |
           | de |
         Then address of result 0 is
-          | type         | value |
-          | house_number | 73 |
-          | road         | Schellingstraße |
-          | suburb       | Eilbek |
-          | postcode     | 22089 |
+          | type          | value |
+          | house_number  | 73 |
+          | road          | Schellingstraße |
+          | neighbourhood | Auenviertel |
+          | suburb        | Eilbek |
+          | postcode      | 22089 |
           | city_district | Wandsbek |
-          | state        | Hamburg |
-          | country      | Deutschland |
-          | country_code | de |
+          | state         | Hamburg |
+          | country       | Deutschland |
+          | country_code  | de |
 
     Scenario: With missing housenumber search falls back to road
         When sending json search query "342 rocha, santa lucia" with address
diff --git a/test/php/Nominatim/AddressDetailsTest.php b/test/php/Nominatim/AddressDetailsTest.php
new file mode 100644 (file)
index 0000000..62faf1a
--- /dev/null
@@ -0,0 +1,112 @@
+<?php
+
+namespace Nominatim;
+
+require_once(CONST_BasePath.'/lib/AddressDetails.php');
+
+
+function chksql($oSql, $sMsg = 'Database request failed')
+{
+    return $oSql;
+}
+
+class AddressDetailsTest extends \PHPUnit\Framework\TestCase
+{
+
+    protected function setUp()
+    {
+        // How the fixture got created
+        //
+        // 1) search for '10 downing street'
+        // https://nominatim.openstreetmap.org/details.php?osmtype=R&osmid=1879842
+        //
+        // 2) find place_id in the local database
+        // SELECT place_id, name FROM placex WHERE osm_type='R' AND osm_id=1879842;
+        //
+        // 3) set postgresql to non-align output, e.g. psql -A or \a in the CLI
+        //
+        // 4) query
+        // SELECT row_to_json(row,true) FROM (
+        //   SELECT *, get_name_by_language(name, ARRAY['name:en']) as localname
+        //   FROM get_addressdata(194663412,10)
+        //   ORDER BY rank_address DESC, isaddress DESC
+        // ) AS row;
+        //
+        // 5) copy&paste into file. Add commas between records
+        //
+        $json = file_get_contents(CONST_BasePath.'/test/php/fixtures/address_details_10_downing_street.json');
+        $data = json_decode($json, true);
+
+        $this->oDbStub = $this->getMockBuilder(\DB::class)
+                              ->setMethods(array('getAll'))
+                              ->getMock();
+        $this->oDbStub->method('getAll')
+                      ->willReturn($data);
+    }
+
+    public function testGetLocaleAddress()
+    {
+        $oAD = new AddressDetails($this->oDbStub, 194663412, 10, 'en');
+        $expected = join(', ', array(
+            '10 Downing Street',
+            '10',
+            'Downing Street',
+            'St. James\'s',
+            'Covent Garden',
+            'Westminster',
+            'London',
+            'Greater London',
+            'England',
+            'SW1A 2AA',
+            'United Kingdom'
+        ));
+        $this->assertEquals($expected, $oAD->getLocaleAddress());
+    }
+
+    public function testGetAddressDetails()
+    {
+        $oAD = new AddressDetails($this->oDbStub, 194663412, 10, 'en');
+        $this->assertEquals(18, count($oAD->getAddressDetails(true)));
+        $this->assertEquals(12, count($oAD->getAddressDetails(false)));
+    }
+
+    public function testGetAddressNames()
+    {
+        $oAD = new AddressDetails($this->oDbStub, 194663412, 10, 'en');
+        $expected = array(
+                     'attraction' => '10 Downing Street',
+                     'house_number' => '10',
+                     'road' => 'Downing Street',
+                     'neighbourhood' => 'St. James\'s',
+                     'suburb' => 'Covent Garden',
+                     'city' => 'London',
+                     'state_district' => 'Greater London',
+                     'state' => 'England',
+                     'postcode' => 'SW1A 2AA',
+                     'country' => 'United Kingdom',
+                     'country_code' => 'gb'
+        );
+
+        $this->assertEquals($expected, $oAD->getAddressNames());
+    }
+
+    public function testGetAdminLevels()
+    {
+        $oAD = new AddressDetails($this->oDbStub, 194663412, 10, 'en');
+        $expected = array(
+                     'level8' => 'Westminster',
+                     'level6' => 'London',
+                     'level5' => 'Greater London',
+                     'level4' => 'England',
+                     'level2' => 'United Kingdom'
+        );
+        $this->assertEquals($expected, $oAD->getAdminLevels());
+    }
+
+    public function testDebugInfo()
+    {
+        $oAD = new AddressDetails($this->oDbStub, 194663412, 10, 'en');
+        $this->assertTrue(is_array($oAD->debugInfo()));
+        $this->assertEquals(18, count($oAD->debugInfo()));
+    }
+}
diff --git a/test/php/Nominatim/ClassTypesTest.php b/test/php/Nominatim/ClassTypesTest.php
new file mode 100644 (file)
index 0000000..8d8481f
--- /dev/null
@@ -0,0 +1,94 @@
+<?php
+
+namespace Nominatim;
+
+require_once(CONST_BasePath.'/lib/ClassTypes.php');
+
+class ClassTypesTest extends \PHPUnit\Framework\TestCase
+{
+    public function testGetInfo()
+    {
+        // 1) Admin level set
+        // city Dublin
+        // https://nominatim.openstreetmap.org/details.php?osmtype=R&osmid=1109531
+        $aPlace = array(
+                   'admin_level' => 7,
+                   'class' => 'boundary',
+                   'type' => 'administrative',
+                   'rank_address' => 14
+        );
+
+        $this->assertEquals('County', ClassTypes\getInfo($aPlace)['label']);
+        $this->assertEquals('County', ClassTypes\getFallbackInfo($aPlace)['label']);
+        $this->assertEquals('County', ClassTypes\getProperty($aPlace, 'label'));
+
+        // 2) No admin level
+        // Eiffel Tower
+        // https://nominatim.openstreetmap.org/details.php?osmtype=W&osmid=5013364
+        $aPlace = array(
+                   'class' => 'tourism',
+                   'type' => 'attraction',
+                   'rank_address' => 29
+        );
+        $this->assertEquals('Attraction', ClassTypes\getInfo($aPlace)['label']);
+        $this->assertEquals(array('simplelabel' => 'address29'), ClassTypes\getFallbackInfo($aPlace));
+        $this->assertEquals('Attraction', ClassTypes\getProperty($aPlace, 'label'));
+
+        // 3) Unknown type
+        // La Maison du Toutou, Paris
+        // https://nominatim.openstreetmap.org/details.php?osmtype=W&osmid=164011651
+        $aPlace = array(
+                   'class' => 'shop',
+                   'type' => 'pet_grooming',
+                   'rank_address' => 29
+        );
+        $this->assertEquals(false, ClassTypes\getInfo($aPlace));
+        $this->assertEquals(array('simplelabel' => 'address29'), ClassTypes\getFallbackInfo($aPlace));
+        $this->assertEquals(false, ClassTypes\getProperty($aPlace, 'label'));
+        $this->assertEquals('mydefault', ClassTypes\getProperty($aPlace, 'label', 'mydefault'));
+    }
+
+    public function testGetClassTypesWithImportance()
+    {
+        $aClasses = ClassTypes\getListWithImportance();
+
+        $this->assertGreaterThan(
+            200,
+            count($aClasses)
+        );
+
+        $this->assertEquals(
+            array(
+             'label' => 'Country',
+             'frequency' => 0,
+             'icon' => 'poi_boundary_administrative',
+             'defzoom' => 6,
+             'defdiameter' => 15,
+             'importance' => 3
+            ),
+            $aClasses['place:country']
+        );
+    }
+
+
+    public function testGetResultDiameter()
+    {
+        $aResult = array('class' => '', 'type' => '');
+        $this->assertEquals(
+            0.0001,
+            ClassTypes\getProperty($aResult, 'defdiameter', 0.0001)
+        );
+
+        $aResult = array('class' => 'place', 'type' => 'country');
+        $this->assertEquals(
+            15,
+            ClassTypes\getProperty($aResult, 'defdiameter', 0.0001)
+        );
+
+        $aResult = array('class' => 'boundary', 'type' => 'administrative', 'admin_level' => 6);
+        $this->assertEquals(
+            0.32,
+            ClassTypes\getProperty($aResult, 'defdiameter', 0.0001)
+        );
+    }
+}
index d2237b602657939b44aadc158792c66701469403..2891388d371b83976a8b50ae9dce379b916e47df 100644 (file)
@@ -2,54 +2,8 @@
 
 namespace Nominatim;
 
-require_once(CONST_BasePath.'/lib/ClassTypes.php');
-
 class LibTest extends \PHPUnit\Framework\TestCase
 {
-    public function testGetClassTypesWithImportance()
-    {
-        $aClasses = ClassTypes\getListWithImportance();
-
-        $this->assertGreaterThan(
-            200,
-            count($aClasses)
-        );
-
-        $this->assertEquals(
-            array(
-             'label' => 'Country',
-             'frequency' => 0,
-             'icon' => 'poi_boundary_administrative',
-             'defzoom' => 6,
-             'defdiameter' => 15,
-             'importance' => 3
-            ),
-            $aClasses['place:country']
-        );
-    }
-
-
-    public function testGetResultDiameter()
-    {
-        $aResult = array('class' => '', 'type' => '');
-        $this->assertEquals(
-            0.0001,
-            ClassTypes\getProperty($aResult, 'defdiameter', 0.0001)
-        );
-
-        $aResult = array('class' => 'place', 'type' => 'country');
-        $this->assertEquals(
-            15,
-            ClassTypes\getProperty($aResult, 'defdiameter', 0.0001)
-        );
-
-        $aResult = array('class' => 'boundary', 'type' => 'administrative', 'admin_level' => 6);
-        $this->assertEquals(
-            0.32,
-            ClassTypes\getProperty($aResult, 'defdiameter', 0.0001)
-        );
-    }
-
 
     public function testAddQuotes()
     {
diff --git a/test/php/fixtures/address_details_10_downing_street.json b/test/php/fixtures/address_details_10_downing_street.json
new file mode 100644 (file)
index 0000000..853a6c0
--- /dev/null
@@ -0,0 +1,217 @@
+[{"place_id":194663412,
+ "osm_type":null,
+ "osm_id":null,
+ "name":{"name": "10 Downing Street", "name:en": "10 Downing Street", "name:es": "10 de Downing Street", "name:he": "דאונינג 10", "name:ko": "다우닝 가 10번지", "name:zh": "唐寧街10號"},
+ "class":"tourism",
+ "type":"attraction",
+ "admin_level":null,
+ "fromarea":true,
+ "isaddress":true,
+ "rank_address":29,
+ "distance":0,
+ "localname":"10 Downing Street"},
+{"place_id":194663412,
+ "osm_type":null,
+ "osm_id":null,
+ "name":{"ref": "10"},
+ "class":"place",
+ "type":"house_number",
+ "admin_level":null,
+ "fromarea":true,
+ "isaddress":true,
+ "rank_address":28,
+ "distance":0,
+ "localname":"10"},
+{"place_id":68310941,
+ "osm_type":"W",
+ "osm_id":4244999,
+ "name":{"name": "Downing Street"},
+ "class":"highway",
+ "type":"residential",
+ "admin_level":15,
+ "fromarea":true,
+ "isaddress":true,
+ "rank_address":26,
+ "distance":0,
+ "localname":"Downing Street"},
+{"place_id":16037318,
+ "osm_type":"N",
+ "osm_id":1653239257,
+ "name":{"name": "St. James's"},
+ "class":"place",
+ "type":"neighbourhood",
+ "admin_level":15,
+ "fromarea":true,
+ "isaddress":true,
+ "rank_address":22,
+ "distance":0.00982435489434447,
+ "localname":"St. James's"},
+{"place_id":51691981,
+ "osm_type":"N",
+ "osm_id":3937587633,
+ "name":{"name": "St Clement Danes"},
+ "class":"place",
+ "type":"neighbourhood",
+ "admin_level":15,
+ "fromarea":true,
+ "isaddress":false,
+ "rank_address":22,
+ "distance":0.0128768181947227,
+ "localname":"St Clement Danes"},
+{"place_id":22208313,
+ "osm_type":"N",
+ "osm_id":2290086954,
+ "name":{"name": "Covent Garden"},
+ "class":"place",
+ "type":"suburb",
+ "admin_level":15,
+ "fromarea":true,
+ "isaddress":true,
+ "rank_address":20,
+ "distance":0.00935748249317067,
+ "localname":"Covent Garden"},
+{"place_id":21742712,
+ "osm_type":"N",
+ "osm_id":2288030397,
+ "name":{"name": "Millbank"},
+ "class":"place",
+ "type":"suburb",
+ "admin_level":15,
+ "fromarea":true,
+ "isaddress":false,
+ "rank_address":20,
+ "distance":0.0106525181285902,
+ "localname":"Millbank"},
+{"place_id":122775,
+ "osm_type":"N",
+ "osm_id":26745371,
+ "name":{"name": "St Giles"},
+ "class":"place",
+ "type":"suburb",
+ "admin_level":15,
+ "fromarea":true,
+ "isaddress":false,
+ "rank_address":20,
+ "distance":0.0136188357358441,
+ "localname":"St Giles"},
+{"place_id":134882,
+ "osm_type":"N",
+ "osm_id":27553719,
+ "name":{"name": "Lambeth"},
+ "class":"place",
+ "type":"suburb",
+ "admin_level":15,
+ "fromarea":true,
+ "isaddress":false,
+ "rank_address":20,
+ "distance":0.0093308163978298,
+ "localname":"Lambeth"},
+{"place_id":194276676,
+ "osm_type":"R",
+ "osm_id":51781,
+ "name":{"name": "City of Westminster", "name:be": "Вэстмінстэр", "name:cy": "San Steffan", "name:en": "Westminster", "name:he": "וסטמינסטר", "name:ru": "Вестминстер"},
+ "class":"place",
+ "type":"city",
+ "admin_level":8,
+ "fromarea":true,
+ "isaddress":true,
+ "rank_address":16,
+ "distance":0.0340909562148044,
+ "localname":"Westminster"},
+{"place_id":195398522,
+ "osm_type":"N",
+ "osm_id":107775,
+ "name":{"name": "London", "name:ab": "Лондан", "name:af": "Londen", "name:am": "ለንደን", "name:an": "Londres", "name:ar": "لندن", "name:ba": "Лондон", "name:be": "Лондан", "name:bg": "Лондон", "name:bn": "লন্ডন", "name:bo": "ལོན་ཊོན།", "name:br": "Londrez", "name:ca": "Londres", "name:co": "Londra", "name:cs": "Londýn", "name:cu": "Лондонъ", "name:cv": "Лондон", "name:cy": "Llundain", "name:de": "London", "name:el": "Λονδίνο", "name:en": "London", "name:eo": "Londono", "name:es": "Londres", "name:eu": "Londres", "name:fa": "لندن", "name:fi": "Lontoo", "name:fr": "Londres", "name:fy": "Londen", "name:ga": "Londain", "name:gd": "Lunnainn", "name:gl": "Londres - London", "name:gn": "Londye", "name:gu": "લંડન", "name:gv": "Lunnin", "name:he": "לונדון", "name:hi": "लंदन", "name:ht": "Lonn", "name:hu": "London", "name:hy": "Լոնդոն", "name:is": "Lundúnir", "name:it": "Londra", "name:ja": "ロンドン", "name:ka": "ლონდონი", "name:kk": "Лондон", "name:kn": "ಲಂಡನ್", "name:ko": "런던", "name:ku": "London", "name:kv": "Лондон", "name:kw": "Loundres", "name:ky": "Лондон", "name:la": "Londinium", "name:li": "Londe", "name:ln": "Londoni", "name:lo": "ລອນດອນ", "name:lt": "Londonas", "name:lv": "Londona", "name:mi": "Rānana", "name:mk": "Лондон", "name:ml": "ലണ്ടൻ", "name:mn": "Лондон", "name:mr": "लंडन", "name:mt": "Londra", "name:my": "လန်ဒန်မြို့", "name:ne": "लण्डन", "name:nl": "Londen", "name:no": "London", "name:oc": "Londres", "name:or": "ଲଣ୍ଡନ", "name:os": "Лондон", "name:pl": "Londyn", "name:ps": "لندن", "name:pt": "Londres", "name:rm": "Londra", "name:ro": "Londra", "name:ru": "Лондон", "name:sa": "लन्डन्", "name:sc": "Londra", "name:si": "ලන්ඩන්", "name:sk": "Londýn", "name:sq": "Londra", "name:sr": "Лондон", "name:sv": "London", "name:ta": "இலண்டன்", "name:te": "లండన్", "name:tg": "Лондон", "name:th": "ลอนดอน", "name:tl": "Londres", "name:tr": "Londra", "name:tt": "Лондон", "name:uk": "Лондон", "name:ur": "لندن", "name:vi": "Luân Đôn", "name:wo": "Londar", "name:yi": "לאנדאן", "name:yo": "Lọndọnu", "name:zh": "倫敦", "name:zu": "ILondon", "name:ang": "Lunden", "name:arc": "ܠܘܢܕܘܢ", "name:arz": "لندن", "name:ast": "Londres", "name:bcl": "Londres", "name:cdo": "Lùng-dŭng", "name:ckb": "لەندەن", "name:diq": "Londra", "name:eml": "Lòndra", "name:ext": "Londri", "name:frp": "Londres", "name:gan": "倫敦", "name:haw": "Lākana", "name:ilo": "Londres", "name:jbo": "london", "name:koi": "Лондон", "name:krc": "Лондон", "name:lad": "Londra", "name:lbe": "Лондон", "name:lez": "Лондон", "name:lij": "Londra", "name:lmo": "Lundra", "name:mhr": "Лондон", "name:mrj": "Лондон", "name:mwl": "Londres", "name:mzn": "لندن", "name:nah": "Londres", "name:nap": "Londra", "name:new": "लण्डन", "name:nrm": "Londres", "name:pcd": "Londe", "name:pms": "Londra", "name:pnb": "لندن", "name:pnt": "Λονδίνο", "name:rue": "Лондон", "name:sah": "Лондон", "name:scn": "Londra", "name:sco": "Lunnon", "name:szl": "Lůndůn", "name:tet": "Londres", "name:tpi": "Landen", "name:tzl": "Londra", "name:udm": "Лондон", "name:vec": "Łondra", "name:vls": "Londn", "name:wuu": "伦敦", "name:xmf": "ლონდონი", "name:yue": "倫敦", "name:zea": "Londen", "name:nds-nl": "Londen", "name:bat-smg": "Londons", "name:roa-rup": "Londra", "name:roa-tara": "Londre", "name:be-tarask": "Лёндан", "name:zh_pinyin": "Lúndūn", "name:zh-classical": "倫敦", "name:zh-simplified": "伦敦", "name:zh-traditional": "倫敦"},
+ "class":"place",
+ "type":"city",
+ "admin_level":2,
+ "fromarea":true,
+ "isaddress":false,
+ "rank_address":16,
+ "distance":0.00412384196971048,
+ "localname":"London"},
+{"place_id":193774423,
+ "osm_type":"R",
+ "osm_id":65606,
+ "name":{"name": "London", "name:be": "Лондан", "name:ca": "Londres", "name:el": "Λονδίνο", "name:en": "London", "name:eo": "Londono", "name:es": "Londres", "name:fa": "لندن", "name:fi": "Lontoo", "name:fr": "Londres", "name:fy": "Londen", "name:gl": "Londres", "name:hi": "लंदन", "name:lt": "Londonas", "name:nl": "Londen", "name:pl": "Londyn", "name:pt": "Londres", "name:ru": "Лондон", "name:uk": "Лондон", "name:vi": "Luân Đôn", "name:zh": "伦敦", "int_name": "London", "name:szl": "Lůndůn", "name:tzl": "Londra", "name:be-tarask": "Лёндан"},
+ "class":"place",
+ "type":"city",
+ "admin_level":6,
+ "fromarea":true,
+ "isaddress":true,
+ "rank_address":12,
+ "distance":0.0172243361058611,
+ "localname":"London"},
+{"place_id":194000080,
+ "osm_type":"R",
+ "osm_id":175342,
+ "name":{"name": "Greater London", "name:be": "Вялікі Лондан", "name:de": "Groß-London", "name:en": "Greater London", "name:fr": "Grand Londres", "name:lt": "Didysis Londonas", "name:ru": "Большой Лондон", "name:uk": "Великий Лондон", "official_name": "Greater London (incl. City of London)", "name:be-tarask": "Вялікі Лёндан"},
+ "class":"boundary",
+ "type":"administrative",
+ "admin_level":5,
+ "fromarea":true,
+ "isaddress":true,
+ "rank_address":10,
+ "distance":0.0172532381571105,
+ "localname":"Greater London"},
+{"place_id":194325361,
+ "osm_type":"R",
+ "osm_id":58447,
+ "name":{"name": "England", "name:be": "Англія", "name:br": "Bro-Saoz", "name:ca": "Anglaterra", "name:cs": "Anglie", "name:cy": "Lloegr", "name:de": "England", "name:el": "Αγγλία", "name:en": "England", "name:eo": "Anglujo", "name:es": "Inglaterra", "name:fi": "Englanti", "name:fr": "Angleterre", "name:fy": "Ingelân", "name:ga": "Sasana", "name:gd": "Sasainn", "name:gv": "Sostyn", "name:he": "אנגליה", "name:hu": "Anglia", "name:ia": "Anglaterra", "name:io": "Anglia", "name:it": "Inghilterra", "name:la": "Anglia", "name:lt": "Anglija", "name:nl": "Engeland", "name:pl": "Anglia", "name:pt": "Inglaterra", "name:ru": "Англия", "name:sk": "Anglicko", "name:sv": "England", "name:tr": "İngiltere", "name:uk": "Англія", "name:vi": "Anh", "name:vo": "Linglän", "name:zh": "英格蘭", "name:hsb": "Jendźelska", "name:nds": "England", "name:tok": "ma Inli", "name:tzl": "Anglatzara", "alt_name:eo": "Anglio", "alt_name:ia": "Anglia", "old_name:vi": "Anh Quốc", "alt_name:nds": "Ingland", "name:be-tarask": "Ангельшчына", "name:zh-classical": "英格蘭", "name:zh-simplified": "英格兰", "name:zh-traditional": "英格蘭"},
+ "class":"boundary",
+ "type":"administrative",
+ "admin_level":4,
+ "fromarea":true,
+ "isaddress":true,
+ "rank_address":8,
+ "distance":1.75192967136328,
+ "localname":"England"},
+{"place_id":null,
+ "osm_type":null,
+ "osm_id":null,
+ "name":{"ref": "SW1A 2AA"},
+ "class":"place",
+ "type":"postcode",
+ "admin_level":null,
+ "fromarea":true,
+ "isaddress":true,
+ "rank_address":5,
+ "distance":0,
+ "localname":"SW1A 2AA"},
+{"place_id":40715006,
+ "osm_type":"N",
+ "osm_id":3055075992,
+ "name":{"ref": "SW1A 2AQ"},
+ "class":"place",
+ "type":"postcode",
+ "admin_level":15,
+ "fromarea":true,
+ "isaddress":false,
+ "rank_address":5,
+ "distance":0.00172905579146705,
+ "localname":"SW1A 2AQ"},
+{"place_id":194354400,
+ "osm_type":"R",
+ "osm_id":62149,
+ "name":{"name": "United Kingdom", "name:ab": "Британиа Ду", "name:af": "Verenigde Koninkryk", "name:ak": "United Kingdom", "name:am": "ዩናይትድ ኪንግደም", "name:an": "Reino Unito", "name:ar": "المملكة المتحدة", "name:az": "Böyük Britaniya", "name:ba": "Бөйөк Британия", "name:be": "Вялікабрытанія", "name:bg": "Обединено кралство Великобритания и Северна Ирландия", "name:bi": "Unaeted Kingdom", "name:bm": "Angilɛtɛri", "name:bn": "যুক্তরাজ্য", "name:bo": "དབྱིན་ཇི་མཉམ་འབྲེལ།", "name:br": "Rouantelezh-Unanet", "name:bs": "Ujedinjeno Kraljevstvo Velike Britanije i Sjeverne Irske", "name:ca": "Regne Unit", "name:ce": "Йоккха Британи", "name:co": "Regnu Unitu", "name:cs": "Spojené království", "name:cu": "Вєлика Британїꙗ", "name:cv": "Аслă Британи", "name:cy": "Deyrnas Unedig", "name:da": "Storbritannien", "name:de": "Vereinigtes Königreich", "name:dv": "ޔުނައިޓެޑް ކިންގްޑަމް", "name:dz": "ཡུ་ནའི་ཊེཊ་ཀིང་ཌམ", "name:ee": "United Kingdom", "name:el": "Ηνωμένο Βασίλειο", "name:en": "United Kingdom", "name:eo": "Britujo", "name:es": "Reino Unido", "name:et": "Suurbritannia", "name:eu": "Erresuma Batua", "name:fa": "بریتانیا", "name:ff": "Laamateeri Rentundi", "name:fi": "Yhdistynyt kuningaskunta", "name:fo": "Stóra Bretland", "name:fr": "Royaume-Uni", "name:fy": "Feriene Keninkryk", "name:ga": "An Ríocht Aontaithe", "name:gd": "An Rìoghachd Aonaichte", "name:gl": "Reino Unido", "name:gn": "Tavetã Joaju", "name:gu": "યુનાઇટેડ કિંગડમ", "name:gv": "Reeriaght Unnaneysit", "name:ha": "Birtaniya", "name:he": "הממלכה המאוחדת", "name:hi": "यूनाइटेड किंगडम", "name:hr": "Ujedinjeno Kraljevstvo", "name:ht": "Wayòm Ini", "name:hu": "Egyesült Királyság", "name:hy": "Միացյալ Թագավորություն", "name:ia": "Regno Unite", "name:id": "Britania Raya", "name:ie": "Reyatu Unit", "name:ig": "Obodoézè Nà Ofú", "name:ii": "ꑱꇩ", "name:io": "Unionita Rejio", "name:is": "Bretland", "name:it": "Regno Unito", "name:ja": "イギリス", "name:jv": "Britania Raya", "name:ka": "გაერთიანებული სამეფო", "name:kg": "Royaume-Uni", "name:ki": "Ngeretha", "name:kk": "Ұлыбритания", "name:kl": "Tuluit Nunaat", "name:km": "រាជាណាចក្ររួម", "name:kn": "ಯುನೈಟೆಡ್ ಕಿಂಗ್ಡಂ", "name:ko": "영국", "name:ks": "یُنایٹِڑ کِنٛگڈَم", "name:ku": "Keyaniya Yekbûyî", "name:kv": "Ыджыд Британия", "name:kw": "Ruwvaneth Unys", "name:ky": "Улуу Британия жана Түндүк Ирландия", "name:la": "Britanniarum Regnum", "name:lb": "Groussbritannien an Nordirland", "name:lg": "Bungereza", "name:li": "Vereineg Keuninkriek", "name:ln": "Ingɛlɛ́tɛlɛ", "name:lo": "ສະຫະລາດຊະອານາຈັກ", "name:lt": "Jungtinė Karalystė", "name:lv": "Apvienotā Karaliste", "name:mg": "Fanjakana Mitambatra", "name:mi": "Kīngitanga Kotahi", "name:mk": "Обединето Кралство", "name:ml": "യുണൈറ്റഡ് കിങ്ഡം", "name:mn": "Их Британи", "name:mr": "युनायटेड किंग्डम", "name:ms": "United Kingdom", "name:mt": "Renju Unit", "name:my": "ယူနိုက်တက်ကင်းဒမ်းနိုင်ငံ", "name:na": "Ingerand", "name:ne": "संयुक्त अधिराज्य", "name:nl": "Verenigd Koninkrijk", "name:nn": "Storbritannia", "name:no": "Storbritannia", "name:nv": "Tótaʼ Dinéʼiʼ Bikéyah", "name:oc": "Reialme Unit", "name:or": "ଯୁକ୍ତରାଜ୍ୟ", "name:os": "Стыр Британи", "name:pa": "ਸੰਯੁਕਤ ਬਾਦਸ਼ਾਹੀ", "name:pl": "Wielka Brytania", "name:ps": "بريتانيا", "name:pt": "Reino Unido", "name:qu": "Hukllachasqa Qhapaq Suyu", "name:rm": "Reginavel Unì", "name:rn": "Ubwongereza", "name:ro": "Regatul Unit al Marii Britanii și al Irlandei de Nord", "name:ru": "Великобритания", "name:rw": "Ubwongereza", "name:sa": "संयुक्त अधिराज्य", "name:sc": "Rennu Auniadu", "name:se": "Ovttastuvvan gonagasriika", "name:sg": "Ködörögbïä--Ôko", "name:sh": "Ujedinjeno Kraljevstvo", "name:si": "එක්සත් රාජධානිය", "name:sk": "Spojené kráľovstvo", "name:sl": "Združeno kraljestvo Velike Britanije in Severne Irske", "name:sn": "United Kingdom", "name:so": "Midowga boqortooyada Britan", "name:sq": "Mbretëria e Bashkuar e Britanisë dhe Irlandës së Veriut", "name:sr": "Уједињено Краљевство", "name:ss": "United Kingdom", "name:su": "Britania", "name:sv": "Storbritannien", "name:sw": "Ufalme wa Muungano", "name:ta": "ஐக்கிய இராச்சியம்", "name:te": "యునైటెడ్ కింగ్డమ్", "name:tg": "Подшоҳии Муттаҳида", "name:th": "สหราชอาณาจักร", "name:ti": "እንግሊዝ", "name:tl": "Nagkakaisang Kaharian", "name:to": "Pilitānia", "name:tr": "Birleşik Krallık", "name:tt": "Бөекбритания", "name:tw": "United Kingdom", "name:ty": "Paratāne", "name:ug": "بۈيۈك بېرىتانىيە", "name:uk": "Велика Британія", "name:ur": "برطانیہ", "name:uz": "Birlashgan Qirollik", "name:vi": "Vương quốc Anh", "name:vo": "Regän Pebalöl", "name:wo": "Nguur-Yu-Bennoo", "name:yi": "פאראייניגטע קעניגרייך", "name:yo": "Ilẹ̀ọba Aṣọ̀kan", "name:za": "Yinghgoz", "name:zh": "英國", "name:zu": "Umbuso Ohlangeneyo", "alt_name": "United Kingdom; UK; Britain; Great Britain", "int_name": "United Kingdom", "name:als": "Vereinigtes Königreich", "name:ang": "Geāned Cynerīce", "name:arc": "ܡܠܟܘܬܐ ܡܚܝܕܬܐ", "name:arz": "المملكه المتحده", "name:ast": "Reinu Xuníu", "name:bar": "Vaeinigts Kinireich", "name:bcl": "Reyno Unido", "name:bjn": "Britania Raya", "name:bpy": "তিলপারাজ্য", "name:bug": "United Kingdom", "name:bxr": "Нэгдсэн Вант Улс", "name:cdo": "Ĭng-guók", "name:ceb": "Hiniusang Gingharian", "name:chr": "ᎡᎵᏏᎯ", "name:chy": "United Kingdom", "name:ckb": "شانشینی یەکگرتوو", "name:crh": "Büyük Britaniya", "name:csb": "Wiôlgô Britanijô", "name:diq": "Qraliya Yewbiyayiye", "name:dsb": "Wjelika Britaniska", "name:eml": "Régn Unî", "name:ext": "Réinu Uniu", "name:frp": "Royômo-Uni", "name:frr": "Feriind Kiningrik", "name:fur": "Ream Unît", "name:gag": "Büük Britaniya", "name:gan": "英國", "name:hak": "Yîn-koet", "name:haw": "Aupuni Mōʻī Hui Pū ʻia", "name:hif": "United Kingdom", "name:hsb": "Zjednoćene kralestwo", "name:ilo": "Nagkaykaysa a Pagarian", "name:jbo": "ritygu'e", "name:kab": "Legliz", "name:kbd": "Британиэшхуэ", "name:koi": "Ыджыт Бритму", "name:krc": "Уллу Британия", "name:ksh": "Jrußbritannie", "name:lad": "Reyno Unido", "name:lez": "ЧIехибритания", "name:lfn": "Rena Unida", "name:lij": "Regno Unïo", "name:lmo": "Regn Ünì", "name:ltg": "Lelbrytaneja", "name:mhr": "Ушымо Королевстве", "name:mrj": "Кого Британи", "name:mwl": "Reino Ounido", "name:mzn": "بریتانیا", "name:nah": "Tlacetilīlli Huēyitlahtohcāyōtl", "name:nap": "Gran Vretagna", "name:nds": "Vereenigt Königriek vun Grootbritannien un Noordirland", "name:nov": "Unionati Regia", "name:nrm": "Rouoyaume Unni", "name:pag": "Reino Unido", "name:pam": "Pisanmetung a Ka-arian", "name:pap": "Reino Uni", "name:pcd": "Roéyôme-Uni", "name:pih": "Yunitid Kingdum", "name:pms": "Regn Unì", "name:pnb": "برطانیہ", "name:pnt": "Ηνωμένο Βασίλειο", "name:rmy": "Phandlo Thagaripen la Bare Britaniyako thai le Nordutne Irlandesko", "name:rue": "Велика Брітанія", "name:sah": "Холбоhуктаах Хоруоллук", "name:scn": "Regnu Unitu", "name:sco": "Unitit Kinrick", "name:srn": "Ingriskondre", "name:stq": "Fereeniged Köönichriek fon Groot-Britannien un Noudirlound", "name:szl": "Wjelgo Brytańijo", "name:tet": "Reinu Naklibur", "name:tok": "ma Juke", "name:tpi": "Yunaitet Kingdom", "name:tzl": "Regipäts Viensiçat", "name:udm": "Великобритания", "name:vec": "Regno Unìo", "name:vep": "Sur' Britanii", "name:vls": "Verênigd Keunienkryk", "name:war": "Reino Unido", "name:wuu": "英国", "name:xal": "Ик Бритишин болн Ар Гәәлгүдин Ниицәтә Нутг", "name:xmf": "გოართოიანაფილი ომაფე", "name:yue": "英國", "name:zea": "Vereênigd Konienkriek", "name:zh_py": "Yingguo", "short_name": "UK", "alt_name:eo": "Britio", "alt_name:sr": "УК;У.К.", "alt_name:vi": "Vương quốc Liên hiệp Anh", "name:nds-nl": "Verienigd Keuninkriek", "name:zh_pyt": "Yīngguó", "name:bat-smg": "Jongtėnė Karalīstė", "name:cbk-zam": "Reinos Unidos de Gran Britania y Norte Irelandia", "name:fiu-vro": "Ütiskuningriik", "name:roa-rup": "Britania Mari", "name:roa-tara": "Regne Aunìte", "official_name": "United Kingdom of Great Britain and Northern Ireland", "short_name:el": "ΗΒ", "short_name:vo": "Britän", "name:be-tarask": "Вялікабрытанія", "name:zh-min-nan": "Liân-ha̍p Ông-kok", "official_name:be": "Злучанае Каралеўства Вялікабрытаніі і Паўночнай Ірландыі", "official_name:br": "Rouantelezh Unanet Breizh-Veur ha Norzhiwerzhon", "official_name:ca": "Regne Unit de Gran Bretanya i Irlanda del Nord", "official_name:cs": "Spojené království Velké Británie a Severního Irska", "official_name:de": "Vereinigtes Königreich Großbritannien und Nordirland", "official_name:el": "Ηνωμένο Βασίλειο της Μεγάλης Βρετανίας και της Βόρειας Ιρλανδίας", "official_name:en": "United Kingdom of Great Britain and Northern Ireland", "official_name:eo": "Unuiĝinta Reĝlando de Granda Britujo kaj Nord-Irlando", "official_name:es": "Reino Unido de Gran Bretaña", "official_name:et": "Suurbritannia ja Põhja-Iiri Ühendkuningriik", "official_name:fr": "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord", "official_name:hr": "Ujedinjeno Kraljevstvo Velike Britanije i Sjeverne Irske", "official_name:id": "Perserikatan Kerajaan Britania Raya dan Irlandia Utara", "official_name:it": "Regno Unito di Gran Bretagna e Irlanda del Nord", "official_name:ja": "グレートブリテン及び北アイルランド連合王国", "official_name:ku": "Keyaniya Yekbûyî ya Brîtaniya Mezin û Bakurê Îrlandê", "official_name:lb": "Vereenegt Kinnekräich vu Groussbritannien an Nordirland", "official_name:no": "Det forente kongeriket Storbritannia og Nord-Irland", "official_name:pl": "Zjednoczone Królestwo Wielkiej Brytanii i Irlandii Północnej", "official_name:pt": "Reino Unido da Grã-Bretanha e Irlanda do Norte", "official_name:ru": "Соединённое королевство Великобритании и Северной Ирландии", "official_name:sk": "Spojené kráľovstvo Veľkej Británie a Severného Írska", "official_name:sl": "Združeno kraljestvo Velike Britanije in Severne Irske", "official_name:sr": "Уједињено Краљевство Велике Британије и Северне Ирске", "official_name:sv": "Förenade konungariket Storbritannien och Nordirland", "official_name:vi": "Vương quốc Liên hiệp Anh và Bắc Ireland", "name:abbreviation": "UK", "name:zh-classical": "英國", "official_name:scn": "Regnu Unitu di Gran Britagna e Irlanna dû Nord", "name:zh-simplified": "英国", "name:zh-traditional": "英國"},
+ "class":"place",
+ "type":"country",
+ "admin_level":2,
+ "fromarea":true,
+ "isaddress":true,
+ "rank_address":4,
+ "distance":4.56060933645498,
+ "localname":"United Kingdom"},
+{"place_id":null,
+ "osm_type":null,
+ "osm_id":null,
+ "name":{"ref": "gb"},
+ "class":"place",
+ "type":"country_code",
+ "admin_level":null,
+ "fromarea":true,
+ "isaddress":false,
+ "rank_address":4,
+ "distance":0,
+ "localname":"gb"}
+]
\ No newline at end of file
index b9c0166c5ad213aaff4e0d0fe20529c8794f5964..ea3e14a35765fbd8ab4db7ba70c6370a8a3c1d7e 100755 (executable)
@@ -3,10 +3,16 @@
 
 require_once(dirname(dirname(__FILE__)).'/settings/settings.php');
 require_once(CONST_BasePath.'/lib/init-cmd.php');
+// ->indirect via init-cmd.php->/lib/cmd.php                for runWithEnv, getCmdOpt
+// ->indirect via init-cmd.php->/lib/init.php->db.php       for &getDB()
+
+require_once(CONST_BasePath.'/lib/setup/SetupClass.php');
+require_once(CONST_BasePath.'/lib/setup_functions.php');
 ini_set('memory_limit', '800M');
 
-# (long-opt, short-opt, min-occurs, max-occurs, num-arguments, num-arguments, type, help)
+use Nominatim\Setup\SetupFunctions as SetupFunctions;
 
+// (long-opt, short-opt, min-occurs, max-occurs, num-arguments, num-arguments, type, help)
 $aCMDOptions
 = array(
    'Create and setup nominatim search system',
@@ -43,711 +49,115 @@ $aCMDOptions
    array('create-country-names', '', 0, 1, 0, 0, 'bool', 'Create default list of searchable country names'),
    array('drop', '', 0, 1, 0, 0, 'bool', 'Drop tables needed for updates, making the database readonly (EXPERIMENTAL)'),
   );
+
+// $aCMDOptions passed to getCmdOpt by reference
 getCmdOpt($_SERVER['argv'], $aCMDOptions, $aCMDResult, true, true);
 
 $bDidSomething = false;
 
-// Check if osm-file is set and points to a valid file if --all or --import-data is given
+//*******************************************************
+// Making some sanity check:
+// Check if osm-file is set and points to a valid file
 if ($aCMDResult['import-data'] || $aCMDResult['all']) {
-    if (!isset($aCMDResult['osm-file'])) {
-        fail('missing --osm-file for data import');
-    }
-
-    if (!file_exists($aCMDResult['osm-file'])) {
-        fail('the path supplied to --osm-file does not exist');
-    }
-
-    if (!is_readable($aCMDResult['osm-file'])) {
-        fail('osm-file "'.$aCMDResult['osm-file'].'" not readable');
-    }
+    // to remain in /lib/setup_functions.php function
+    checkInFile($aCMDResult['osm-file']);
 }
 
-// by default, use all but one processor, but never more than 15.
-$iInstances = isset($aCMDResult['threads'])
-              ? $aCMDResult['threads']
-              : (min(16, getProcessorCount()) - 1);
-
-if ($iInstances < 1) {
-    $iInstances = 1;
-    warn("resetting threads to $iInstances");
-}
-
-// Assume we can steal all the cache memory in the box (unless told otherwise)
-if (isset($aCMDResult['osm2pgsql-cache'])) {
-    $iCacheMemory = $aCMDResult['osm2pgsql-cache'];
-} else {
-    $iCacheMemory = getCacheMemoryMB();
+// osmosis init is no longer supported
+if ($aCMDResult['osmosis-init']) {
+    $bDidSomething = true;
+    echo "Command 'osmosis-init' no longer available, please use utils/update.php --init-updates.\n";
 }
 
-$sModulePath = CONST_Database_Module_Path;
-info('module path: ' . $sModulePath);
-
-$aDSNInfo = DB::parseDSN(CONST_Database_DSN);
-if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
+// ******************************************************
+// instantiate Setup class
+$cSetup = new SetupFunctions($aCMDResult);
 
+// *******************************************************
+// go through complete process if 'all' is selected or start selected functions
 if ($aCMDResult['create-db'] || $aCMDResult['all']) {
-    info('Create DB');
     $bDidSomething = true;
-    $oDB = DB::connect(CONST_Database_DSN, false);
-    if (!PEAR::isError($oDB)) {
-        fail('database already exists ('.CONST_Database_DSN.')');
-    }
-
-    $sCreateDBCmd = 'createdb -E UTF-8 -p '.$aDSNInfo['port'].' '.$aDSNInfo['database'];
-    if (isset($aDSNInfo['username']) && $aDSNInfo['username']) {
-        $sCreateDBCmd .= ' -U ' . $aDSNInfo['username'];
-    }
-    if (isset($aDSNInfo['hostspec']) && $aDSNInfo['hostspec']) {
-        $sCreateDBCmd .= ' -h ' . $aDSNInfo['hostspec'];
-    }
-
-    $aProcEnv = null;
-    if (isset($aDSNInfo['password']) && $aDSNInfo['password']) {
-        $aProcEnv = array_merge(array('PGPASSWORD' => $aDSNInfo['password']), $_ENV);
-    }
-
-    $result = runWithEnv($sCreateDBCmd, $aProcEnv);
-    if ($result != 0) fail('Error executing external command: '.$sCreateDBCmd);
+    $cSetup->createDB();
 }
 
 if ($aCMDResult['setup-db'] || $aCMDResult['all']) {
-    info('Setup DB');
     $bDidSomething = true;
+    $cSetup->setupDB();
+}
 
-    $oDB =& getDB();
-
-    $fPostgresVersion = getPostgresVersion($oDB);
-    echo 'Postgres version found: '.$fPostgresVersion."\n";
-
-    if ($fPostgresVersion < 9.1) {
-        fail('Minimum supported version of Postgresql is 9.1.');
-    }
-
-    pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS hstore');
-    pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS postgis');
-
-    // For extratags and namedetails the hstore_to_json converter is
-    // needed which is only available from Postgresql 9.3+. For older
-    // versions add a dummy function that returns nothing.
-    $iNumFunc = chksql($oDB->getOne("select count(*) from pg_proc where proname = 'hstore_to_json'"));
-
-    if ($iNumFunc == 0) {
-        pgsqlRunScript("create function hstore_to_json(dummy hstore) returns text AS 'select null::text' language sql immutable");
-        warn('Postgresql is too old. extratags and namedetails API not available.');
-    }
-
-    $fPostgisVersion = getPostgisVersion($oDB);
-    echo 'Postgis version found: '.$fPostgisVersion."\n";
-
-    if ($fPostgisVersion < 2.1) {
-        // Functions were renamed in 2.1 and throw an annoying deprecation warning
-        pgsqlRunScript('ALTER FUNCTION st_line_interpolate_point(geometry, double precision) RENAME TO ST_LineInterpolatePoint');
-        pgsqlRunScript('ALTER FUNCTION ST_Line_Locate_Point(geometry, geometry) RENAME TO ST_LineLocatePoint');
-    }
-    if ($fPostgisVersion < 2.2) {
-        pgsqlRunScript('ALTER FUNCTION ST_Distance_Spheroid(geometry, geometry, spheroid) RENAME TO ST_DistanceSpheroid');
-    }
-
-    $i = chksql($oDB->getOne("select count(*) from pg_user where usename = '".CONST_Database_Web_User."'"));
-    if ($i == 0) {
-        echo "\nERROR: Web user '".CONST_Database_Web_User."' does not exist. Create it with:\n";
-        echo "\n          createuser ".CONST_Database_Web_User."\n\n";
-        exit(1);
-    }
-
-    if (!checkModulePresence()) {
-        fail('error loading nominatim.so module');
-    }
-
-    if (!file_exists(CONST_ExtraDataPath.'/country_osm_grid.sql.gz')) {
-        echo 'Error: you need to download the country_osm_grid first:';
-        echo "\n    wget -O ".CONST_ExtraDataPath."/country_osm_grid.sql.gz https://www.nominatim.org/data/country_grid.sql.gz\n";
-        exit(1);
-    }
-
-    pgsqlRunScriptFile(CONST_BasePath.'/data/country_name.sql');
-    pgsqlRunScriptFile(CONST_BasePath.'/data/country_naturalearthdata.sql');
-    pgsqlRunScriptFile(CONST_BasePath.'/data/country_osm_grid.sql.gz');
-    pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_table.sql');
-    if (file_exists(CONST_BasePath.'/data/gb_postcode_data.sql.gz')) {
-        pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_data.sql.gz');
-    } else {
-        warn('external UK postcode table not found.');
-    }
-    if (CONST_Use_Extra_US_Postcodes) {
-        pgsqlRunScriptFile(CONST_BasePath.'/data/us_postcode.sql');
-    }
-
-    if ($aCMDResult['no-partitions']) {
-        pgsqlRunScript('update country_name set partition = 0');
-    }
-
-    // the following will be needed by create_functions later but
-    // is only defined in the subsequently called create_tables.
-    // Create dummies here that will be overwritten by the proper
-    // versions in create-tables.
-    pgsqlRunScript('CREATE TABLE IF NOT EXISTS place_boundingbox ()');
-    pgsqlRunScript('CREATE TYPE wikipedia_article_match AS ()', false);
+// Try accessing the C module, so we know early if something is wrong
+if (!checkModulePresence()) {
+    fail('error loading nominatim.so module');
 }
 
 if ($aCMDResult['import-data'] || $aCMDResult['all']) {
-    info('Import data');
     $bDidSomething = true;
-
-    $osm2pgsql = CONST_Osm2pgsql_Binary;
-    if (!file_exists($osm2pgsql)) {
-        echo "Check CONST_Osm2pgsql_Binary in your local settings file.\n";
-        echo "Normally you should not need to set this manually.\n";
-        fail("osm2pgsql not found in '$osm2pgsql'");
-    }
-
-    if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
-        $osm2pgsql .= ' --flat-nodes '.CONST_Osm2pgsql_Flatnode_File;
-    }
-    if (CONST_Tablespace_Osm2pgsql_Data)
-        $osm2pgsql .= ' --tablespace-slim-data '.CONST_Tablespace_Osm2pgsql_Data;
-    if (CONST_Tablespace_Osm2pgsql_Index)
-        $osm2pgsql .= ' --tablespace-slim-index '.CONST_Tablespace_Osm2pgsql_Index;
-    if (CONST_Tablespace_Place_Data)
-        $osm2pgsql .= ' --tablespace-main-data '.CONST_Tablespace_Place_Data;
-    if (CONST_Tablespace_Place_Index)
-        $osm2pgsql .= ' --tablespace-main-index '.CONST_Tablespace_Place_Index;
-    $osm2pgsql .= ' -lsc -O gazetteer --hstore --number-processes 1';
-    $osm2pgsql .= ' -C '.$iCacheMemory;
-    $osm2pgsql .= ' -P '.$aDSNInfo['port'];
-    if (isset($aDSNInfo['username']) && $aDSNInfo['username']) {
-        $osm2pgsql .= ' -U ' . $aDSNInfo['username'];
-    }
-    if (isset($aDSNInfo['hostspec']) && $aDSNInfo['hostspec']) {
-        $osm2pgsql .= ' -H ' . $aDSNInfo['hostspec'];
-    }
-
-    $aProcEnv = null;
-    if (isset($aDSNInfo['password']) && $aDSNInfo['password']) {
-        $aProcEnv = array_merge(array('PGPASSWORD' => $aDSNInfo['password']), $_ENV);
-    }
-
-    $osm2pgsql .= ' -d '.$aDSNInfo['database'].' '.$aCMDResult['osm-file'];
-    runWithEnv($osm2pgsql, $aProcEnv);
-
-    $oDB =& getDB();
-    if (!$aCMDResult['ignore-errors'] && !chksql($oDB->getRow('select * from place limit 1'))) {
-        fail('No Data');
-    }
+    $cSetup->importData($aCMDResult['osm-file']);
 }
 
 if ($aCMDResult['create-functions'] || $aCMDResult['all']) {
-    info('Create Functions');
     $bDidSomething = true;
-
-    if (!checkModulePresence()) {
-        fail('error loading nominatim.so module');
-    }
-
-    create_sql_functions($aCMDResult);
+    $cSetup->createFunctions();
 }
 
 if ($aCMDResult['create-tables'] || $aCMDResult['all']) {
-    info('Create Tables');
     $bDidSomething = true;
-
-    $sTemplate = file_get_contents(CONST_BasePath.'/sql/tables.sql');
-    $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
-    $sTemplate = replace_tablespace(
-        '{ts:address-data}',
-        CONST_Tablespace_Address_Data,
-        $sTemplate
-    );
-    $sTemplate = replace_tablespace(
-        '{ts:address-index}',
-        CONST_Tablespace_Address_Index,
-        $sTemplate
-    );
-    $sTemplate = replace_tablespace(
-        '{ts:search-data}',
-        CONST_Tablespace_Search_Data,
-        $sTemplate
-    );
-    $sTemplate = replace_tablespace(
-        '{ts:search-index}',
-        CONST_Tablespace_Search_Index,
-        $sTemplate
-    );
-    $sTemplate = replace_tablespace(
-        '{ts:aux-data}',
-        CONST_Tablespace_Aux_Data,
-        $sTemplate
-    );
-    $sTemplate = replace_tablespace(
-        '{ts:aux-index}',
-        CONST_Tablespace_Aux_Index,
-        $sTemplate
-    );
-    pgsqlRunScript($sTemplate, false);
-
-    // re-run the functions
-    info('Recreate Functions');
-    create_sql_functions($aCMDResult);
+    $cSetup->createTables();
+    $cSetup->createFunctions();
 }
 
 if ($aCMDResult['create-partition-tables'] || $aCMDResult['all']) {
-    info('Create Partition Tables');
     $bDidSomething = true;
-
-    $sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-tables.src.sql');
-    $sTemplate = replace_tablespace(
-        '{ts:address-data}',
-        CONST_Tablespace_Address_Data,
-        $sTemplate
-    );
-    $sTemplate = replace_tablespace(
-        '{ts:address-index}',
-        CONST_Tablespace_Address_Index,
-        $sTemplate
-    );
-    $sTemplate = replace_tablespace(
-        '{ts:search-data}',
-        CONST_Tablespace_Search_Data,
-        $sTemplate
-    );
-    $sTemplate = replace_tablespace(
-        '{ts:search-index}',
-        CONST_Tablespace_Search_Index,
-        $sTemplate
-    );
-    $sTemplate = replace_tablespace(
-        '{ts:aux-data}',
-        CONST_Tablespace_Aux_Data,
-        $sTemplate
-    );
-    $sTemplate = replace_tablespace(
-        '{ts:aux-index}',
-        CONST_Tablespace_Aux_Index,
-        $sTemplate
-    );
-
-    pgsqlRunPartitionScript($sTemplate);
+    $cSetup->createPartitionTables();
 }
 
-
 if ($aCMDResult['create-partition-functions'] || $aCMDResult['all']) {
-    info('Create Partition Functions');
     $bDidSomething = true;
-
-    $sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-functions.src.sql');
-
-    pgsqlRunPartitionScript($sTemplate);
+    $cSetup->createPartitionFunctions();
 }
 
 if ($aCMDResult['import-wikipedia-articles'] || $aCMDResult['all']) {
     $bDidSomething = true;
-    $sWikiArticlesFile = CONST_Wikipedia_Data_Path.'/wikipedia_article.sql.bin';
-    $sWikiRedirectsFile = CONST_Wikipedia_Data_Path.'/wikipedia_redirect.sql.bin';
-    if (file_exists($sWikiArticlesFile)) {
-        info('Importing wikipedia articles');
-        pgsqlRunDropAndRestore($sWikiArticlesFile);
-    } else {
-        warn('wikipedia article dump file not found - places will have default importance');
-    }
-    if (file_exists($sWikiRedirectsFile)) {
-        info('Importing wikipedia redirects');
-        pgsqlRunDropAndRestore($sWikiRedirectsFile);
-    } else {
-        warn('wikipedia redirect dump file not found - some place importance values may be missing');
-    }
+    $cSetup->importWikipediaArticles();
 }
 
-
 if ($aCMDResult['load-data'] || $aCMDResult['all']) {
-    info('Drop old Data');
     $bDidSomething = true;
-
-    $oDB =& getDB();
-    if (!pg_query($oDB->connection, 'TRUNCATE word')) fail(pg_last_error($oDB->connection));
-    echo '.';
-    if (!pg_query($oDB->connection, 'TRUNCATE placex')) fail(pg_last_error($oDB->connection));
-    echo '.';
-    if (!pg_query($oDB->connection, 'TRUNCATE location_property_osmline')) fail(pg_last_error($oDB->connection));
-    echo '.';
-    if (!pg_query($oDB->connection, 'TRUNCATE place_addressline')) fail(pg_last_error($oDB->connection));
-    echo '.';
-    if (!pg_query($oDB->connection, 'TRUNCATE place_boundingbox')) fail(pg_last_error($oDB->connection));
-    echo '.';
-    if (!pg_query($oDB->connection, 'TRUNCATE location_area')) fail(pg_last_error($oDB->connection));
-    echo '.';
-    if (!pg_query($oDB->connection, 'TRUNCATE search_name')) fail(pg_last_error($oDB->connection));
-    echo '.';
-    if (!pg_query($oDB->connection, 'TRUNCATE search_name_blank')) fail(pg_last_error($oDB->connection));
-    echo '.';
-    if (!pg_query($oDB->connection, 'DROP SEQUENCE seq_place')) fail(pg_last_error($oDB->connection));
-    echo '.';
-    if (!pg_query($oDB->connection, 'CREATE SEQUENCE seq_place start 100000')) fail(pg_last_error($oDB->connection));
-    echo '.';
-
-    $sSQL = 'select distinct partition from country_name';
-    $aPartitions = chksql($oDB->getCol($sSQL));
-    if (!$aCMDResult['no-partitions']) $aPartitions[] = 0;
-    foreach ($aPartitions as $sPartition) {
-        if (!pg_query($oDB->connection, 'TRUNCATE location_road_'.$sPartition)) fail(pg_last_error($oDB->connection));
-        echo '.';
-    }
-
-    // used by getorcreate_word_id to ignore frequent partial words
-    $sSQL = 'CREATE OR REPLACE FUNCTION get_maxwordfreq() RETURNS integer AS ';
-    $sSQL .= '$$ SELECT '.CONST_Max_Word_Frequency.' as maxwordfreq; $$ LANGUAGE SQL IMMUTABLE';
-    if (!pg_query($oDB->connection, $sSQL)) {
-        fail(pg_last_error($oDB->connection));
-    }
-    echo ".\n";
-
-    // pre-create the word list
-    if (!$aCMDResult['disable-token-precalc']) {
-        info('Loading word list');
-        pgsqlRunScriptFile(CONST_BasePath.'/data/words.sql');
-    }
-
-    info('Load Data');
-    $sColumns = 'osm_type, osm_id, class, type, name, admin_level, address, extratags, geometry';
-
-    $aDBInstances = array();
-    $iLoadThreads = max(1, $iInstances - 1);
-    for ($i = 0; $i < $iLoadThreads; $i++) {
-        $aDBInstances[$i] =& getDB(true);
-        $sSQL = "INSERT INTO placex ($sColumns) SELECT $sColumns FROM place WHERE osm_id % $iLoadThreads = $i";
-        $sSQL .= " and not (class='place' and type='houses' and osm_type='W'";
-        $sSQL .= "          and ST_GeometryType(geometry) = 'ST_LineString')";
-        $sSQL .= ' and ST_IsValid(geometry)';
-        if ($aCMDResult['verbose']) echo "$sSQL\n";
-        if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) {
-            fail(pg_last_error($aDBInstances[$i]->connection));
-        }
-    }
-    // last thread for interpolation lines
-    $aDBInstances[$iLoadThreads] =& getDB(true);
-    $sSQL = 'insert into location_property_osmline';
-    $sSQL .= ' (osm_id, address, linegeo)';
-    $sSQL .= ' SELECT osm_id, address, geometry from place where ';
-    $sSQL .= "class='place' and type='houses' and osm_type='W' and ST_GeometryType(geometry) = 'ST_LineString'";
-    if ($aCMDResult['verbose']) echo "$sSQL\n";
-    if (!pg_send_query($aDBInstances[$iLoadThreads]->connection, $sSQL)) {
-        fail(pg_last_error($aDBInstances[$iLoadThreads]->connection));
-    }
-
-    $bFailed = false;
-    for ($i = 0; $i <= $iLoadThreads; $i++) {
-        while (($hPGresult = pg_get_result($aDBInstances[$i]->connection)) !== false) {
-            $resultStatus = pg_result_status($hPGresult);
-            // PGSQL_EMPTY_QUERY, PGSQL_COMMAND_OK, PGSQL_TUPLES_OK,
-            // PGSQL_COPY_OUT, PGSQL_COPY_IN, PGSQL_BAD_RESPONSE,
-            // PGSQL_NONFATAL_ERROR and PGSQL_FATAL_ERROR
-            if ($resultStatus != PGSQL_COMMAND_OK && $resultStatus != PGSQL_TUPLES_OK) {
-                $resultError = pg_result_error($hPGresult);
-                echo '-- error text ' . $i . ': ' . $resultError . "\n";
-                $bFailed = true;
-            }
-        }
-    }
-    if ($bFailed) {
-        fail('SQL errors loading placex and/or location_property_osmline tables');
-    }
-    echo "\n";
-    info('Reanalysing database');
-    pgsqlRunScript('ANALYSE');
-
-    $sDatabaseDate = getDatabaseDate($oDB);
-    pg_query($oDB->connection, 'TRUNCATE import_status');
-    if ($sDatabaseDate === false) {
-        warn('could not determine database date.');
-    } else {
-        $sSQL = "INSERT INTO import_status (lastimportdate) VALUES('".$sDatabaseDate."')";
-        pg_query($oDB->connection, $sSQL);
-        echo "Latest data imported from $sDatabaseDate.\n";
-    }
+    $cSetup->loadData($aCMDResult['disable-token-precalc']);
 }
 
 if ($aCMDResult['import-tiger-data']) {
-    info('Import Tiger data');
     $bDidSomething = true;
-
-    $sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_start.sql');
-    $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
-    $sTemplate = replace_tablespace(
-        '{ts:aux-data}',
-        CONST_Tablespace_Aux_Data,
-        $sTemplate
-    );
-    $sTemplate = replace_tablespace(
-        '{ts:aux-index}',
-        CONST_Tablespace_Aux_Index,
-        $sTemplate
-    );
-    pgsqlRunScript($sTemplate, false);
-
-    $aDBInstances = array();
-    for ($i = 0; $i < $iInstances; $i++) {
-        $aDBInstances[$i] =& getDB(true);
-    }
-
-    foreach (glob(CONST_Tiger_Data_Path.'/*.sql') as $sFile) {
-        echo $sFile.': ';
-        $hFile = fopen($sFile, 'r');
-        $sSQL = fgets($hFile, 100000);
-        $iLines = 0;
-
-        while (true) {
-            for ($i = 0; $i < $iInstances; $i++) {
-                if (!pg_connection_busy($aDBInstances[$i]->connection)) {
-                    while (pg_get_result($aDBInstances[$i]->connection));
-                    $sSQL = fgets($hFile, 100000);
-                    if (!$sSQL) break 2;
-                    if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) fail(pg_last_error($oDB->connection));
-                    $iLines++;
-                    if ($iLines == 1000) {
-                        echo '.';
-                        $iLines = 0;
-                    }
-                }
-            }
-            usleep(10);
-        }
-
-        fclose($hFile);
-
-        $bAnyBusy = true;
-        while ($bAnyBusy) {
-            $bAnyBusy = false;
-            for ($i = 0; $i < $iInstances; $i++) {
-                if (pg_connection_busy($aDBInstances[$i]->connection)) $bAnyBusy = true;
-            }
-            usleep(10);
-        }
-        echo "\n";
-    }
-
-    info('Creating indexes on Tiger data');
-    $sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_finish.sql');
-    $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
-    $sTemplate = replace_tablespace(
-        '{ts:aux-data}',
-        CONST_Tablespace_Aux_Data,
-        $sTemplate
-    );
-    $sTemplate = replace_tablespace(
-        '{ts:aux-index}',
-        CONST_Tablespace_Aux_Index,
-        $sTemplate
-    );
-    pgsqlRunScript($sTemplate, false);
+    $cSetup->importTigerData();
 }
 
 if ($aCMDResult['calculate-postcodes'] || $aCMDResult['all']) {
-    info('Calculate Postcodes');
     $bDidSomething = true;
-    $oDB =& getDB();
-    if (!pg_query($oDB->connection, 'TRUNCATE location_postcode')) {
-        fail(pg_last_error($oDB->connection));
-    }
-
-    $sSQL  = 'INSERT INTO location_postcode';
-    $sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
-    $sSQL .= "SELECT nextval('seq_place'), 1, country_code,";
-    $sSQL .= "       upper(trim (both ' ' from address->'postcode')) as pc,";
-    $sSQL .= '       ST_Centroid(ST_Collect(ST_Centroid(geometry)))';
-    $sSQL .= '  FROM placex';
-    $sSQL .= " WHERE address ? 'postcode' AND address->'postcode' NOT SIMILAR TO '%(,|;)%'";
-    $sSQL .= '       AND geometry IS NOT null';
-    $sSQL .= ' GROUP BY country_code, pc';
-
-    if (!pg_query($oDB->connection, $sSQL)) {
-        fail(pg_last_error($oDB->connection));
-    }
-
-    if (CONST_Use_Extra_US_Postcodes) {
-        // only add postcodes that are not yet available in OSM
-        $sSQL  = 'INSERT INTO location_postcode';
-        $sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
-        $sSQL .= "SELECT nextval('seq_place'), 1, 'us', postcode,";
-        $sSQL .= '       ST_SetSRID(ST_Point(x,y),4326)';
-        $sSQL .= '  FROM us_postcode WHERE postcode NOT IN';
-        $sSQL .= '        (SELECT postcode FROM location_postcode';
-        $sSQL .= "          WHERE country_code = 'us')";
-        if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
-    }
-
-    // add missing postcodes for GB (if available)
-    $sSQL  = 'INSERT INTO location_postcode';
-    $sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
-    $sSQL .= "SELECT nextval('seq_place'), 1, 'gb', postcode, geometry";
-    $sSQL .= '  FROM gb_postcode WHERE postcode NOT IN';
-    $sSQL .= '           (SELECT postcode FROM location_postcode';
-    $sSQL .= "             WHERE country_code = 'gb')";
-    if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
-
-    if (!$aCMDResult['all']) {
-        $sSQL = "DELETE FROM word WHERE class='place' and type='postcode'";
-        $sSQL .= 'and word NOT IN (SELECT postcode FROM location_postcode)';
-        if (!pg_query($oDB->connection, $sSQL)) {
-            fail(pg_last_error($oDB->connection));
-        }
-    }
-    $sSQL = 'SELECT count(getorcreate_postcode_id(v)) FROM ';
-    $sSQL .= '(SELECT distinct(postcode) as v FROM location_postcode) p';
-
-    if (!pg_query($oDB->connection, $sSQL)) {
-        fail(pg_last_error($oDB->connection));
-    }
-}
-
-if ($aCMDResult['osmosis-init']) {
-    $bDidSomething = true;
-    echo "Command 'osmosis-init' no longer available, please use utils/update.php --init-updates.\n";
+    $cSetup->calculatePostcodes($aCMDResult['all']);
 }
 
 if ($aCMDResult['index'] || $aCMDResult['all']) {
     $bDidSomething = true;
-    $sOutputFile = '';
-    $sBaseCmd = CONST_InstallPath.'/nominatim/nominatim -i -d '.$aDSNInfo['database'].' -P '.$aDSNInfo['port'].' -t '.$iInstances.$sOutputFile;
-    if (isset($aDSNInfo['hostspec']) && $aDSNInfo['hostspec']) {
-        $sBaseCmd .= ' -H ' . $aDSNInfo['hostspec'];
-    }
-    if (isset($aDSNInfo['username']) && $aDSNInfo['username']) {
-        $sBaseCmd .= ' -U ' . $aDSNInfo['username'];
-    }
-    $aProcEnv = null;
-    if (isset($aDSNInfo['password']) && $aDSNInfo['password']) {
-        $aProcEnv = array_merge(array('PGPASSWORD' => $aDSNInfo['password']), $_ENV);
-    }
-
-    info('Index ranks 0 - 4');
-    $iStatus = runWithEnv($sBaseCmd.' -R 4', $aProcEnv);
-    if ($iStatus != 0) {
-        fail('error status ' . $iStatus . ' running nominatim!');
-    }
-    if (!$aCMDResult['index-noanalyse']) pgsqlRunScript('ANALYSE');
-    info('Index ranks 5 - 25');
-    $iStatus = runWithEnv($sBaseCmd.' -r 5 -R 25', $aProcEnv);
-    if ($iStatus != 0) {
-        fail('error status ' . $iStatus . ' running nominatim!');
-    }
-    if (!$aCMDResult['index-noanalyse']) pgsqlRunScript('ANALYSE');
-    info('Index ranks 26 - 30');
-    $iStatus = runWithEnv($sBaseCmd.' -r 26', $aProcEnv);
-    if ($iStatus != 0) {
-        fail('error status ' . $iStatus . ' running nominatim!');
-    }
-
-    info('Index postcodes');
-    $oDB =& getDB();
-    $sSQL = 'UPDATE location_postcode SET indexed_status = 0';
-    if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection));
+    $cSetup->index($aCMDResult['index-noanalyse']);
 }
 
 if ($aCMDResult['create-search-indices'] || $aCMDResult['all']) {
-    info('Create Search indices');
     $bDidSomething = true;
-
-    $sTemplate = file_get_contents(CONST_BasePath.'/sql/indices.src.sql');
-    $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
-    $sTemplate = replace_tablespace(
-        '{ts:address-index}',
-        CONST_Tablespace_Address_Index,
-        $sTemplate
-    );
-    $sTemplate = replace_tablespace(
-        '{ts:search-index}',
-        CONST_Tablespace_Search_Index,
-        $sTemplate
-    );
-    $sTemplate = replace_tablespace(
-        '{ts:aux-index}',
-        CONST_Tablespace_Aux_Index,
-        $sTemplate
-    );
-
-    pgsqlRunScript($sTemplate);
+    $cSetup->createSearchIndices();
 }
 
 if ($aCMDResult['create-country-names'] || $aCMDResult['all']) {
-    info('Create search index for default country names');
     $bDidSomething = true;
-
-    pgsqlRunScript("select getorcreate_country(make_standard_name('uk'), 'gb')");
-    pgsqlRunScript("select getorcreate_country(make_standard_name('united states'), 'us')");
-    pgsqlRunScript('select count(*) from (select getorcreate_country(make_standard_name(country_code), country_code) from country_name where country_code is not null) as x');
-    pgsqlRunScript("select count(*) from (select getorcreate_country(make_standard_name(name->'name'), country_code) from country_name where name ? 'name') as x");
-
-    $sSQL = 'select count(*) from (select getorcreate_country(make_standard_name(v), country_code) from (select country_code, skeys(name) as k, svals(name) as v from country_name) x where k ';
-    if (CONST_Languages) {
-        $sSQL .= 'in ';
-        $sDelim = '(';
-        foreach (explode(',', CONST_Languages) as $sLang) {
-            $sSQL .= $sDelim."'name:$sLang'";
-            $sDelim = ',';
-        }
-        $sSQL .= ')';
-    } else {
-        // all include all simple name tags
-        $sSQL .= "like 'name:%'";
-    }
-    $sSQL .= ') v';
-    pgsqlRunScript($sSQL);
+    $cSetup->createCountryNames($aCMDResult);
 }
 
 if ($aCMDResult['drop']) {
-    info('Drop tables only required for updates');
-    // The implementation is potentially a bit dangerous because it uses
-    // a positive selection of tables to keep, and deletes everything else.
-    // Including any tables that the unsuspecting user might have manually
-    // created. USE AT YOUR OWN PERIL.
     $bDidSomething = true;
-
-    // tables we want to keep. everything else goes.
-    $aKeepTables = array(
-                    '*columns',
-                    'import_polygon_*',
-                    'import_status',
-                    'place_addressline',
-                    'location_postcode',
-                    'location_property*',
-                    'placex',
-                    'search_name',
-                    'seq_*',
-                    'word',
-                    'query_log',
-                    'new_query_log',
-                    'spatial_ref_sys',
-                    'country_name',
-                    'place_classtype_*'
-                   );
-
-    $oDB =& getDB();
-    $aDropTables = array();
-    $aHaveTables = chksql($oDB->getCol("SELECT tablename FROM pg_tables WHERE schemaname='public'"));
-
-    foreach ($aHaveTables as $sTable) {
-        $bFound = false;
-        foreach ($aKeepTables as $sKeep) {
-            if (fnmatch($sKeep, $sTable)) {
-                $bFound = true;
-                break;
-            }
-        }
-        if (!$bFound) array_push($aDropTables, $sTable);
-    }
-
-    foreach ($aDropTables as $sDrop) {
-        if ($aCMDResult['verbose']) echo "dropping table $sDrop\n";
-        @pg_query($oDB->connection, "DROP TABLE $sDrop CASCADE");
-        // ignore warnings/errors as they might be caused by a table having
-        // been deleted already by CASCADE
-    }
-
-    if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
-        if ($aCMDResult['verbose']) echo 'deleting '.CONST_Osm2pgsql_Flatnode_File."\n";
-        unlink(CONST_Osm2pgsql_Flatnode_File);
-    }
+    $cSetup->drop($aCMDResult);
 }
 
+// ******************************************************
+// If we did something, repeat the warnings
 if (!$bDidSomething) {
     showUsage($aCMDOptions, true);
 } else {
@@ -756,210 +166,3 @@ if (!$bDidSomething) {
     echo "\n";
     info('Setup finished.');
 }
-
-
-function pgsqlRunScriptFile($sFilename)
-{
-    global $aCMDResult;
-    if (!file_exists($sFilename)) fail('unable to find '.$sFilename);
-
-    // Convert database DSN to psql parameters
-    $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
-    if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
-    $sCMD = 'psql -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'];
-    if (!$aCMDResult['verbose']) {
-        $sCMD .= ' -q';
-    }
-    if (isset($aDSNInfo['hostspec']) && $aDSNInfo['hostspec']) {
-        $sCMD .= ' -h ' . $aDSNInfo['hostspec'];
-    }
-    if (isset($aDSNInfo['username']) && $aDSNInfo['username']) {
-        $sCMD .= ' -U ' . $aDSNInfo['username'];
-    }
-    $aProcEnv = null;
-    if (isset($aDSNInfo['password']) && $aDSNInfo['password']) {
-        $aProcEnv = array_merge(array('PGPASSWORD' => $aDSNInfo['password']), $_ENV);
-    }
-
-    $ahGzipPipes = null;
-    if (preg_match('/\\.gz$/', $sFilename)) {
-        $aDescriptors = array(
-                         0 => array('pipe', 'r'),
-                         1 => array('pipe', 'w'),
-                         2 => array('file', '/dev/null', 'a')
-                        );
-        $hGzipProcess = proc_open('zcat '.$sFilename, $aDescriptors, $ahGzipPipes);
-        if (!is_resource($hGzipProcess)) fail('unable to start zcat');
-        $aReadPipe = $ahGzipPipes[1];
-        fclose($ahGzipPipes[0]);
-    } else {
-        $sCMD .= ' -f '.$sFilename;
-        $aReadPipe = array('pipe', 'r');
-    }
-
-    $aDescriptors = array(
-                     0 => $aReadPipe,
-                     1 => array('pipe', 'w'),
-                     2 => array('file', '/dev/null', 'a')
-                    );
-    $ahPipes = null;
-    $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes, null, $aProcEnv);
-    if (!is_resource($hProcess)) fail('unable to start pgsql');
-
-    // TODO: error checking
-    while (!feof($ahPipes[1])) {
-        echo fread($ahPipes[1], 4096);
-    }
-    fclose($ahPipes[1]);
-
-    $iReturn = proc_close($hProcess);
-    if ($iReturn > 0) {
-        fail("pgsql returned with error code ($iReturn)");
-    }
-    if ($ahGzipPipes) {
-        fclose($ahGzipPipes[1]);
-        proc_close($hGzipProcess);
-    }
-}
-
-function pgsqlRunScript($sScript, $bfatal = true)
-{
-    global $aCMDResult;
-    runSQLScript(
-        $sScript,
-        $bfatal,
-        $aCMDResult['verbose'],
-        $aCMDResult['ignore-errors']
-    );
-}
-
-function pgsqlRunPartitionScript($sTemplate)
-{
-    global $aCMDResult;
-    $oDB =& getDB();
-
-    $sSQL = 'select distinct partition from country_name';
-    $aPartitions = chksql($oDB->getCol($sSQL));
-    if (!$aCMDResult['no-partitions']) $aPartitions[] = 0;
-
-    preg_match_all('#^-- start(.*?)^-- end#ms', $sTemplate, $aMatches, PREG_SET_ORDER);
-    foreach ($aMatches as $aMatch) {
-        $sResult = '';
-        foreach ($aPartitions as $sPartitionName) {
-            $sResult .= str_replace('-partition-', $sPartitionName, $aMatch[1]);
-        }
-        $sTemplate = str_replace($aMatch[0], $sResult, $sTemplate);
-    }
-
-    pgsqlRunScript($sTemplate);
-}
-
-function pgsqlRunRestoreData($sDumpFile)
-{
-    // Convert database DSN to psql parameters
-    $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
-    if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
-    $sCMD = 'pg_restore -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'].' -Fc -a '.$sDumpFile;
-
-    $aDescriptors = array(
-                     0 => array('pipe', 'r'),
-                     1 => array('pipe', 'w'),
-                     2 => array('file', '/dev/null', 'a')
-                    );
-    $ahPipes = null;
-    $hProcess = proc_open($sCMD, $aDescriptors, $ahPipes);
-    if (!is_resource($hProcess)) fail('unable to start pg_restore');
-
-    fclose($ahPipes[0]);
-
-    // TODO: error checking
-    while (!feof($ahPipes[1])) {
-        echo fread($ahPipes[1], 4096);
-    }
-    fclose($ahPipes[1]);
-
-    $iReturn = proc_close($hProcess);
-}
-
-function pgsqlRunDropAndRestore($sDumpFile)
-{
-    // Convert database DSN to psql parameters
-    $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
-    if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
-    $sCMD = 'pg_restore -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'].' -Fc --clean '.$sDumpFile;
-    if (isset($aDSNInfo['hostspec']) && $aDSNInfo['hostspec']) {
-        $sCMD .= ' -h ' . $aDSNInfo['hostspec'];
-    }
-    if (isset($aDSNInfo['username']) && $aDSNInfo['username']) {
-        $sCMD .= ' -U ' . $aDSNInfo['username'];
-    }
-    $aProcEnv = null;
-    if (isset($aDSNInfo['password']) && $aDSNInfo['password']) {
-        $aProcEnv = array_merge(array('PGPASSWORD' => $aDSNInfo['password']), $_ENV);
-    }
-
-    $iReturn = runWithEnv($sCMD, $aProcEnv);
-}
-
-function passthruCheckReturn($sCmd)
-{
-    $iResult = -1;
-    passthru($sCmd, $iResult);
-}
-
-function replace_tablespace($sTemplate, $sTablespace, $sSql)
-{
-    if ($sTablespace) {
-        $sSql = str_replace($sTemplate, 'TABLESPACE "'.$sTablespace.'"', $sSql);
-    } else {
-        $sSql = str_replace($sTemplate, '', $sSql);
-    }
-
-    return $sSql;
-}
-
-function create_sql_functions($aCMDResult)
-{
-    global $sModulePath;
-    $sTemplate = file_get_contents(CONST_BasePath.'/sql/functions.sql');
-    $sTemplate = str_replace('{modulepath}', $sModulePath, $sTemplate);
-    if ($aCMDResult['enable-diff-updates']) {
-        $sTemplate = str_replace('RETURN NEW; -- %DIFFUPDATES%', '--', $sTemplate);
-    }
-    if ($aCMDResult['enable-debug-statements']) {
-        $sTemplate = str_replace('--DEBUG:', '', $sTemplate);
-    }
-    if (CONST_Limit_Reindexing) {
-        $sTemplate = str_replace('--LIMIT INDEXING:', '', $sTemplate);
-    }
-    if (!CONST_Use_US_Tiger_Data) {
-        $sTemplate = str_replace('-- %NOTIGERDATA% ', '', $sTemplate);
-    }
-    if (!CONST_Use_Aux_Location_data) {
-        $sTemplate = str_replace('-- %NOAUXDATA% ', '', $sTemplate);
-    }
-    pgsqlRunScript($sTemplate);
-}
-
-function checkModulePresence()
-{
-    // Try accessing the C module, so we know early if something is wrong
-    // and can simply error out.
-    global $sModulePath;
-    $sSQL = "CREATE FUNCTION nominatim_test_import_func(text) RETURNS text AS '";
-    $sSQL .= $sModulePath."/nominatim.so', 'transliteration' LANGUAGE c IMMUTABLE STRICT";
-    $sSQL .= ';DROP FUNCTION nominatim_test_import_func(text);';
-
-    $oDB =& getDB();
-    $oResult = $oDB->query($sSQL);
-
-    $bResult = true;
-
-    if (PEAR::isError($oResult)) {
-        echo "\nERROR: Failed to load nominatim module. Reason:\n";
-        echo $oResult->userinfo."\n\n";
-        $bResult = false;
-    }
-
-    return $bResult;
-}
index ad57101009f9dd7e996a2eeb536817c754b738c9..f5c208577e775c48aa81c6788dd29186941e5ef9 100755 (executable)
@@ -3,10 +3,14 @@
 
 require_once(dirname(dirname(__FILE__)).'/settings/settings.php');
 require_once(CONST_BasePath.'/lib/init-cmd.php');
+require_once(CONST_BasePath.'/lib/setup_functions.php');
+require_once(CONST_BasePath.'/lib/setup/SetupClass.php');
+
 ini_set('memory_limit', '800M');
 
-# (long-opt, short-opt, min-occurs, max-occurs, num-arguments, num-arguments, type, help)
+use Nominatim\Setup\SetupFunctions as SetupFunctions;
 
+// (long-opt, short-opt, min-occurs, max-occurs, num-arguments, num-arguments, type, help)
 $aCMDOptions
 = array(
    'Import / update / index osm data',
@@ -40,6 +44,7 @@ $aCMDOptions
    array('recompute-word-counts', '', 0, 1, 0, 0, 'bool', 'Compute frequency of full-word search terms'),
    array('no-npi', '', 0, 1, 0, 0, 'bool', '(obsolete)'),
   );
+
 getCmdOpt($_SERVER['argv'], $aCMDOptions, $aResult, true, true);
 
 if (!isset($aResult['index-instances'])) $aResult['index-instances'] = 1;
@@ -99,13 +104,14 @@ if ($aResult['init-updates']) {
         echo "and have set up CONST_Pyosmium_Binary to point to pyosmium-get-changes.\n";
         fail('pyosmium-get-changes not found or not usable');
     }
+
     if (!$aResult['no-update-functions']) {
-        $sSetup ='@PHP_BIN@ '. CONST_InstallPath.'/utils/setup.php';
-        $iRet = -1;
-        passthru($sSetup.' --create-functions --enable-diff-updates', $iRet);
-        if ($iRet != 0) {
-            fail('Error running setup script');
-        }
+        // instantiate setupClass to use the function therein
+        $cSetup = new SetupFunctions(array(
+                                      'enable-diff-updates' => true,
+                                      'verbose' => $aResult['verbose']
+                                     ));
+        $cSetup->createFunctions();
     }
 
     $sDatabaseDate = getDatabaseDate($oDB);
index 062d7ceebe26a66e34e388e9ee6f44202cf965a8..4162f1513bb1c4904556111a4c686a243c0a2303 100755 (executable)
@@ -25,10 +25,10 @@ export DEBIAN_FRONTEND=noninteractive #DOCS:
 # Now you can install all packages needed for Nominatim:
 
     sudo apt-get install -y build-essential cmake g++ libboost-dev libboost-system-dev \
-                            libboost-filesystem-dev libexpat1-dev zlib1g-dev libxml2-dev\
+                            libboost-filesystem-dev libexpat1-dev zlib1g-dev libxml2-dev \
                             libbz2-dev libpq-dev libproj-dev \
                             postgresql-server-dev-10 postgresql-10-postgis-2.4 \
-                            postgresql-contrib-10 \
+                            postgresql-contrib-10 postgresql-10-postgis-scripts \
                             apache2 php php-pgsql libapache2-mod-php php-pear php-db \
                             php-intl git
 
@@ -77,7 +77,7 @@ export DEBIAN_FRONTEND=noninteractive #DOCS:
 # ---------------------
 #
 # Tune the postgresql configuration, which is located in 
-# `/etc/postgresql/9.5/main/postgresql.conf`. See section *Postgres Tuning* in
+# `/etc/postgresql/10/main/postgresql.conf`. See section *Postgres Tuning* in
 # [the installation page](../admin/Installation.md#postgresql-tuning)
 # for the parameters to change.
 #