]> git.openstreetmap.org Git - nominatim.git/blob - utils/export.php
initial version of an export script
[nominatim.git] / utils / export.php
1 #!/usr/bin/php -Cq
2 <?php
3         # Script to extract structured city and street data
4         # from a running nominatim instance as CSV data
5
6
7         require_once(dirname(dirname(__FILE__)).'/lib/init-cmd.php');
8         ini_set('memory_limit', '800M');
9
10         $aCMDOptions = array(
11                 "Export addresses as CSV file from a Nominatim database",
12                 array('help', 'h', 0, 1, 0, 0, false, 'Show Help'),
13                 array('quiet', 'q', 0, 1, 0, 0, 'bool', 'Quiet output'),
14                 array('verbose', 'v', 0, 1, 0, 0, 'bool', 'Verbose output'),
15
16         array('output-type', '', 0, 1, 1, 1, 'str', 'Type of places to output (see below)'),
17         array('output-format', '', 0, 1, 1, 1, 'str', 'Column mapping (see below)'),
18         array('output-all-postcodes', '', 0, 1, 0, 0, 'bool', 'List all postcodes for address instead of just the most likely one'),
19         array('language', '', 0, 1, 1, 1, 'str', 'Preferred language for output (local name, if omitted)'),
20         array('restrict-to-country', '', 0, 1, 1, 1, 'str', 'Export only objects within country (country code)'),
21         array('restrict-to-osm-node', '', 0, 1, 1, 1, 'int', 'Export only objects that are children of this OSM node'),
22         array('restrict-to-osm-way', '', 0, 1, 1, 1, 'int', 'Export only objects that are children of this OSM way'),
23         array('restrict-to-osm-relation', '', 0, 1, 1, 1, 'int', 'Export only objects that are children of this OSM relation'),
24         "\nAddress ranks: continent, country, state, county, city, suburb, street, path",
25         "Additional output types: postcode, placeid (placeid for each object)",
26         "\noutput-format must be a semicolon-separated list of address ranks. Multiple ranks",
27         "can be merged into one column by simply using a comma-separated list.",
28         "\nDefault output-type: street",
29         "Default output format: street;suburb;city;county;state;country"
30     );
31         getCmdOpt($_SERVER['argv'], $aCMDOptions, $aCMDResult, true, true);
32
33     $aRankmap = array( 'continent' => 1,
34                   'country' => 4,
35                   'state' => 8,
36                   'county' => 12,
37                   'city' => 16,
38                   'suburb' => 20,
39                   'street' => 26,
40                   'path' => 27
41             );
42
43     $oDB =& getDB();
44
45     if (isset($aCMDResult['output-type']))
46     {
47         if (!isset($aRankmap[$aCMDResult['output-type']])) fail('unknown output-type: '.$aCMDResult['output-type']);
48         $iOutputRank = $aRankmap[$aCMDResult['output-type']];
49     }
50     else
51     {
52         $iOutputRank = $aRankmap['street'];
53     }
54
55
56     // Preferred language
57     if (!isset($aCMDResult['language'])) $aCMDResult['language'] = 'xx';
58     $aLangPrefOrder = getPreferredLanguages($aCMDResult['language']);
59     $sLanguagePrefArraySQL = "ARRAY[".join(',',array_map("getDBQuoted",$aLangPrefOrder))."]";
60
61     // output formatting: build up a lookup table that maps address ranks to columns
62     $aColumnMapping = array();
63     $iNumCol = 0;
64     If (!isset($aCMDResult['output-format'])) $aCMDResult['output-format'] = 'street;suburb;city;county;state;country';
65     foreach (preg_split('/\s*;\s*/',$aCMDResult['output-format']) as $sColumn)
66     {
67         $bHasData = false;
68         foreach (preg_split('/\s*,\s*/', $sColumn) as $sRank)
69         {
70             if ($sRank == 'postcode' || $sRank == 'placeid')
71             {
72                 $aColumnMapping[$sRank] = $iNumCol;
73                 $bHasData = true;
74             }
75             elseif (isset($aRankmap[$sRank]))
76             {
77                 $iRank = $aRankmap[$sRank];
78                 if ($iRank <= $iOutputRank) {
79                     $aColumnMapping[(string)$iRank] = $iNumCol;
80                     $bHasData = true;
81                 }
82             }
83         }
84         if ($bHasData) $iNumCol++;
85     }
86
87     // build the query for objects
88     $sPlacexSQL = 'select min(place_id) as place_id, ';
89     $sPlacexSQL .= 'array_agg(place_id) as place_ids, ';
90     $sPlacexSQL .= 'calculated_country_code as cc, ';
91     // get the address places excluding postcodes
92     $sPlacexSQL .= 'array(select address_place_id from place_addressline a where a.place_id = placex.place_id and isaddress and address_place_id != placex.place_id and not cached_rank_address in (5,11) and cached_rank_address > 2 order by cached_rank_address) as address';
93     $sPlacexSQL .= " from placex where name is not null and linked_place_id is null";
94
95     $sPlacexSQL .= ' and rank_address = '.$iOutputRank;
96
97     if (isset($aCMDResult['restrict-to-country']))
98     {
99         $sPlacexSQL .= ' and calculated_country_code = '.getDBQuoted($aCMDResult['restrict-to-country']);
100     }
101
102     // restriction to parent place id
103     $sParentId = false;
104     $sOsmType = false;
105
106     if (isset($aCMDResult['restrict-to-osm-node']))
107     {
108         $sOsmType = 'N';
109         $sOsmId = $aCMDResult['restrict-to-osm-node'];
110     }
111     if (isset($aCMDResult['restrict-to-osm-way']))
112     {
113         $sOsmType = 'W';
114         $sOsmId = $aCMDResult['restrict-to-osm-way'];
115     }
116     if (isset($aCMDResult['restrict-to-osm-relation']))
117     {
118         $sOsmType = 'R';
119         $sOsmId = $aCMDResult['restrict-to-osm-relation'];
120     }
121     if ($sOsmType)
122     {
123         $sSQL = 'select place_id from placex where';
124         $sSQL .= ' osm_type = '.getDBQuoted($sOsmType);
125         $sSQL .= ' and osm_id = '.$sOsmId;
126         $sParentId = $oDB->getOne($sSQL);
127         if (PEAR::isError($sParentId)) fail(pg_last_error($oDB->connection));
128         if (!$sParentId) fail('Could not find place '.$sOsmType.' '.$sOsmId);
129     }
130     if ($sParentId)
131     {
132         $sPlacexSQL .= ' and place_id in (select place_id from place_addressline where address_place_id = '.$sParentId.' and isaddress)';
133     }
134
135     $sPlacexSQL .= " group by name->'name', address, calculated_country_code";
136
137         # Iterate over placeids
138         # to get further hierarchical information
139     //var_dump($sPlacexSQL);
140     $aRes =& $oDB->query($sPlacexSQL);
141     if (PEAR::isError($aRes)) fail(pg_last_error($oDB->connection));
142     $fOutstream = fopen("php://output", 'w');
143         while ($aRes->fetchInto($aRow))
144         {
145         //var_dump($aRow);
146                 $iPlaceID = $aRow['place_id'];
147                 $sSQL = "select rank_address,get_name_by_language(name,$sLanguagePrefArraySQL) as localname from get_addressdata($iPlaceID)";
148                 $sSQL .= " WHERE isaddress";
149                 $sSQL .= " order by rank_address desc,isaddress desc";
150             $aAddressLines = $oDB->getAll($sSQL);
151         if (PEAR::IsError($aAddressLines)) fail(pg_last_error($oDB->connection));
152
153
154         $aOutput = array_fill(0, $iNumCol, '');
155         # output address parts
156         foreach ($aAddressLines as $aAddress)
157         {
158             if (isset($aColumnMapping[$aAddress['rank_address']]))
159             {
160                 $aOutput[$aColumnMapping[$aAddress['rank_address']]] = $aAddress['localname'];
161             }
162         }
163         # output postcode
164         if (isset($aColumnMapping['postcode']))
165         {
166             if ($aCMDResult['output-all-postcodes'])
167             {
168                 $sSQL = "select array_agg(px.postcode) from placex px join place_addressline pa ";
169             }
170             else
171             {
172                 $sSQL = "select px.postcode from placex px join place_addressline pa ";
173             }
174             $sSQL .= "on px.place_id = pa.address_place_id ";
175             $sSQL .= "where pa.cached_rank_address in (5,11) ";
176             $sSQL .= "and pa.place_id in (select place_id from place_addressline where address_place_id in (".substr($aRow['place_ids'], 1, -1).")) ";
177             $sSQL .= "group by postcode order by count(*) desc limit 1";
178             $sRes = $oDB->getOne($sSQL);
179             if (PEAR::IsError($sRes)) fail(pg_last_error($oDB->connection));
180             if ($aCMDResult['output-all-postcodes'])
181             {
182                 $aOutput[$aColumnMapping['postcode']] = substr($sRes, 1, -1);
183             }
184             else
185             {
186                 $aOutput[$aColumnMapping['postcode']] = $sRes;
187             }
188         }
189         if (isset($aColumnMapping['placeid']))
190         {
191             $aOutput[$aColumnMapping['placeid']] = substr($aRow['place_ids'], 1, -1);
192         }
193         fputcsv($fOutstream, $aOutput);
194
195     }
196     fclose($fOutstream);