4 require_once(dirname(dirname(__FILE__)).'/settings/settings.php');
5 require_once(CONST_BasePath.'/lib/init-cmd.php');
6 ini_set('memory_limit', '800M');
10 "Import / update / index osm data",
11 array('help', 'h', 0, 1, 0, 0, false, 'Show Help'),
12 array('quiet', 'q', 0, 1, 0, 0, 'bool', 'Quiet output'),
13 array('verbose', 'v', 0, 1, 0, 0, 'bool', 'Verbose output'),
15 array('import-osmosis', '', 0, 1, 0, 0, 'bool', 'Import using osmosis'),
16 array('import-osmosis-all', '', 0, 1, 0, 0, 'bool', 'Import using osmosis forever'),
17 array('no-npi', '', 0, 1, 0, 0, 'bool', '(obsolate)'),
18 array('no-index', '', 0, 1, 0, 0, 'bool', 'Do not index the new data'),
20 array('import-all', '', 0, 1, 0, 0, 'bool', 'Import all available files'),
22 array('import-file', '', 0, 1, 1, 1, 'realpath', 'Re-import data from an OSM file'),
23 array('import-diff', '', 0, 1, 1, 1, 'realpath', 'Import a diff (osc) file from local file system'),
24 array('osm2pgsql-cache', '', 0, 1, 1, 1, 'int', 'Cache size used by osm2pgsql'),
26 array('import-node', '', 0, 1, 1, 1, 'int', 'Re-import node'),
27 array('import-way', '', 0, 1, 1, 1, 'int', 'Re-import way'),
28 array('import-relation', '', 0, 1, 1, 1, 'int', 'Re-import relation'),
29 array('import-from-main-api', '', 0, 1, 0, 0, 'bool', 'Use OSM API instead of Overpass to download objects'),
31 array('index', '', 0, 1, 0, 0, 'bool', 'Index'),
32 array('index-rank', '', 0, 1, 1, 1, 'int', 'Rank to start indexing from'),
33 array('index-instances', '', 0, 1, 1, 1, 'int', 'Number of indexing instances (threads)'),
35 array('deduplicate', '', 0, 1, 0, 0, 'bool', 'Deduplicate tokens'),
37 getCmdOpt($_SERVER['argv'], $aCMDOptions, $aResult, true, true);
39 if (!isset($aResult['index-instances'])) $aResult['index-instances'] = 1;
40 if (!isset($aResult['index-rank'])) $aResult['index-rank'] = 0;
42 date_default_timezone_set('Etc/UTC');
46 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
47 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
49 // cache memory to be used by osm2pgsql, should not be more than the available memory
50 $iCacheMemory = (isset($aResult['osm2pgsql-cache'])?$aResult['osm2pgsql-cache']:2000);
51 if ($iCacheMemory + 500 > getTotalMemoryMB()) {
52 $iCacheMemory = getCacheMemoryMB();
53 echo "WARNING: resetting cache memory to $iCacheMemory\n";
55 $sOsm2pgsqlCmd = CONST_Osm2pgsql_Binary.' -klas --number-processes 1 -C '.$iCacheMemory.' -O gazetteer -d '.$aDSNInfo['database'].' -P '.$aDSNInfo['port'];
56 if (!is_null(CONST_Osm2pgsql_Flatnode_File)) {
57 $sOsm2pgsqlCmd .= ' --flat-nodes '.CONST_Osm2pgsql_Flatnode_File;
61 if (isset($aResult['import-diff'])) {
62 // import diff directly (e.g. from osmosis --rri)
63 $sNextFile = $aResult['import-diff'];
64 if (!file_exists($sNextFile)) {
65 fail("Cannot open $sNextFile\n");
69 $sCMD = $sOsm2pgsqlCmd.' '.$sNextFile;
71 exec($sCMD, $sJunk, $iErrorLevel);
74 fail("Error from osm2pgsql, $iErrorLevel\n");
77 // Don't update the import status - we don't know what this file contains
80 $sTemporaryFile = CONST_BasePath.'/data/osmosischange.osc';
82 if (isset($aResult['import-file']) && $aResult['import-file']) {
84 $sCMD = CONST_Osmosis_Binary.' --read-xml \''.$aResult['import-file'].'\' --read-empty --derive-change --write-xml-change '.$sTemporaryFile;
86 exec($sCMD, $sJunk, $iErrorLevel);
88 fail("Error converting osm to osc, osmosis returned: $iErrorLevel\n");
92 $bUseOSMApi = isset($aResult['import-from-main-api']) && $aResult['import-from-main-api'];
94 if (isset($aResult['import-node']) && $aResult['import-node']) {
96 $sContentURL = 'http://www.openstreetmap.org/api/0.6/node/'.$aResult['import-node'];
98 $sContentURL = 'http://overpass-api.de/api/interpreter?data=node('.$aResult['import-node'].');out%20meta;';
102 if (isset($aResult['import-way']) && $aResult['import-way']) {
104 $sContentURL = 'http://www.openstreetmap.org/api/0.6/way/'.$aResult['import-way'].'/full';
106 $sContentURL = 'http://overpass-api.de/api/interpreter?data=(way('.$aResult['import-way'].');node(w););out%20meta;';
110 if (isset($aResult['import-relation']) && $aResult['import-relation']) {
112 $sContentURLsModifyXMLstr = 'http://www.openstreetmap.org/api/0.6/relation/'.$aResult['import-relation'].'/full';
114 $sContentURL = 'http://overpass-api.de/api/interpreter?data=((rel('.$aResult['import-relation'].');way(r);node(w));node(r));out%20meta;';
119 $sModifyXMLstr = file_get_contents($sContentURL);
123 0 => array("pipe", "r"), // stdin
124 1 => array("pipe", "w"), // stdout
125 2 => array("pipe", "w") // stderr
127 $sCMD = CONST_Osmosis_Binary.' --read-xml - --read-empty --derive-change --write-xml-change '.$sTemporaryFile;
129 $hProc = proc_open($sCMD, $aSpec, $aPipes);
130 if (!is_resource($hProc)) {
131 fail("Error converting osm to osc, osmosis failed\n");
133 fwrite($aPipes[0], $sModifyXMLstr);
135 $sOut = stream_get_contents($aPipes[1]);
136 if ($aResult['verbose']) echo $sOut;
138 $sErrors = stream_get_contents($aPipes[2]);
139 if ($aResult['verbose']) echo $sErrors;
141 if ($iError = proc_close($hProc)) {
144 fail("Error converting osm to osc, osmosis returned: $iError\n");
149 // import generated change file
150 $sCMD = $sOsm2pgsqlCmd.' '.$sTemporaryFile;
152 exec($sCMD, $sJunk, $iErrorLevel);
154 fail("osm2pgsql exited with error level $iErrorLevel\n");
158 if ($aResult['deduplicate']) {
160 if (getPostgresVersion() < 9.3) {
161 fail("ERROR: deduplicate is only currently supported in postgresql 9.3");
165 $sSQL = 'select partition from country_name order by country_code';
166 $aPartitions = chksql($oDB->getCol($sSQL));
169 $sSQL = "select word_token,count(*) from word where substr(word_token, 1, 1) = ' '";
170 $sSQL .= " and class is null and type is null and country_code is null";
171 $sSQL .= " group by word_token having count(*) > 1 order by word_token";
172 $aDuplicateTokens = chksql($oDB->getAll($sSQL));
173 foreach ($aDuplicateTokens as $aToken) {
174 if (trim($aToken['word_token']) == '' || trim($aToken['word_token']) == '-') continue;
175 echo "Deduping ".$aToken['word_token']."\n";
176 $sSQL = "select word_id,";
177 $sSQL .= " (select count(*) from search_name where nameaddress_vector @> ARRAY[word_id]) as num";
178 $sSQL .= " from word where word_token = '".$aToken['word_token'];
179 $sSQL .= "' and class is null and type is null and country_code is null order by num desc";
180 $aTokenSet = chksql($oDB->getAll($sSQL));
182 $aKeep = array_shift($aTokenSet);
183 $iKeepID = $aKeep['word_id'];
185 foreach ($aTokenSet as $aRemove) {
186 $sSQL = "update search_name set";
187 $sSQL .= " name_vector = array_replace(name_vector,".$aRemove['word_id'].",".$iKeepID."),";
188 $sSQL .= " nameaddress_vector = array_replace(nameaddress_vector,".$aRemove['word_id'].",".$iKeepID.")";
189 $sSQL .= " where name_vector @> ARRAY[".$aRemove['word_id']."]";
190 chksql($oDB->query($sSQL));
192 $sSQL = "update search_name set";
193 $sSQL .= " nameaddress_vector = array_replace(nameaddress_vector,".$aRemove['word_id'].",".$iKeepID.")";
194 $sSQL .= " where nameaddress_vector @> ARRAY[".$aRemove['word_id']."]";
195 chksql($oDB->query($sSQL));
197 $sSQL = "update location_area_country set";
198 $sSQL .= " keywords = array_replace(keywords,".$aRemove['word_id'].",".$iKeepID.")";
199 $sSQL .= " where keywords @> ARRAY[".$aRemove['word_id']."]";
200 chksql($oDB->query($sSQL));
202 foreach ($aPartitions as $sPartition) {
203 $sSQL = "update search_name_".$sPartition." set";
204 $sSQL .= " name_vector = array_replace(name_vector,".$aRemove['word_id'].",".$iKeepID.")";
205 $sSQL .= " where name_vector @> ARRAY[".$aRemove['word_id']."]";
206 chksql($oDB->query($sSQL));
208 $sSQL = "update location_area_country set";
209 $sSQL .= " keywords = array_replace(keywords,".$aRemove['word_id'].",".$iKeepID.")";
210 $sSQL .= " where keywords @> ARRAY[".$aRemove['word_id']."]";
211 chksql($oDB->query($sSQL));
214 $sSQL = "delete from word where word_id = ".$aRemove['word_id'];
215 chksql($oDB->query($sSQL));
220 if ($aResult['index']) {
221 passthru(CONST_InstallPath.'/nominatim/nominatim -i -d '.$aDSNInfo['database'].' -P '.$aDSNInfo['port'].' -t '.$aResult['index-instances'].' -r '.$aResult['index-rank']);
224 if ($aResult['import-osmosis'] || $aResult['import-osmosis-all']) {
226 if (strpos(CONST_Replication_Url, 'download.geofabrik.de') !== false && CONST_Replication_Update_Interval < 86400) {
227 fail("Error: Update interval too low for download.geofabrik.de. Please check install documentation (http://wiki.openstreetmap.org/wiki/Nominatim/Installation#Updates)\n");
230 $sImportFile = CONST_BasePath.'/data/osmosischange.osc';
231 $sOsmosisConfigDirectory = CONST_InstallPath.'/settings';
232 $sCMDDownload = CONST_Osmosis_Binary.' --read-replication-interval workingDirectory='.$sOsmosisConfigDirectory.' --simplify-change --write-xml-change '.$sImportFile;
233 $sCMDCheckReplicationLag = CONST_Osmosis_Binary.' -q --read-replication-lag workingDirectory='.$sOsmosisConfigDirectory;
234 $sCMDImport = $sOsm2pgsqlCmd.' '.$sImportFile;
235 $sCMDIndex = CONST_InstallPath.'/nominatim/nominatim -i -d '.$aDSNInfo['database'].' -P '.$aDSNInfo['port'].' -t '.$aResult['index-instances'];
238 $fStartTime = time();
241 if (!file_exists($sImportFile)) {
242 // First check if there are new updates published (except for minutelies - there's always new diffs to process)
243 if (CONST_Replication_Update_Interval > 60) {
244 unset($aReplicationLag);
245 exec($sCMDCheckReplicationLag, $aReplicationLag, $iErrorLevel);
246 while ($iErrorLevel > 0 || $aReplicationLag[0] < 1) {
248 echo "Error: $iErrorLevel. ";
249 echo "Re-trying: ".$sCMDCheckReplicationLag." in ".CONST_Replication_Recheck_Interval." secs\n";
253 sleep(CONST_Replication_Recheck_Interval);
254 unset($aReplicationLag);
255 exec($sCMDCheckReplicationLag, $aReplicationLag, $iErrorLevel);
257 // There are new replication files - use osmosis to download the file
258 echo "\n".date('Y-m-d H:i:s')." Replication Delay is ".$aReplicationLag[0]."\n";
260 $fStartTime = time();
261 $fCMDStartTime = time();
262 echo $sCMDDownload."\n";
263 exec($sCMDDownload, $sJunk, $iErrorLevel);
264 while ($iErrorLevel > 0) {
265 echo "Error: $iErrorLevel\n";
267 echo 'Re-trying: '.$sCMDDownload."\n";
268 exec($sCMDDownload, $sJunk, $iErrorLevel);
270 $iFileSize = filesize($sImportFile);
271 $sBatchEnd = getosmosistimestamp($sOsmosisConfigDirectory);
272 $sSQL = "INSERT INTO import_osmosis_log values ('$sBatchEnd',$iFileSize,'".date('Y-m-d H:i:s', $fCMDStartTime)."','".date('Y-m-d H:i:s')."','osmosis')";
275 echo date('Y-m-d H:i:s')." Completed osmosis step for $sBatchEnd in ".round((time()-$fCMDStartTime)/60, 2)." minutes\n";
278 $iFileSize = filesize($sImportFile);
279 $sBatchEnd = getosmosistimestamp($sOsmosisConfigDirectory);
282 $fCMDStartTime = time();
283 echo $sCMDImport."\n";
284 exec($sCMDImport, $sJunk, $iErrorLevel);
286 echo "Error: $iErrorLevel\n";
289 $sSQL = "INSERT INTO import_osmosis_log values ('$sBatchEnd',$iFileSize,'".date('Y-m-d H:i:s', $fCMDStartTime)."','".date('Y-m-d H:i:s')."','osm2pgsql')";
292 echo date('Y-m-d H:i:s')." Completed osm2pgsql step for $sBatchEnd in ".round((time()-$fCMDStartTime)/60, 2)." minutes\n";
294 // Archive for debug?
295 unlink($sImportFile);
297 $sBatchEnd = getosmosistimestamp($sOsmosisConfigDirectory);
300 $sThisIndexCmd = $sCMDIndex;
301 if (!isset($aResult['index-instances'])) {
302 if (getLoadAverage() < 24)
303 $iIndexInstances = 2;
305 $iIndexInstances = 1;
307 $iIndexInstances = $aResult['index-instances'];
309 $sThisIndexCmd = $sCMDIndex.' -t '.$iIndexInstances;
310 $fCMDStartTime = time();
312 if (!$aResult['no-index']) {
313 echo "$sThisIndexCmd\n";
314 exec($sThisIndexCmd, $sJunk, $iErrorLevel);
316 echo "Error: $iErrorLevel\n";
321 $sSQL = "INSERT INTO import_osmosis_log values ('$sBatchEnd',$iFileSize,'".date('Y-m-d H:i:s', $fCMDStartTime)."','".date('Y-m-d H:i:s')."','index')";
324 echo date('Y-m-d H:i:s')." Completed index step for $sBatchEnd in ".round((time()-$fCMDStartTime)/60, 2)." minutes\n";
326 $sSQL = "update import_status set lastimportdate = '$sBatchEnd'";
329 $fDuration = time() - $fStartTime;
330 echo date('Y-m-d H:i:s')." Completed all for $sBatchEnd in ".round($fDuration/60, 2)." minutes\n";
331 if (!$aResult['import-osmosis-all']) exit(0);
333 if (CONST_Replication_Update_Interval > 60) {
334 $iSleep = max(0, (strtotime($sBatchEnd)+CONST_Replication_Update_Interval-time()));
336 $iSleep = max(0, CONST_Replication_Update_Interval-$fDuration);
338 echo date('Y-m-d H:i:s')." Sleeping $iSleep seconds\n";
344 function getosmosistimestamp($sOsmosisConfigDirectory)
346 $sStateFile = file_get_contents($sOsmosisConfigDirectory.'/state.txt');
347 preg_match('#timestamp=(.+)#', $sStateFile, $aResult);
348 return str_replace('\:', ':', $aResult[1]);