2 @define('CONST_LibDir', dirname(dirname(__FILE__)));
4 require_once(CONST_LibDir.'/init-cmd.php');
5 require_once(CONST_LibDir.'/setup_functions.php');
6 require_once(CONST_LibDir.'/setup/SetupClass.php');
8 ini_set('memory_limit', '800M');
10 use Nominatim\Setup\SetupFunctions as SetupFunctions;
12 // (long-opt, short-opt, min-occurs, max-occurs, num-arguments, num-arguments, type, help)
15 'Import / update / index osm data',
16 array('help', 'h', 0, 1, 0, 0, false, 'Show Help'),
17 array('quiet', 'q', 0, 1, 0, 0, 'bool', 'Quiet output'),
18 array('verbose', 'v', 0, 1, 0, 0, 'bool', 'Verbose output'),
20 array('init-updates', '', 0, 1, 0, 0, 'bool', 'Set up database for updating'),
21 array('check-for-updates', '', 0, 1, 0, 0, 'bool', 'Check if new updates are available'),
22 array('no-update-functions', '', 0, 1, 0, 0, 'bool', 'Do not update trigger functions to support differential updates (assuming the diff update logic is already present)'),
23 array('import-osmosis', '', 0, 1, 0, 0, 'bool', 'Import updates once'),
24 array('import-osmosis-all', '', 0, 1, 0, 0, 'bool', 'Import updates forever'),
25 array('no-index', '', 0, 1, 0, 0, 'bool', 'Do not index the new data'),
27 array('calculate-postcodes', '', 0, 1, 0, 0, 'bool', 'Update postcode centroid table'),
29 array('import-file', '', 0, 1, 1, 1, 'realpath', 'Re-import data from an OSM file'),
30 array('import-diff', '', 0, 1, 1, 1, 'realpath', 'Import a diff (osc) file from local file system'),
31 array('osm2pgsql-cache', '', 0, 1, 1, 1, 'int', 'Cache size used by osm2pgsql'),
33 array('import-node', '', 0, 1, 1, 1, 'int', 'Re-import node'),
34 array('import-way', '', 0, 1, 1, 1, 'int', 'Re-import way'),
35 array('import-relation', '', 0, 1, 1, 1, 'int', 'Re-import relation'),
36 array('import-from-main-api', '', 0, 1, 0, 0, 'bool', 'Use OSM API instead of Overpass to download objects'),
38 array('index', '', 0, 1, 0, 0, 'bool', 'Index'),
39 array('index-rank', '', 0, 1, 1, 1, 'int', 'Rank to start indexing from'),
40 array('index-instances', '', 0, 1, 1, 1, 'int', 'Number of indexing instances (threads)'),
42 array('recompute-word-counts', '', 0, 1, 0, 0, 'bool', 'Compute frequency of full-word search terms'),
43 array('update-address-levels', '', 0, 1, 0, 0, 'bool', 'Reimport address level configuration (EXPERT)'),
44 array('recompute-importance', '', 0, 1, 0, 0, 'bool', 'Recompute place importances'),
46 array('project-dir', '', 0, 1, 1, 1, 'realpath', 'Base directory of the Nominatim installation (default: .)'),
49 getCmdOpt($_SERVER['argv'], $aCMDOptions, $aResult, true, true);
51 loadSettings($aCMDResult['project-dir'] ?? getcwd());
54 if (!isset($aResult['index-instances'])) $aResult['index-instances'] = 1;
55 if (!isset($aResult['index-rank'])) $aResult['index-rank'] = 0;
57 date_default_timezone_set('Etc/UTC');
59 $oDB = new Nominatim\DB();
61 $fPostgresVersion = $oDB->getPostgresVersion();
63 $aDSNInfo = Nominatim\DB::parseDSN(getSetting('DATABASE_DSN'));
64 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
66 // cache memory to be used by osm2pgsql, should not be more than the available memory
67 $iCacheMemory = (isset($aResult['osm2pgsql-cache'])?$aResult['osm2pgsql-cache']:2000);
68 if ($iCacheMemory + 500 > getTotalMemoryMB()) {
69 $iCacheMemory = getCacheMemoryMB();
70 echo "WARNING: resetting cache memory to $iCacheMemory\n";
73 $oOsm2pgsqlCmd = (new \Nominatim\Shell(getOsm2pgsqlBinary()))
74 ->addParams('--hstore')
75 ->addParams('--latlong')
76 ->addParams('--append')
78 ->addParams('--with-forward-dependencies', 'false')
79 ->addParams('--log-progress', 'true')
80 ->addParams('--number-processes', 1)
81 ->addParams('--cache', $iCacheMemory)
82 ->addParams('--output', 'gazetteer')
83 ->addParams('--style', getImportStyle())
84 ->addParams('--database', $aDSNInfo['database'])
85 ->addParams('--port', $aDSNInfo['port']);
87 if (isset($aDSNInfo['hostspec']) && $aDSNInfo['hostspec']) {
88 $oOsm2pgsqlCmd->addParams('--host', $aDSNInfo['hostspec']);
90 if (isset($aDSNInfo['username']) && $aDSNInfo['username']) {
91 $oOsm2pgsqlCmd->addParams('--user', $aDSNInfo['username']);
93 if (isset($aDSNInfo['password']) && $aDSNInfo['password']) {
94 $oOsm2pgsqlCmd->addEnvPair('PGPASSWORD', $aDSNInfo['password']);
96 if (getSetting('FLATNODE_FILE')) {
97 $oOsm2pgsqlCmd->addParams('--flat-nodes', getSetting('FLATNODE_FILE'));
99 if ($fPostgresVersion >= 11.0) {
100 $oOsm2pgsqlCmd->addEnvPair(
102 '-c jit=off -c max_parallel_workers_per_gather=0'
106 $oNominatimCmd = new \Nominatim\Shell(getSetting('NOMINATIM_TOOL'));
107 if ($aResult['quiet']) {
108 $oNominatimCmd->addParams('--quiet');
110 if ($aResult['verbose']) {
111 $oNominatimCmd->addParams('--verbose');
114 $sPyosmiumBin = getSetting('PYOSMIUM_BINARY');
115 $sBaseURL = getSetting('REPLICATION_URL');
118 if ($aResult['init-updates']) {
119 $oCmd = (clone($oNominatimCmd))->addParams('replication', '--init');
121 if ($aResult['no-update-functions']) {
122 $oCmd->addParams('--no-update-functions');
128 if ($aResult['check-for-updates']) {
129 $aLastState = $oDB->getRow('SELECT sequence_id FROM import_status');
131 if (!$aLastState['sequence_id']) {
132 fail('Updates not set up. Please run ./utils/update.php --init-updates.');
135 $oCmd = (new \Nominatim\Shell(CONST_BinDir.'/check_server_for_updates.py'))
136 ->addParams($sBaseURL)
137 ->addParams($aLastState['sequence_id']);
138 $iRet = $oCmd->run();
143 if (isset($aResult['import-diff']) || isset($aResult['import-file'])) {
144 // import diffs and files directly (e.g. from osmosis --rri)
145 $sNextFile = isset($aResult['import-diff']) ? $aResult['import-diff'] : $aResult['import-file'];
147 if (!file_exists($sNextFile)) {
148 fail("Cannot open $sNextFile\n");
152 $oCMD = (clone $oOsm2pgsqlCmd)->addParams($sNextFile);
153 echo $oCMD->escapedCmd()."\n";
154 $iRet = $oCMD->run();
157 fail("Error from osm2pgsql, $iRet\n");
160 // Don't update the import status - we don't know what this file contains
163 if ($aResult['calculate-postcodes']) {
164 (clone($oNominatimCmd))->addParams('refresh', '--postcodes')->run();
167 $sTemporaryFile = CONST_InstallDir.'/osmosischange.osc';
169 $bUseOSMApi = isset($aResult['import-from-main-api']) && $aResult['import-from-main-api'];
171 if (isset($aResult['import-node']) && $aResult['import-node']) {
173 $sContentURL = 'https://www.openstreetmap.org/api/0.6/node/'.$aResult['import-node'];
175 $sContentURL = 'https://overpass-api.de/api/interpreter?data=node('.$aResult['import-node'].');out%20meta;';
179 if (isset($aResult['import-way']) && $aResult['import-way']) {
181 $sContentURL = 'https://www.openstreetmap.org/api/0.6/way/'.$aResult['import-way'].'/full';
183 $sContentURL = 'https://overpass-api.de/api/interpreter?data=(way('.$aResult['import-way'].');%3E;);out%20meta;';
187 if (isset($aResult['import-relation']) && $aResult['import-relation']) {
189 $sContentURL = 'https://www.openstreetmap.org/api/0.6/relation/'.$aResult['import-relation'].'/full';
191 $sContentURL = 'https://overpass-api.de/api/interpreter?data=(rel(id:'.$aResult['import-relation'].');%3E;);out%20meta;';
196 file_put_contents($sTemporaryFile, file_get_contents($sContentURL));
201 // import generated change file
203 $oCMD = (clone $oOsm2pgsqlCmd)->addParams($sTemporaryFile);
204 echo $oCMD->escapedCmd()."\n";
206 $iRet = $oCMD->run();
208 fail("osm2pgsql exited with error level $iRet\n");
212 if ($aResult['recompute-word-counts']) {
213 (clone($oNominatimCmd))->addParams('refresh', '--word-counts')->run();
216 if ($aResult['index']) {
217 (clone $oNominatimCmd)->addParams('index', '--minrank', $aResult['index-rank'])->run();
220 if ($aResult['update-address-levels']) {
221 (clone($oNominatimCmd))->addParams('refresh', '--address-levels')->run();
224 if ($aResult['recompute-importance']) {
225 echo "Updating importance values for database.\n";
226 $oDB = new Nominatim\DB();
229 $sSQL = 'ALTER TABLE placex DISABLE TRIGGER ALL;';
230 $sSQL .= 'UPDATE placex SET (wikipedia, importance) =';
231 $sSQL .= ' (SELECT wikipedia, importance';
232 $sSQL .= ' FROM compute_importance(extratags, country_code, osm_type, osm_id));';
233 $sSQL .= 'UPDATE placex s SET wikipedia = d.wikipedia, importance = d.importance';
234 $sSQL .= ' FROM placex d';
235 $sSQL .= ' WHERE s.place_id = d.linked_place_id and d.wikipedia is not null';
236 $sSQL .= ' and (s.wikipedia is null or s.importance < d.importance);';
237 $sSQL .= 'ALTER TABLE placex ENABLE TRIGGER ALL;';
241 if ($aResult['import-osmosis'] || $aResult['import-osmosis-all']) {
243 if (strpos($sBaseURL, 'download.geofabrik.de') !== false && getSetting('REPLICATION_UPDATE_INTERVAL') < 86400) {
244 fail('Error: Update interval too low for download.geofabrik.de. ' .
245 "Please check install documentation (https://nominatim.org/release-docs/latest/admin/Import-and-Update#setting-up-the-update-process)\n");
248 $sImportFile = CONST_InstallDir.'/osmosischange.osc';
250 $oCMDDownload = (new \Nominatim\Shell($sPyosmiumBin))
251 ->addParams('--server', $sBaseURL)
252 ->addParams('--outfile', $sImportFile)
253 ->addParams('--size', getSetting('REPLICATION_MAX_DIFF'));
255 $oCMDImport = (clone $oOsm2pgsqlCmd)->addParams($sImportFile);
258 $fStartTime = time();
259 $aLastState = $oDB->getRow('SELECT *, EXTRACT (EPOCH FROM lastimportdate) as unix_ts FROM import_status');
261 if (!$aLastState['sequence_id']) {
262 echo "Updates not set up. Please run ./utils/update.php --init-updates.\n";
266 echo 'Currently at sequence '.$aLastState['sequence_id'].' ('.$aLastState['lastimportdate'].') - '.$aLastState['indexed']." indexed\n";
268 $sBatchEnd = $aLastState['lastimportdate'];
269 $iEndSequence = $aLastState['sequence_id'];
271 if ($aLastState['indexed']) {
272 // Sleep if the update interval has not yet been reached.
273 $fNextUpdate = $aLastState['unix_ts'] + getSetting('REPLICATION_UPDATE_INTERVAL');
274 if ($fNextUpdate > $fStartTime) {
275 $iSleepTime = $fNextUpdate - $fStartTime;
276 echo "Waiting for next update for $iSleepTime sec.";
280 // Download the next batch of changes.
282 $fCMDStartTime = time();
283 $iNextSeq = (int) $aLastState['sequence_id'];
286 $oCMD = (clone $oCMDDownload)->addParams('--start-id', $iNextSeq);
287 echo $oCMD->escapedCmd()."\n";
288 if (file_exists($sImportFile)) {
289 unlink($sImportFile);
291 exec($oCMD->escapedCmd(), $aOutput, $iResult);
294 $sSleep = getSetting('REPLICATION_RECHECK_INTERVAL');
295 echo 'No new updates. Sleeping for '.$sSleep." sec.\n";
297 } elseif ($iResult != 0) {
298 echo 'ERROR: updates failed.';
301 $iEndSequence = (int)$aOutput[0];
305 // get the newest object from the diff file
308 $oCMD = new \Nominatim\Shell(CONST_BinDir.'/osm_file_date.py', $sImportFile);
309 exec($oCMD->escapedCmd(), $sBatchEnd, $iRet);
311 echo "Diff file is empty. skipping import.\n";
312 if (!$aResult['import-osmosis-all']) {
319 fail('Error getting date from diff file.');
321 $sBatchEnd = $sBatchEnd[0];
324 $fCMDStartTime = time();
327 echo $oCMDImport->escapedCmd()."\n";
329 $iErrorLevel = $oCMDImport->run();
331 echo "Error executing osm2pgsql: $iErrorLevel\n";
335 // write the update logs
336 $iFileSize = filesize($sImportFile);
337 $sSQL = 'INSERT INTO import_osmosis_log';
338 $sSQL .= '(batchend, batchseq, batchsize, starttime, endtime, event)';
339 $sSQL .= " values ('$sBatchEnd',$iEndSequence,$iFileSize,'";
340 $sSQL .= date('Y-m-d H:i:s', $fCMDStartTime)."','";
341 $sSQL .= date('Y-m-d H:i:s')."','import')";
346 $sSQL = "UPDATE import_status SET lastimportdate = '$sBatchEnd', indexed=false, sequence_id = $iEndSequence";
349 echo date('Y-m-d H:i:s')." Completed download step for $sBatchEnd in ".round((time()-$fCMDStartTime)/60, 2)." minutes\n";
353 if (!$aResult['no-index']) {
354 $fCMDStartTime = time();
356 $oThisIndexCmd = clone($oNominatimCmd);
357 $oThisIndexCmd->addParams('index');
358 echo $oThisIndexCmd->escapedCmd()."\n";
359 $iErrorLevel = $oThisIndexCmd->run();
361 echo "Error: $iErrorLevel\n";
365 $sSQL = 'INSERT INTO import_osmosis_log';
366 $sSQL .= '(batchend, batchseq, batchsize, starttime, endtime, event)';
367 $sSQL .= " values ('$sBatchEnd',$iEndSequence,NULL,'";
368 $sSQL .= date('Y-m-d H:i:s', $fCMDStartTime)."','";
369 $sSQL .= date('Y-m-d H:i:s')."','index')";
372 echo date('Y-m-d H:i:s')." Completed index step for $sBatchEnd in ".round((time()-$fCMDStartTime)/60, 2)." minutes\n";
374 if ($aResult['import-osmosis-all']) {
375 echo "Error: --no-index cannot be used with continuous imports (--import-osmosis-all).\n";
380 $fDuration = time() - $fStartTime;
381 echo date('Y-m-d H:i:s')." Completed all for $sBatchEnd in ".round($fDuration/60, 2)." minutes\n";
382 if (!$aResult['import-osmosis-all']) exit(0);