X-Git-Url: https://git.openstreetmap.org./nominatim.git/blobdiff_plain/a855ffe58e258be6ac4a68a802d87b65fdfd6033..7e0fdf5928a0c726b22ad969267a3b9e0402c823:/utils/setup.php?ds=sidebyside diff --git a/utils/setup.php b/utils/setup.php index da16e3f0..501ad3f2 100755 --- a/utils/setup.php +++ b/utils/setup.php @@ -7,7 +7,7 @@ ini_set('memory_limit', '800M'); $aCMDOptions = array( - "Create and setup nominatim search system", + 'Create and setup nominatim search system', array('help', 'h', 0, 1, 0, 0, false, 'Show Help'), array('quiet', 'q', 0, 1, 0, 0, 'bool', 'Quiet output'), array('verbose', 'v', 0, 1, 0, 0, 'bool', 'Verbose output'), @@ -28,7 +28,7 @@ $aCMDOptions array('create-tables', '', 0, 1, 0, 0, 'bool', 'Create main tables'), array('create-partition-tables', '', 0, 1, 0, 0, 'bool', 'Create required partition tables'), array('create-partition-functions', '', 0, 1, 0, 0, 'bool', 'Create required partition triggers'), - array('no-partitions', '', 0, 1, 0, 0, 'bool', "Do not partition search indices (speeds up import of single country extracts)"), + array('no-partitions', '', 0, 1, 0, 0, 'bool', 'Do not partition search indices (speeds up import of single country extracts)'), array('import-wikipedia-articles', '', 0, 1, 0, 0, 'bool', 'Import wikipedia article dump'), array('load-data', '', 0, 1, 0, 0, 'bool', 'Copy data to live tables from import table'), array('disable-token-precalc', '', 0, 1, 0, 0, 'bool', 'Disable name precalculation (EXPERT)'), @@ -61,15 +61,18 @@ if ($aCMDResult['import-data'] || $aCMDResult['all']) { } -// This is a pretty hard core default - the number of processors in the box - 1 -$iInstances = isset($aCMDResult['threads'])?$aCMDResult['threads']:(getProcessorCount()-1); +// 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; - echo "WARNING: resetting threads to $iInstances\n"; + warn("resetting threads to $iInstances"); } if ($iInstances > getProcessorCount()) { $iInstances = getProcessorCount(); - echo "WARNING: resetting threads to $iInstances\n"; + warn("resetting threads to $iInstances"); } // Assume we can steal all the cache memory in the box (unless told otherwise) @@ -83,7 +86,7 @@ $aDSNInfo = DB::parseDSN(CONST_Database_DSN); if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432; if ($aCMDResult['create-db'] || $aCMDResult['all']) { - echo "Create DB\n"; + info('Create DB'); $bDidSomething = true; $oDB = DB::connect(CONST_Database_DSN, false); if (!PEAR::isError($oDB)) { @@ -93,18 +96,16 @@ if ($aCMDResult['create-db'] || $aCMDResult['all']) { } if ($aCMDResult['setup-db'] || $aCMDResult['all']) { - echo "Setup DB\n"; + info('Setup DB'); $bDidSomething = true; - // TODO: path detection, detection memory, etc. - // $oDB =& getDB(); $fPostgresVersion = getPostgresVersion($oDB); echo 'Postgres version found: '.$fPostgresVersion."\n"; if ($fPostgresVersion < 9.1) { - fail("Minimum supported version of Postgresql is 9.1."); + fail('Minimum supported version of Postgresql is 9.1.'); } pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS hstore'); @@ -117,7 +118,7 @@ if ($aCMDResult['setup-db'] || $aCMDResult['all']) { if ($iNumFunc == 0) { pgsqlRunScript("create function hstore_to_json(dummy hstore) returns text AS 'select null::text' language sql immutable"); - echo "WARNING: Postgresql is too old. extratags and namedetails API not available."; + warn('Postgresql is too old. extratags and namedetails API not available.'); } $fPostgisVersion = getPostgisVersion($oDB); @@ -126,20 +127,46 @@ if ($aCMDResult['setup-db'] || $aCMDResult['all']) { 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, double precision) RENAME TO ST_LineLocatePoint'); + pgsqlRunScript('ALTER FUNCTION ST_Line_Locate_Point(geometry, geometry) RENAME TO ST_LineLocatePoint'); } if ($fPostgisVersion < 2.2) { - pgsqlRunScript('ALTER FUNCTION ST_Distance_Spheroid(geometry, double precision) RENAME TO ST_DistanceSpheroid'); + 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); + } + + // Try accessing the C module, so we know early if something is wrong + // and can simply error out. + $sSQL = "CREATE FUNCTION nominatim_test_import_func(text) RETURNS text AS '"; + $sSQL .= CONST_InstallPath."/module/nominatim.so', 'transliteration' LANGUAGE c IMMUTABLE STRICT"; + $sSQL .= ';DROP FUNCTION nominatim_test_import_func(text);'; + $oResult = $oDB->query($sSQL); + + if (PEAR::isError($oResult)) { + echo "\nERROR: Failed to load nominatim module. Reason:\n"; + echo $oResult->userinfo."\n\n"; + exit(1); + } + + 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'); + 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 { - echo "WARNING: external UK postcode table not found.\n"; + warn('external UK postcode table not found.'); } if (CONST_Use_Extra_US_Postcodes) { pgsqlRunScriptFile(CONST_BasePath.'/data/us_postcode.sql'); @@ -158,16 +185,17 @@ if ($aCMDResult['setup-db'] || $aCMDResult['all']) { } if ($aCMDResult['import-data'] || $aCMDResult['all']) { - echo "Import\n"; + info('Import data'); $bDidSomething = true; $osm2pgsql = CONST_Osm2pgsql_Binary; if (!file_exists($osm2pgsql)) { - echo "Please download and build osm2pgsql.\nIf it is already installed, check the path in your local settings (settings/local.php) file.\n"; + 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)) { + if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) { $osm2pgsql .= ' --flat-nodes '.CONST_Osm2pgsql_Flatnode_File; } if (CONST_Tablespace_Osm2pgsql_Data) @@ -185,22 +213,24 @@ if ($aCMDResult['import-data'] || $aCMDResult['all']) { passthruCheckReturn($osm2pgsql); $oDB =& getDB(); - if (!chksql($oDB->getRow('select * from place limit 1'))) { + if (!$aCMDResult['ignore-errors'] && !chksql($oDB->getRow('select * from place limit 1'))) { fail('No Data'); } } if ($aCMDResult['create-functions'] || $aCMDResult['all']) { - echo "Functions\n"; + info('Create Functions'); $bDidSomething = true; - if (!file_exists(CONST_InstallPath.'/module/nominatim.so')) fail("nominatim module not built"); + if (!file_exists(CONST_InstallPath.'/module/nominatim.so')) { + fail('nominatim module not built'); + } create_sql_functions($aCMDResult); } if ($aCMDResult['create-tables'] || $aCMDResult['all']) { + info('Create Tables'); $bDidSomething = true; - echo "Tables\n"; $sTemplate = file_get_contents(CONST_BasePath.'/sql/tables.sql'); $sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate); $sTemplate = replace_tablespace( @@ -236,12 +266,12 @@ if ($aCMDResult['create-tables'] || $aCMDResult['all']) { pgsqlRunScript($sTemplate, false); // re-run the functions - echo "Functions\n"; + info('Recreate Functions'); create_sql_functions($aCMDResult); } if ($aCMDResult['create-partition-tables'] || $aCMDResult['all']) { - echo "Partition Tables\n"; + info('Create Partition Tables'); $bDidSomething = true; $sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-tables.src.sql'); @@ -281,7 +311,7 @@ if ($aCMDResult['create-partition-tables'] || $aCMDResult['all']) { if ($aCMDResult['create-partition-functions'] || $aCMDResult['all']) { - echo "Partition Functions\n"; + info('Create Partition Functions'); $bDidSomething = true; $sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-functions.src.sql'); @@ -291,27 +321,25 @@ if ($aCMDResult['create-partition-functions'] || $aCMDResult['all']) { if ($aCMDResult['import-wikipedia-articles'] || $aCMDResult['all']) { $bDidSomething = true; - $sWikiArticlesFile = CONST_BasePath.'/data/wikipedia_article.sql.bin'; - $sWikiRedirectsFile = CONST_BasePath.'/data/wikipedia_redirect.sql.bin'; + $sWikiArticlesFile = CONST_Wikipedia_Data_Path.'/wikipedia_article.sql.bin'; + $sWikiRedirectsFile = CONST_Wikipedia_Data_Path.'/wikipedia_redirect.sql.bin'; if (file_exists($sWikiArticlesFile)) { - echo "Importing wikipedia articles..."; + info('Importing wikipedia articles'); pgsqlRunDropAndRestore($sWikiArticlesFile); - echo "...done\n"; } else { - echo "WARNING: wikipedia article dump file not found - places will have default importance\n"; + warn('wikipedia article dump file not found - places will have default importance'); } if (file_exists($sWikiRedirectsFile)) { - echo "Importing wikipedia redirects..."; + info('Importing wikipedia redirects'); pgsqlRunDropAndRestore($sWikiRedirectsFile); - echo "...done\n"; } else { - echo "WARNING: wikipedia redirect dump file not found - some place importance values may be missing\n"; + warn('wikipedia redirect dump file not found - some place importance values may be missing'); } } if ($aCMDResult['load-data'] || $aCMDResult['all']) { - echo "Drop old Data\n"; + info('Drop old Data'); $bDidSomething = true; $oDB =& getDB(); @@ -345,34 +373,45 @@ if ($aCMDResult['load-data'] || $aCMDResult['all']) { } // used by getorcreate_word_id to ignore frequent partial words - if (!pg_query($oDB->connection, 'CREATE OR REPLACE FUNCTION get_maxwordfreq() RETURNS integer AS $$ SELECT '.CONST_Max_Word_Frequency.' as maxwordfreq; $$ LANGUAGE SQL IMMUTABLE')) fail(pg_last_error($oDB->connection)); + $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']) { - echo "Loading word list\n"; + info('Loading word list'); pgsqlRunScriptFile(CONST_BasePath.'/data/words.sql'); } - echo "Load Data\n"; + 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 (osm_type, osm_id, class, type, name, admin_level, '; - $sSQL .= 'housenumber, street, addr_place, isin, postcode, country_code, extratags, '; - $sSQL .= 'geometry) select * from place where osm_id % '.$iLoadThreads.' = '.$i; - $sSQL .= " and not (class='place' and type='houses' and osm_type='W' and ST_GeometryType(geometry) = 'ST_LineString')"; + $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($oDB->connection)); + 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 = 'select insert_osmline (osm_id, housenumber, street, addr_place, postcode, country_code, '; - $sSQL .= 'geometry) from place where '; + $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[$i]->connection, $sSQL)) fail(pg_last_error($oDB->connection)); + if (!pg_send_query($aDBInstances[$iLoadThreads]->connection, $sSQL)) { + fail(pg_last_error($aDBInstances[$iLoadThreads]->connection)); + } $bAnyBusy = true; while ($bAnyBusy) { @@ -384,11 +423,22 @@ if ($aCMDResult['load-data'] || $aCMDResult['all']) { echo '.'; } echo "\n"; - echo "Reanalysing database...\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"; + } } if ($aCMDResult['import-tiger-data']) { + info('Import Tiger data'); $bDidSomething = true; $sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_start.sql'); @@ -412,7 +462,7 @@ if ($aCMDResult['import-tiger-data']) { foreach (glob(CONST_Tiger_Data_Path.'/*.sql') as $sFile) { echo $sFile.': '; - $hFile = fopen($sFile, "r"); + $hFile = fopen($sFile, 'r'); $sSQL = fgets($hFile, 100000); $iLines = 0; @@ -425,7 +475,7 @@ if ($aCMDResult['import-tiger-data']) { if (!pg_send_query($aDBInstances[$i]->connection, $sSQL)) fail(pg_last_error($oDB->connection)); $iLines++; if ($iLines == 1000) { - echo "."; + echo '.'; $iLines = 0; } } @@ -446,7 +496,7 @@ if ($aCMDResult['import-tiger-data']) { echo "\n"; } - echo "Creating indexes\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( @@ -463,119 +513,93 @@ if ($aCMDResult['import-tiger-data']) { } if ($aCMDResult['calculate-postcodes'] || $aCMDResult['all']) { + info('Calculate Postcodes'); $bDidSomething = true; $oDB =& getDB(); - if (!pg_query($oDB->connection, 'DELETE from placex where osm_type=\'P\'')) fail(pg_last_error($oDB->connection)); - $sSQL = "insert into placex (osm_type,osm_id,class,type,postcode,calculated_country_code,geometry) "; - $sSQL .= "select 'P',nextval('seq_postcodes'),'place','postcode',postcode,calculated_country_code,"; - $sSQL .= "ST_SetSRID(ST_Point(x,y),4326) as geometry from (select calculated_country_code,postcode,"; - $sSQL .= "avg(st_x(st_centroid(geometry))) as x,avg(st_y(st_centroid(geometry))) as y "; - $sSQL .= "from placex where postcode is not null group by calculated_country_code,postcode) as x"; - if (!pg_query($oDB->connection, $sSQL)) fail(pg_last_error($oDB->connection)); + 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) { - $sSQL = "insert into placex (osm_type,osm_id,class,type,postcode,calculated_country_code,geometry) "; - $sSQL .= "select 'P',nextval('seq_postcodes'),'place','postcode',postcode,'us',"; - $sSQL .= "ST_SetSRID(ST_Point(x,y),4326) as geometry from us_postcode"; + // 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)); } -} -if ($aCMDResult['osmosis-init'] || ($aCMDResult['all'] && !$aCMDResult['drop'])) { // no use doing osmosis-init when dropping update tables - $bDidSomething = true; - $oDB =& getDB(); + // 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 (!file_exists(CONST_Osmosis_Binary)) { - echo "Please download osmosis.\nIf it is already installed, check the path in your local settings (settings/local.php) file.\n"; - if (!$aCMDResult['all']) { - fail("osmosis not found in '".CONST_Osmosis_Binary."'"); - } - } else { - if (file_exists(CONST_InstallPath.'/settings/configuration.txt')) { - echo "settings/configuration.txt already exists\n"; - } else { - passthru(CONST_Osmosis_Binary.' --read-replication-interval-init '.CONST_InstallPath.'/settings'); - // update osmosis configuration.txt with our settings - passthru("sed -i 's!baseUrl=.*!baseUrl=".CONST_Replication_Url."!' ".CONST_InstallPath.'/settings/configuration.txt'); - passthru("sed -i 's:maxInterval = .*:maxInterval = ".CONST_Replication_MaxInterval.":' ".CONST_InstallPath.'/settings/configuration.txt'); + 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'; - // Find the last node in the DB - $iLastOSMID = $oDB->getOne("select max(osm_id) from place where osm_type = 'N'"); - - // Lookup the timestamp that node was created (less 3 hours for margin for changsets to be closed) - $sLastNodeURL = 'http://www.openstreetmap.org/api/0.6/node/'.$iLastOSMID."/1"; - $sLastNodeXML = file_get_contents($sLastNodeURL); - preg_match('#timestamp="(([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})Z)"#', $sLastNodeXML, $aLastNodeDate); - $iLastNodeTimestamp = strtotime($aLastNodeDate[1]) - (3*60*60); - - // Search for the correct state file - uses file timestamps so need to sort by date descending - $sRepURL = CONST_Replication_Url."/"; - $sRep = file_get_contents($sRepURL."?C=M;O=D;F=1"); - // download.geofabrik.de: 000/