]> git.openstreetmap.org Git - nominatim.git/blob - utils/update.php
bracket spacing for if/else/for/foreach/while/switch according to PSR2 standard
[nominatim.git] / utils / update.php
1 #!/usr/bin/php -Cq
2 <?php
3
4 require_once(dirname(dirname(__FILE__)).'/settings/settings.php');
5 require_once(CONST_BasePath.'/lib/init-cmd.php');
6 ini_set('memory_limit', '800M');
7
8 $aCMDOptions = array(
9     "Import / update / index osm data",
10     array('help', 'h', 0, 1, 0, 0, false, 'Show Help'),
11     array('quiet', 'q', 0, 1, 0, 0, 'bool', 'Quiet output'),
12     array('verbose', 'v', 0, 1, 0, 0, 'bool', 'Verbose output'),
13
14     array('import-osmosis', '', 0, 1, 0, 0, 'bool', 'Import using osmosis'),
15     array('import-osmosis-all', '', 0, 1, 0, 0, 'bool', 'Import using osmosis forever'),
16     array('no-npi', '', 0, 1, 0, 0, 'bool', '(obsolate)'),
17     array('no-index', '', 0, 1, 0, 0, 'bool', 'Do not index the new data'),
18
19     array('import-all', '', 0, 1, 0, 0, 'bool', 'Import all available files'),
20
21     array('import-file', '', 0, 1, 1, 1, 'realpath', 'Re-import data from an OSM file'),
22     array('import-diff', '', 0, 1, 1, 1, 'realpath', 'Import a diff (osc) file from local file system'),
23     array('osm2pgsql-cache', '', 0, 1, 1, 1, 'int', 'Cache size used by osm2pgsql'),
24
25     array('import-node', '', 0, 1, 1, 1, 'int', 'Re-import node'),
26     array('import-way', '', 0, 1, 1, 1, 'int', 'Re-import way'),
27     array('import-relation', '', 0, 1, 1, 1, 'int', 'Re-import relation'),
28     array('import-from-main-api', '', 0, 1, 0, 0, 'bool', 'Use OSM API instead of Overpass to download objects'),
29
30     array('index', '', 0, 1, 0, 0, 'bool', 'Index'),
31     array('index-rank', '', 0, 1, 1, 1, 'int', 'Rank to start indexing from'),
32     array('index-instances', '', 0, 1, 1, 1, 'int', 'Number of indexing instances (threads)'),
33
34     array('deduplicate', '', 0, 1, 0, 0, 'bool', 'Deduplicate tokens'),
35 );
36 getCmdOpt($_SERVER['argv'], $aCMDOptions, $aResult, true, true);
37
38 if (!isset($aResult['index-instances'])) $aResult['index-instances'] = 1;
39 if (!isset($aResult['index-rank'])) $aResult['index-rank'] = 0;
40
41 date_default_timezone_set('Etc/UTC');
42
43 $oDB =& getDB();
44
45 $aDSNInfo = DB::parseDSN(CONST_Database_DSN);
46 if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
47
48 // cache memory to be used by osm2pgsql, should not be more than the available memory
49 $iCacheMemory = (isset($aResult['osm2pgsql-cache'])?$aResult['osm2pgsql-cache']:2000);
50 if ($iCacheMemory + 500 > getTotalMemoryMB()) {
51     $iCacheMemory = getCacheMemoryMB();
52     echo "WARNING: resetting cache memory to $iCacheMemory\n";
53 }
54 $sOsm2pgsqlCmd = CONST_Osm2pgsql_Binary.' -klas --number-processes 1 -C '.$iCacheMemory.' -O gazetteer -d '.$aDSNInfo['database'].' -P '.$aDSNInfo['port'];
55 if (!is_null(CONST_Osm2pgsql_Flatnode_File)) {
56     $sOsm2pgsqlCmd .= ' --flat-nodes '.CONST_Osm2pgsql_Flatnode_File;
57 }
58
59
60 if (isset($aResult['import-diff'])) {
61     // import diff directly (e.g. from osmosis --rri)
62     $sNextFile = $aResult['import-diff'];
63     if (!file_exists($sNextFile)) {
64         fail("Cannot open $sNextFile\n");
65     }
66
67     // Import the file
68     $sCMD = $sOsm2pgsqlCmd.' '.$sNextFile;
69     echo $sCMD."\n";
70     exec($sCMD, $sJunk, $iErrorLevel);
71
72     if ($iErrorLevel) {
73         fail("Error from osm2pgsql, $iErrorLevel\n");
74     }
75
76     // Don't update the import status - we don't know what this file contains
77 }
78
79 $sTemporaryFile = CONST_BasePath.'/data/osmosischange.osc';
80 $bHaveDiff = false;
81 if (isset($aResult['import-file']) && $aResult['import-file']) {
82     $bHaveDiff = true;
83     $sCMD = CONST_Osmosis_Binary.' --read-xml \''.$aResult['import-file'].'\' --read-empty --derive-change --write-xml-change '.$sTemporaryFile;
84     echo $sCMD."\n";
85     exec($sCMD, $sJunk, $iErrorLevel);
86     if ($iErrorLevel) {
87         fail("Error converting osm to osc, osmosis returned: $iErrorLevel\n");
88     }
89 }
90
91 $bUseOSMApi = isset($aResult['import-from-main-api']) && $aResult['import-from-main-api'];
92 $sContentURL = '';
93 if (isset($aResult['import-node']) && $aResult['import-node']) {
94     if ($bUseOSMApi) {
95         $sContentURL = 'http://www.openstreetmap.org/api/0.6/node/'.$aResult['import-node'];
96     } else {
97         $sContentURL = 'http://overpass-api.de/api/interpreter?data=node('.$aResult['import-node'].');out%20meta;';
98     }
99 }
100
101 if (isset($aResult['import-way']) && $aResult['import-way']) {
102     if ($bUseOSMApi) {
103         $sContentURL = 'http://www.openstreetmap.org/api/0.6/way/'.$aResult['import-way'].'/full';
104     } else {
105         $sContentURL = 'http://overpass-api.de/api/interpreter?data=(way('.$aResult['import-way'].');node(w););out%20meta;';
106     }
107 }
108
109 if (isset($aResult['import-relation']) && $aResult['import-relation']) {
110     if ($bUseOSMApi) {
111         $sContentURLsModifyXMLstr = 'http://www.openstreetmap.org/api/0.6/relation/'.$aResult['import-relation'].'/full';
112     } else {
113         $sContentURL = 'http://overpass-api.de/api/interpreter?data=((rel('.$aResult['import-relation'].');way(r);node(w));node(r));out%20meta;';
114     }
115 }
116
117 if ($sContentURL) {
118     $sModifyXMLstr = file_get_contents($sContentURL);
119     $bHaveDiff = true;
120
121     $aSpec = array(
122         0 => array("pipe", "r"),  // stdin
123         1 => array("pipe", "w"),  // stdout
124         2 => array("pipe", "w") // stderr
125     );
126     $sCMD = CONST_Osmosis_Binary.' --read-xml - --read-empty --derive-change --write-xml-change '.$sTemporaryFile;
127     echo $sCMD."\n";
128     $hProc = proc_open($sCMD, $aSpec, $aPipes);
129     if (!is_resource($hProc)) {
130         fail("Error converting osm to osc, osmosis failed\n");
131     }
132     fwrite($aPipes[0], $sModifyXMLstr);
133     fclose($aPipes[0]);
134     $sOut = stream_get_contents($aPipes[1]);
135     if ($aResult['verbose']) echo $sOut;
136     fclose($aPipes[1]);
137     $sErrors = stream_get_contents($aPipes[2]);
138     if ($aResult['verbose']) echo $sErrors;
139     fclose($aPipes[2]);
140     if ($iError = proc_close($hProc)) {
141         echo $sOut;
142         echo $sErrors;
143         fail("Error converting osm to osc, osmosis returned: $iError\n");
144     }
145 }
146
147 if ($bHaveDiff) {
148     // import generated change file
149     $sCMD = $sOsm2pgsqlCmd.' '.$sTemporaryFile;
150     echo $sCMD."\n";
151     exec($sCMD, $sJunk, $iErrorLevel);
152     if ($iErrorLevel) {
153         fail("osm2pgsql exited with error level $iErrorLevel\n");
154     }
155 }
156
157 if ($aResult['deduplicate']) {
158     //
159     if (getPostgresVersion() < 9.3) {
160         fail("ERROR: deduplicate is only currently supported in postgresql 9.3");
161     }
162
163     $oDB =& getDB();
164     $sSQL = 'select partition from country_name order by country_code';
165     $aPartitions = chksql($oDB->getCol($sSQL));
166     $aPartitions[] = 0;
167
168     $sSQL = "select word_token,count(*) from word where substr(word_token, 1, 1) = ' ' and class is null and type is null and country_code is null group by word_token having count(*) > 1 order by word_token";
169     $aDuplicateTokens = chksql($oDB->getAll($sSQL));
170     foreach ($aDuplicateTokens as $aToken) {
171         if (trim($aToken['word_token']) == '' || trim($aToken['word_token']) == '-') continue;
172         echo "Deduping ".$aToken['word_token']."\n";
173         $sSQL = "select word_id,(select count(*) from search_name where nameaddress_vector @> ARRAY[word_id]) as num from word where word_token = '".$aToken['word_token']."' and class is null and type is null and country_code is null order by num desc";
174         $aTokenSet = chksql($oDB->getAll($sSQL));
175
176         $aKeep = array_shift($aTokenSet);
177         $iKeepID = $aKeep['word_id'];
178
179         foreach ($aTokenSet as $aRemove) {
180             $sSQL = "update search_name set";
181             $sSQL .= " name_vector = array_replace(name_vector,".$aRemove['word_id'].",".$iKeepID."),";
182             $sSQL .= " nameaddress_vector = array_replace(nameaddress_vector,".$aRemove['word_id'].",".$iKeepID.")";
183             $sSQL .= " where name_vector @> ARRAY[".$aRemove['word_id']."]";
184             chksql($oDB->query($sSQL));
185
186             $sSQL = "update search_name set";
187             $sSQL .= " nameaddress_vector = array_replace(nameaddress_vector,".$aRemove['word_id'].",".$iKeepID.")";
188             $sSQL .= " where nameaddress_vector @> ARRAY[".$aRemove['word_id']."]";
189             chksql($oDB->query($sSQL));
190
191             $sSQL = "update location_area_country set";
192             $sSQL .= " keywords = array_replace(keywords,".$aRemove['word_id'].",".$iKeepID.")";
193             $sSQL .= " where keywords @> ARRAY[".$aRemove['word_id']."]";
194             chksql($oDB->query($sSQL));
195
196             foreach ($aPartitions as $sPartition) {
197                 $sSQL = "update search_name_".$sPartition." set";
198                 $sSQL .= " name_vector = array_replace(name_vector,".$aRemove['word_id'].",".$iKeepID.")";
199                 $sSQL .= " where name_vector @> ARRAY[".$aRemove['word_id']."]";
200                 chksql($oDB->query($sSQL));
201
202                 $sSQL = "update location_area_country set";
203                 $sSQL .= " keywords = array_replace(keywords,".$aRemove['word_id'].",".$iKeepID.")";
204                 $sSQL .= " where keywords @> ARRAY[".$aRemove['word_id']."]";
205                 chksql($oDB->query($sSQL));
206             }
207
208             $sSQL = "delete from word where word_id = ".$aRemove['word_id'];
209             chksql($oDB->query($sSQL));
210         }
211     }
212 }
213
214 if ($aResult['index']) {
215     passthru(CONST_InstallPath.'/nominatim/nominatim -i -d '.$aDSNInfo['database'].' -P '.$aDSNInfo['port'].' -t '.$aResult['index-instances'].' -r '.$aResult['index-rank']);
216 }
217
218 if ($aResult['import-osmosis'] || $aResult['import-osmosis-all']) {
219     //
220     if (strpos(CONST_Replication_Url, 'download.geofabrik.de') !== false && CONST_Replication_Update_Interval < 86400) {
221         fail("Error: Update interval too low for download.geofabrik.de.  Please check install documentation (http://wiki.openstreetmap.org/wiki/Nominatim/Installation#Updates)\n");
222     }
223
224     $sImportFile = CONST_BasePath.'/data/osmosischange.osc';
225     $sOsmosisConfigDirectory = CONST_InstallPath.'/settings';
226     $sCMDDownload = CONST_Osmosis_Binary.' --read-replication-interval workingDirectory='.$sOsmosisConfigDirectory.' --simplify-change --write-xml-change '.$sImportFile;
227     $sCMDCheckReplicationLag = CONST_Osmosis_Binary.' -q --read-replication-lag workingDirectory='.$sOsmosisConfigDirectory;
228     $sCMDImport = $sOsm2pgsqlCmd.' '.$sImportFile;
229     $sCMDIndex = CONST_InstallPath.'/nominatim/nominatim -i -d '.$aDSNInfo['database'].' -P '.$aDSNInfo['port'].' -t '.$aResult['index-instances'];
230
231     while (true) {
232         $fStartTime = time();
233         $iFileSize = 1001;
234
235         if (!file_exists($sImportFile)) {
236             // First check if there are new updates published (except for minutelies - there's always new diffs to process)
237             if (CONST_Replication_Update_Interval > 60) {
238                 unset($aReplicationLag);
239                 exec($sCMDCheckReplicationLag, $aReplicationLag, $iErrorLevel); 
240                 while ($iErrorLevel > 0 || $aReplicationLag[0] < 1) {
241                     if ($iErrorLevel) {
242                         echo "Error: $iErrorLevel. ";
243                         echo "Re-trying: ".$sCMDCheckReplicationLag." in ".CONST_Replication_Recheck_Interval." secs\n";
244                     } else {
245                         echo ".";
246                     }
247                     sleep(CONST_Replication_Recheck_Interval);
248                     unset($aReplicationLag);
249                     exec($sCMDCheckReplicationLag, $aReplicationLag, $iErrorLevel); 
250                 }
251                 // There are new replication files - use osmosis to download the file
252                 echo "\n".date('Y-m-d H:i:s')." Replication Delay is ".$aReplicationLag[0]."\n";
253             }
254             $fStartTime = time();
255             $fCMDStartTime = time();
256             echo $sCMDDownload."\n";
257             exec($sCMDDownload, $sJunk, $iErrorLevel);
258             while ($iErrorLevel > 0) {
259                 echo "Error: $iErrorLevel\n";
260                 sleep(60);
261                 echo 'Re-trying: '.$sCMDDownload."\n";
262                 exec($sCMDDownload, $sJunk, $iErrorLevel);
263             }
264             $iFileSize = filesize($sImportFile);
265             $sBatchEnd = getosmosistimestamp($sOsmosisConfigDirectory);
266             $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')";
267             var_Dump($sSQL);
268             $oDB->query($sSQL);
269             echo date('Y-m-d H:i:s')." Completed osmosis step for $sBatchEnd in ".round((time()-$fCMDStartTime)/60,2)." minutes\n";
270         }
271
272         $iFileSize = filesize($sImportFile);
273         $sBatchEnd = getosmosistimestamp($sOsmosisConfigDirectory);
274
275         // Import the file
276         $fCMDStartTime = time();
277         echo $sCMDImport."\n";
278         exec($sCMDImport, $sJunk, $iErrorLevel);
279         if ($iErrorLevel) {
280             echo "Error: $iErrorLevel\n";
281             exit($iErrorLevel);
282         }
283         $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')";
284         var_Dump($sSQL);
285         $oDB->query($sSQL);
286         echo date('Y-m-d H:i:s')." Completed osm2pgsql step for $sBatchEnd in ".round((time()-$fCMDStartTime)/60,2)." minutes\n";
287
288         // Archive for debug?
289         unlink($sImportFile);
290
291         $sBatchEnd = getosmosistimestamp($sOsmosisConfigDirectory);
292
293         // Index file
294         $sThisIndexCmd = $sCMDIndex;
295         $fCMDStartTime = time();
296
297         if (!$aResult['no-index']) {
298             echo "$sThisIndexCmd\n";
299             exec($sThisIndexCmd, $sJunk, $iErrorLevel);
300             if ($iErrorLevel) {
301                 echo "Error: $iErrorLevel\n";
302                 exit($iErrorLevel);
303             }
304         }
305
306         $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')";
307         var_Dump($sSQL);
308         $oDB->query($sSQL);
309         echo date('Y-m-d H:i:s')." Completed index step for $sBatchEnd in ".round((time()-$fCMDStartTime)/60,2)." minutes\n";
310
311         $sSQL = "update import_status set lastimportdate = '$sBatchEnd'";
312         $oDB->query($sSQL);
313
314         $fDuration = time() - $fStartTime;
315         echo date('Y-m-d H:i:s')." Completed all for $sBatchEnd in ".round($fDuration/60,2)." minutes\n";
316         if (!$aResult['import-osmosis-all']) exit(0);
317
318         if (CONST_Replication_Update_Interval > 60) {
319             $iSleep = max(0,(strtotime($sBatchEnd)+CONST_Replication_Update_Interval-time()));
320         } else {
321             $iSleep = max(0,CONST_Replication_Update_Interval-$fDuration);
322         }
323         echo date('Y-m-d H:i:s')." Sleeping $iSleep seconds\n";
324         sleep($iSleep);
325     }
326 }
327
328 function getosmosistimestamp($sOsmosisConfigDirectory)
329 {
330     $sStateFile = file_get_contents($sOsmosisConfigDirectory.'/state.txt');
331     preg_match('#timestamp=(.+)#', $sStateFile, $aResult);
332     return str_replace('\:',':',$aResult[1]);
333 }