Add a facility to ignore sections of the file and ignore [IMG ID] ...
[ump2osm.git] / txt2osm.py
blobc4264f62a080c17888a5b04e72ee575de24cc70f
1 #!/usr/bin/env python2
2 # vim: set fileencoding=utf-8 encoding=utf-8 et :
3 # txt2osm, an UnofficialMapProject .txt to OpenStreetMap .osm converter.
4 # Copyright (C) 2008 Mariusz Adamski, rhn
5 # Copyright (C) 2009 Andrzej Zaborowski
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License
9 # as published by the Free Software Foundation; either version 2
10 # of the License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 # MA 02110-1301, USA.
22 import sys
23 import time
24 import math
25 from xml.sax import saxutils
27 __version__ = '0.1.1'
29 pline_types = {
30 0x1: [ "highway", "motorway" ],
31 0x2: [ "highway", "trunk" ],
32 0x3: [ "highway", "primary" ],
33 0x4: [ "highway", "secondary" ],
34 0x5: [ "highway", "tertiary" ],
35 0x6: [ "highway", "residential" ],
36 0x7: [ "highway", "living_street", "note", "FIXME: select one of: living_street, service, residential" ],
37 0x8: [ "highway", "primary_link" ],
38 0x9: [ "highway", "secondary_link" ],
39 0xa: [ "highway", "unclassified" ],
40 0xb: [ "highway", "trunk_link" ],
41 0xc: [ "junction", "roundabout" ],
42 0xd: [ "highway", "cycleway" ],
43 0xe: [ "highway", "service", "tunnel", "yes" ],
44 0x14: [ "railway", "rail" ],
45 0x16: [ "highway", "pedestrian" ],
46 0x18: [ "waterway", "stream" ],
47 0x19: [ "_rel", "restriction" ],
48 0x1a: [ "route", "ferry" ],
49 0x1b: [ "route", "ferry" ],
50 0x1c: [ "boundary", "administrative", "admin_level", "8" ],
51 0x1d: [ "boundary", "administrative", "admin_level", "4" ],
52 0x1e: [ "boundary", "administrative", "admin_level", "2" ],
53 0x1f: [ "waterway", "canal" ],
54 0x20: [ "barrier", "wall" ],
55 0x21: [ "barrier", "wall" ],
56 0x22: [ "barrier", "city_wall" ],
57 0x23: [ "highway", "track", "note", "fixme" ],
58 0x24: [ "highway", "road" ],
59 0x25: [ "barrier", "retaining_wall" ],
60 0x26: [ "waterway", "drain" ],
61 0x27: [ "aeroway", "runway" ],
62 0x28: [ "man_made", "pipeline" ],
63 0x29: [ "power", "line", "barrier", "retaining_wall",
64 "note", "fixme: choose one" ],
65 0x2a: [ "note", "fixme" ],
66 0x2c: [ "boundary", "historical", "admin_level", "2" ],
67 0x2f: [ "_rel", "lane_restriction" ],
68 0x44: [ "boundary", "administrative", "admin_level", "9" ],
69 0x4b: [ "note", "fixme" ],
71 0xe00: [ "highway", "footway", "ref", "Czerwony szlak",
72 "marked_trail_red", "yes" ],
73 0xe01: [ "highway", "footway", "ref", "Żółty szlak",
74 "marked_trail_yellow", "yes" ],
75 0xe02: [ "highway", "footway", "ref", "Zielony szlak",
76 "marked_trail_green", "yes" ],
77 0xe03: [ "highway", "footway", "ref", "Niebieski szlak",
78 "marked_trail_blue", "yes" ],
79 0xe04: [ "highway", "footway", "ref", "Czarny szlak",
80 "marked_trail_black", "yes" ],
81 0xe07: [ "highway", "footway", "ref", "Szlak", "note", "FIXME" ],
82 0xe08: [ "highway", "cycleway", "ref", "Czerwony szlak",
83 "marked_trail_red", "yes" ],
84 0xe09: [ "highway", "cycleway", "ref", "Żółty szlak",
85 "marked_trail_yellow", "yes" ],
86 0xe0a: [ "highway", "cycleway", "ref", "Zielony szlak",
87 "marked_trail_green", "yes" ],
88 0x1e0a:[ "highway", "cycleway", "ref", "Zielony szlak",
89 "marked_trail_green", "yes" ],
90 0xe0b: [ "highway", "cycleway", "ref", "Niebieski szlak",
91 "marked_trail_blue", "yes" ],
92 0xe0c: [ "highway", "cycleway", "ref", "Czarny szlak",
93 "marked_trail_black", "yes" ],
94 0xe0d: [ "highway", "cycleway", "ref", "Zielony szlak z liściem",
95 "marked_trail_green", "yes" ],
96 0xe0f: [ "highway", "cycleway", "ref", "Szlak", "note", "FIXME" ],
98 0xe10: [ "railway", "tram" ],
99 0xe11: [ "railway", "abandoned" ],
101 0xe12: [ "highway", "construction" ], # TODO
102 0xe13: [ "railway", "construction" ], # TODO
104 0x6701: [ "highway", "path" ],
105 0x6702: [ "highway", "track" ],
106 0x6707: [ "highway", "path", "ref", "Niebieski szlak", "bicycle", "yes",
107 "marked_trail_blue", "yes" ],
109 0x10e00: [ "highway", "path", "ref", "Czerwony szlak",
110 "marked_trail_red", "yes" ],
111 0x10e01: [ "highway", "path", "ref", "Żółty szlak",
112 "marked_trail_yellow", "yes" ],
113 0x10e02: [ "highway", "path", "ref", "Zielony szlak",
114 "marked_trail_green", "yes" ],
115 0x10e03: [ "highway", "path", "ref", "Niebieski szlak",
116 "marked_trail_blue", "yes" ],
117 0x10e04: [ "highway", "path", "ref", "Czarny szlak",
118 "marked_trail_black", "yes" ],
119 0x10e07: [ "highway", "path", "ref", "Szlak", "note", "FIXME" ],
120 0x10e08: [ "highway", "cycleway", "ref", "Czerwony szlak",
121 "marked_trail_red", "yes" ],
122 0x10e09: [ "highway", "cycleway", "ref", "Żółty szlak",
123 "marked_trail_yellow", "yes" ],
124 0x10e0a: [ "highway", "cycleway", "ref", "Zielony szlak",
125 "marked_trail_green", "yes" ],
126 0x10e0b: [ "highway", "cycleway", "ref", "Niebieski szlak",
127 "marked_trail_blue", "yes" ],
128 0x10e0c: [ "highway", "cycleway", "ref", "Czarny szlak",
129 "marked_trail_black", "yes" ],
130 0x10e0d: [ "highway", "cycleway", "ref", "Szlak",
131 "marked_trail_black", "yes", "note", "FIXME" ],
132 0x10e0f: [ "highway", "cycleway", "ref", "Szlak", "note", "FIXME" ],
134 0x10e10: [ "railway", "tram" ],
135 0x10e11: [ "railway", "abandoned" ],
137 0x10e12: [ "highway", "construction" ], # TODO
138 0x10e13: [ "railway", "construction" ], # TODO
140 shape_types = {
141 0x1: [ "landuse", "residential" ],
142 0x2: [ "landuse", "residential" ],
143 0x3: [ "highway", "pedestrian", "area", "yes" ],
144 0x4: [ "landuse", "military" ],
145 0x5: [ "amenity", "parking" ],
146 0x6: [ "amenity", "parking", "building", "garage" ],
147 0x7: [ "building", "terminal", "fixme", "Tag manually!" ],
148 0x8: [ "landuse", "retail", "building", "shops", "shop", "fixme",
149 "fixme", "Tag manually!" ],
150 0x9: [ "leisure", "marina" ], # a wild guess
151 0xa: [ "amenity", "school", "building", "hall" ],
152 0xb: [ "amenity", "hospital", "building", "hall" ],
153 0xc: [ "landuse", "industrial" ], # a wild guess
154 0xd: [ "landuse", "construction" ],
155 0xe: [ "aeroway", "aerodrome" ],
156 0x13: [ "building", "yes" ],
157 0x14: [ "natural", "wood" ], # sometimes landuse=military
158 0x15: [ "natural", "wood" ],
159 0x16: [ "natural", "wood" ],
160 0x17: [ "leisure", "park" ],
161 0x18: [ "leisure", "pitch", "sport", "tennis" ],
162 0x19: [ "leisure", "pitch" ], # or stadium...
163 0x1a: [ "landuse", "cemetery" ],
164 0x1e: [ "landuse", "forest", "leisure", "nature_reserve" ],
165 0x1f: [ "landuse", "forest", "leisure", "nature_reserve" ],
166 0x20: [ "tourism", "attraction" ], # a wild guess (forest?)
167 0x28: [ "natural", "coastline" ],
168 0x29: [ "natural", "water" ],
169 0x32: [ "natural", "coastline" ],
170 0x3b: [ "natural", "water" ], # how does this differ from 0x40?
171 0x3c: [ "natural", "water" ], # how does this differ from 0x40?
172 0x3d: [ "natural", "water" ], # how does this differ from 0x40?
173 0x3e: [ "natural", "water" ], # how does this differ from 0x40?
174 0x3f: [ "natural", "water" ], # how does this differ from 0x40?
175 0x40: [ "natural", "water" ],
176 0x41: [ "natural", "water", "amenity", "fountain" ],
177 0x42: [ "landuse", "reservoir" ], # how does this differ from 0x40?
178 0x43: [ "landuse", "reservoir" ], # how does this differ from 0x40?
179 0x44: [ "landuse", "reservoir" ], # how does this differ from 0x40?
180 0x45: [ "landuse", "reservoir" ], # how does this differ from 0x40?
181 0x46: [ "waterway", "riverbank" ],
182 0x47: [ "waterway", "riverbank" ], # how does this differ from 0x46?
183 0x48: [ "waterway", "riverbank" ], # how does this differ from 0x46?
184 0x49: [ "waterway", "riverbank" ], # how does this differ from 0x46?
185 0x4a: [ "highway", "residential", "oneway", "yes" ],
186 0x4c: [ "natural", "water" ], # how does this differ from 0x40?
187 0x4d: [ "natural", "glacier" ],
188 0x4e: [ "landuse", "allotments" ],
189 0x4f: [ "natural", "scrub" ],
190 0x50: [ "natural", "wood" ],
191 0x51: [ "natural", "wetland" ],
192 0x52: [ "leisure", "garden", "tourism", "zoo" ],
193 0x53: [ "landuse", "landfill" ],
195 0x2d0a: [ "leisure", "stadium" ],
197 poi_types = {
198 0x04: [ "place", "city" ],
199 0x05: [ "place", "city" ],
200 0x06: [ "place", "city" ],
201 0x07: [ "place", "city" ],
202 0x08: [ "place", "town" ],
203 0x08: [ "place", "town" ],
204 0x09: [ "place", "town" ],
205 0x0a: [ "place", "town" ],
206 0x0b: [ "place", "town" ],
207 0x0c: [ "place", "village" ],
208 0x0d: [ "place", "village" ],
209 0x0e: [ "place", "village" ],
210 0x2d: [ "amenity", "townhall" ],
212 0x0100: [ "place", "city" ], # Also used for voivodeships, regions
213 0x0200: [ "place", "city" ],
214 0x0300: [ "place", "city" ], # Also used for country nodes, seas
215 0x0400: [ "place", "city" ],
216 0x0500: [ "place", "city" ],
217 0x0600: [ "place", "city" ],
218 0x0700: [ "place", "city" ],
219 0x0800: [ "place", "city" ],
220 0x0900: [ "place", "city" ],
221 0x0a00: [ "place", "town" ],
222 0x0b00: [ "place", "town" ],
223 0x0c00: [ "place", "town" ],
224 0x0d00: [ "place", "town" ],
225 0x0e00: [ "place", "village" ],
226 0x0f00: [ "place", "village" ],
227 0x1000: [ "place", "village" ],
228 0x1100: [ "place", "village" ],
229 0x1150: [ "landuse", "construction" ],
230 0x1200: [ "bridge", "yes" ],
231 0x1500: [ "place", "locality" ],
232 0x1600: [ "man_made", "lighthouse" ],
233 0x1602: [ "note", "fixme" ],
234 0x1605: [ "amenity", "citymap_post", "tourism", "information" ],
235 0x1606: [ "man_made", "beacon", "mark_type", "buoy" ],
236 0x1607: [ "man_made", "beacon", "mark_type", "safe_water" ],
237 0x1608: [ "man_made", "beacon", "mark_type", "lateral_left" ],
238 0x1609: [ "man_made", "beacon", "mark_type", "lateral_right" ],
239 0x160a: [ "man_made", "beacon", "mark_type", "isolated_danger" ],
240 0x160b: [ "man_made", "beacon", "mark_type", "special" ],
241 0x160c: [ "man_made", "beacon", "mark_type", "cardinal" ],
242 0x160d: [ "man_made", "beacon", "mark_type", "other" ],
243 0x160e: [ "amenity", "signpost" ],
244 0x160f: [ "man_made", "beacon", "mark_type", "white" ],
245 0x1610: [ "man_made", "beacon", "mark_type", "red" ],
246 0x1611: [ "man_made", "beacon", "mark_type", "green" ],
247 0x1612: [ "man_made", "beacon", "mark_type", "yellow" ],
248 0x1613: [ "man_made", "beacon", "mark_type", "orange" ],
249 0x1614: [ "man_made", "beacon", "mark_type", "magenta" ],
250 0x1615: [ "man_made", "beacon", "mark_type", "blue" ],
251 0x1616: [ "man_made", "beacon", "mark_type", "multicolored" ],
252 0x1708: [ "shop", "fixme" ],
253 0x1709: [ "bridge", "yes" ],
254 0x170b: [ "shop", "verify!" ],
255 0x1710: [ "barrier", "gate" ],
256 0x17105:[ "highway", "stop" ],
257 0x1711: [ "note", "FIXME" ],
258 0x1712: [ "landuse", "construction" ],
259 0x170a: [ "note", "FIXME: verify" ],
260 0x170d: [ "note", "FIXME" ],
261 0x180c: [ "man_made", "beacon", "mark_type", "grounded-red" ], # TODO
262 0x180c: [ "man_made", "beacon", "mark_type", "grounded-green" ], # TODO
263 0x180c: [ "man_made", "beacon", "mark_type", "grounded-yellow" ], # TODO
264 0x180c: [ "man_made", "beacon", "mark_type", "cardinal-north" ],
265 0x190c: [ "man_made", "beacon", "mark_type", "cardinal-south" ],
266 0x1a0c: [ "man_made", "beacon", "mark_type", "cardinal-east" ],
267 0x1b0c: [ "man_made", "beacon", "mark_type", "cardinal-west" ],
268 0x190b: [ "highway", "construction" ],
269 0x1a0b: [ "man_made", "beacon" ],
270 0x1a10: [ "man_made", "beacon" ],
271 0x1b00: [ "note", "fixme" ],
272 0x1b02: [ "natural", "peak" ],
273 0x1b05: [ "amenity", "signpost" ],
274 0x1b0f: [ "aeroway", "taxiway" ],
275 0x1c00: [ "barrier", "obstruction" ],
276 0x1c01: [ "man_made", "ship_wreck" ],
277 0x1c07: [ "barrier", "obstruction", "visibility", "no" ],
278 0x1c09: [ "note", "fixme" ],
279 0x1e00: [ "place", "region" ],
280 0x1f00: [ "place", "region" ],
281 0x2000: [ "highway", "motorway_junction" ],
282 0x2100: [ "highway", "motorway_junction", "amenity", "parking" ],
283 0x2110: [ "highway", "motorway_junction", "amenity", "parking" ],
284 0x2200: [ "highway", "motorway_junction" ],
285 0x2400: [ "amenity", "weigh_station" ],
286 0x2500: [ "highway", "motorway_junction", "barrier", "toll_booth" ],
287 0x2600: [ "bridge", "yes" ],
288 0x2700: [ "highway", "motorway_junction" ],
289 0x2800: [ "place", "region" ], # Seems to be used villages though
290 0x2900: [ "landuse", "commercial" ],
291 0x2a: [ "amenity", "restaurant" ],
292 0x2a00: [ "amenity", "restaurant" ],
293 0x2a01: [ "amenity", "restaurant", "cuisine", "american" ],
294 0x2a02: [ "amenity", "restaurant", "cuisine", "asian" ],
295 0x2a025:[ "amenity", "restaurant", "cuisine", "sushi" ],
296 0x2a03: [ "amenity", "restaurant", "cuisine", "barbecue" ],
297 0x2a030:[ "amenity", "restaurant", "cuisine", "barbecue" ],
298 0x2a031:[ "amenity", "restaurant", "cuisine", "grill" ],
299 0x2a032:[ "amenity", "restaurant", "cuisine", "kebab" ],
300 0x2a04: [ "amenity", "restaurant", "cuisine", "chinese" ],
301 0x2a05: [ "shop", "bakery" ],
302 0x2a06: [ "amenity", "pub" ],
303 0x2a07: [ "amenity", "fast_food", "cuisine", "burger" ],
304 0x2a08: [ "amenity", "restaurant", "cuisine", "italian" ],
305 0x2a09: [ "amenity", "restaurant", "cuisine", "mexican" ],
306 0x2a0a: [ "amenity", "restaurant", "cuisine", "pizza" ],
307 0x2a0b: [ "amenity", "restaurant", "cuisine", "sea_food" ],
308 0x2a0c: [ "amenity", "restaurant", "cuisine", "grill" ],
309 0x2a0d: [ "amenity", "restaurant", "cuisine", "bagel" ], # bagle?
310 0x2a0e: [ "amenity", "cafe" ],
311 0x2a0f: [ "amenity", "restaurant", "cuisine", "french" ],
312 0x2a10: [ "amenity", "restaurant", "cuisine", "german" ],
313 0x2a11: [ "amenity", "restaurant", "cuisine", "british" ],
314 0x2a12: [ "amenity", "fast_food", "cuisine", "greek" ],
315 0x2a125:[ "amenity", "fast_food", "cuisine", "lebanese" ],
316 0x2a13: [ "amenity", "restaurant", "cuisine", "international" ],
317 0x2a14: [ "amenity", "restaurant", "cuisine", "regional" ],
318 0x2b00: [ "tourism", "hostel" ],
319 0x2b01: [ "tourism", "hotel" ],
320 0x2b015:[ "tourism", "motel" ],
321 0x2b02: [ "tourism", "hostel" ],
322 0x2b03: [ "tourism", "camp_site" ],
323 0x2b04: [ "tourism", "hotel" ],
324 0x2c00: [ "tourism", "attraction" ],
325 0x2c005:[ "tourism", "viewpoint" ],
326 0x2c01: [ "tourism", "attraction", "leisure", "park" ],
327 0x2c015:[ "leisure", "playground" ],
328 0x2c02: [ "tourism", "museum" ],
329 0x2c025:[ "tourism", "museum", "amenity", "arts_centre" ],
330 0x2c03: [ "amenity", "library" ],
331 0x2c04: [ "historic", "castle" ],
332 0x2c040:[ "historic", "castle", "castle_type", "dworek" ],
333 0x2c041:[ "historic", "castle", "castle_type", "palace" ],
334 0x2c042:[ "historic", "castle", "castle_type", "fortress" ],
335 0x2c043:[ "historic", "castle", "castle_type", "fortress" ],
336 0x2c05: [ "amenity", "school" ],
337 0x2c055:[ "amenity", "university" ],
338 0x2c056:[ "amenity", "kindergarten", "note", "Przedszkole" ],
339 0x2c057:[ "amenity", "kindergarten", "note", "Żłobek" ],
340 0x2c06: [ "leisure", "park" ],
341 0x2c07: [ "tourism", "zoo" ],
342 0x2c08: [ "leisure", "sports_centre" ],
343 0x2c080:[ "leisure", "pitch" ],
344 0x2c081:[ "leisure", "stadium" ],
345 0x2c09: [ "amenity", "theatre", "note", "concert_hall" ],
346 0x2c0a: [ "amenity", "restaurant", "cuisine", "wine_bar" ],
347 0x2c0b: [ "amenity", "place_of_worship" ],
348 0x2c0c: [ "natural", "spring", "amenity", "spa" ],
349 0x2d00: [ "leisure", "track" ],
350 0x2d01: [ "amenity", "theatre" ],
351 0x2d02: [ "amenity", "fast_food" ],
352 0x2d03: [ "amenity", "cinema" ],
353 0x2d04: [ "amenity", "nightclub" ],
354 0x2d045:[ "amenity", "casino" ],
355 0x2d05: [ "sport", "golf", "leisure", "golf_course" ],
356 0x2d06: [ "sport", "skiing" ],
357 0x2d07: [ "sport", "9pin" ],
358 0x2d08: [ "sport", "skating" ],
359 0x2d09: [ "sport", "swimming" ],
360 0x2d0a: [ "leisure", "stadium" ],
361 0x2d0a0:[ "leisure", "sports_centre", "sport", "fitness" ],
362 0x2d0a1:[ "leisure", "sports_centre", "sport", "tennis" ],
363 0x2d0a2:[ "leisure", "sports_centre", "sport", "skating" ],
364 0x2d0b: [ "sport", "sailing" ],
365 0x2e: [ "shop", "stationery" ],
366 0x2e00: [ "shop", "mall" ],
367 0x2e01: [ "shop", "department_store" ],
368 0x2e02: [ "shop", "grocery" ],
369 0x2e025:[ "amenity", "marketplace" ],
370 0x2e03: [ "shop", "supermarket" ],
371 0x2e04: [ "shop", "mall" ],
372 0x2e05: [ "amenity", "pharmacy" ],
373 0x2e06: [ "shop", "convenience" ],
374 0x2e07: [ "shop", "clothes" ],
375 0x2e08: [ "shop", "garden_centre" ],
376 0x2e09: [ "shop", "furniture" ],
377 0x2e0a: [ "shop", "outdoor" ],
378 0x2e0a5:[ "shop", "bicycle" ],
379 0x2e0b: [ "shop", "computer" ],
380 0x2e0c: [ "shop", "pets" ],
381 0x2f00: [ "amenity", "miscellaneous" ],
382 0x2f01: [ "amenity", "fuel" ],
383 0x2f02: [ "amenity", "car_rental" ],
384 0x2f03: [ "shop", "car_repair" ],
385 0x2f030:[ "shop", "car" ],
386 0x2f04: [ "aeroway", "aerodrome" ],
387 0x2f05: [ "amenity", "post_office" ],
388 0x2f050:[ "amenity", "post_office", "type", "courier" ],
389 0x2f051:[ "amenity", "post_office", "type", "courier", "operator", "dhl" ],
390 0x2f052:[ "amenity", "post_office", "type", "courier", "operator", "ups" ],
391 0x2f06: [ "amenity", "bank" ], # Also used with amenity=bureau_de_change
392 0x2f061:[ "amenity", "bank", "atm", "yes" ],
393 0x2f062:[ "amenity", "atm" ],
394 0x2f07: [ "shop", "car" ],
395 0x2f08: [ "amenity", "bus_station" ],
396 0x2f080:[ "highway", "bus_stop" ],
397 0x2f081:[ "railway", "tram_stop" ],
398 0x2f082:[ "railway", "station", "operator", "metro" ],
399 0x2f083:[ "highway", "bus_stop", "operator", "PKS" ],
400 0x2f084:[ "railway", "station", "operator", "PKP" ],
402 0x2f09: [ "waterway", "boatyard" ],
403 0x2f0a: [ "shop", "car_wrecker" ],
404 0x2f0b: [ "amenity", "parking" ],
405 0x2f0c: [ "amenity", "toilets" ],
406 0x2f0c5:[ "tourism", "information" ],
407 0x2f0d: [ "amenity", "automobile_club" ],
408 0x2f0e: [ "shop", "car_wash" ],
409 0x2f0f: [ "shop", "outdoor", "operator", "Garmin" ],
410 0x2f10: [ "amenity", "personal_service" ],
411 0x2f104:[ "amenity", "personal_service", "shop", "hairdresser" ],
412 0x2f105:[ "amenity", "personal_service", "shop", "tattoo" ],
413 0x2f106:[ "amenity", "personal_service", "shop", "optician" ],
414 0x2f11: [ "amenity", "public_building" ],
415 0x2f115:[ "landuse", "industrial", "amenity", "factory" ],
416 0x2f116:[ "landuse", "commercial" ],
417 0x2f12: [ "amenity", "wifi" ],
418 0x2f13: [ "shop", "bicycle" ],
419 0x2f14: [ "amenity", "public_building", ],
420 0x2f144:[ "amenity", "public_building", "type", "social" ],
421 0x2f145:[ "amenity", "personal_service", "shop", "laundry" ],
422 0x2f15: [ "amenity", "public_building" ],
423 0x2f16: [ "amenity", "parking", "truck_stop", "yes" ],
424 0x2f17: [ "amenity", "travel_agency" ],
425 0x3000: [ "amenity", "public_building" ],
426 0x3001: [ "amenity", "police" ],
427 0x3002: [ "amenity", "hospital" ],
428 0x30025:[ "amenity", "doctors" ],
429 0x30026:[ "amenity", "veterinary" ],
430 0x30027:[ "shop", "dentist" ],
431 0x3003: [ "amenity", "public_building" ],
432 0x3004: [ "amenity", "courthouse" ],
433 0x3005: [ "amenity", "nightclub" ],
434 0x3006: [ "amenity", "border_station" ],
435 0x3007: [ "amenity", "townhall" ],
436 0x3008: [ "amenity", "fire_station" ],
437 0x4000: [ "leisure", "golf_course" ],
438 0x4100: [ "landuse", "reservoir" ],
439 0x4200: [ "man_made", "ship" ],
440 0x4300: [ "leisure", "marina" ],
441 0x4400: [ "amenity", "fuel" ],
442 0x4500: [ "amenity", "restaurant" ],
443 0x4600: [ "amenity", "fast_food" ],
444 0x4800: [ "tourism", "camp_sire" ],
445 0x4900: [ "leisure", "park" ],
446 0x4700: [ "waterway", "dock" ],
447 0x4701: [ "waterway", "boat_ramp" ],
448 0x4a00: [ "tourism", "picnic_site" ],
449 0x4b00: [ "amenity", "hospital" ],
450 0x4c00: [ "tourism", "information" ],
451 0x4d00: [ "amenity", "parking" ],
452 0x4e00: [ "amenity", "toilets" ],
453 0x5700: [ "amenity", "police_car" ], # Fixme
454 0x5000: [ "amenity", "drinking_water" ],
455 0x5100: [ "amenity", "telephone" ],
456 0x5200: [ "tourism", "viewpoint" ],
457 0x5300: [ "sport", "skiing" ],
458 0x5400: [ "sport", "swimming" ],
459 0x5500: [ "waterway", "dam" ], # Map_Features requires a way
460 0x5600: [ "barrier", "gate" ],
461 0x56001:[ "danger", "photoradar", "type", "fake" ],
462 0x56002:[ "danger", "photoradar", "type", "portable" ],
463 0x56003:[ "danger", "photoradar", "type", "permanent" ],
464 0x56004:[ "danger", "police_control" ],
465 0x56005:[ "danger", "police_control;photoradar" ],
466 0x5700: [ "danger", "yes" ],
467 0x57000:[ "danger", "photoradar" ],
468 0x57001:[ "danger", "radar" ],
469 0x57002:[ "danger", "yes" ],
470 0x57003:[ "danger", "speed_limit" ],
471 0x57004:[ "danger", "level_crossing" ],
472 0x5800: [ "amenity", "prison" ],
473 0x5900: [ "aeroway", "aerodrome" ],
474 0x5901: [ "aeroway", "aerodrome" ],
475 0x5902: [ "aeroway", "aerodrome" ],
476 0x5903: [ "aeroway", "aerodrome" ],
477 0x5904: [ "aeroway", "helipad" ],
478 0x5905: [ "aeroway", "aerodrome" ],
479 0x593f: [ "aeroway", "aerodrome" ],
480 0x5a00: [ "amenity", "signpost" ],
481 0x5c00: [ "place", "hamlet" ],
482 0x5d00: [ "tourism", "information" ],
483 0x5f00: [ "natural", "scree" ],
484 0x6100: [ "amenity", "shelter", "building", "residential" ],
485 0x6101: [ "building", "industrial" ],
486 0x6200: [ "depth", "_name" ],
487 0x6300: [ "ele", "_name" ],
488 0x6400: [ "historic", "monument", "note", "FIXME" ],
489 0x6401: [ "bridge", "yes" ],
490 0x6402: [ "landuse", "industrial" ],
491 0x6403: [ "landuse", "cemetery" ],
492 0x6404: [ "amenity", "place_of_worship", "religion", "christian" ],
493 0x6405: [ "amenity", "public_building" ],
494 0x6406: [ "amenity", "ferry_terminal" ],
495 0x6407: [ "waterway", "dam" ], # Map_Features requires a way
496 0x6408: [ "amenity", "hospital" ],
497 0x6409: [ "man_made", "water_works" ], # random pick from Map_Features
498 0x640a: [ "amenity", "signpost" ],
499 0x640b: [ "landuse", "military" ],
500 0x640c: [ "landuse", "industrial", "amenity", "mine" ],
501 0x640d: [ "man_made", "works", "waterway", "oil_platform" ],
502 0x640e: [ "leisure", "park" ],
503 0x6410: [ "amenity", "school" ],
504 0x6411: [ "man_made", "tower" ],
505 0x64110:[ "man_made", "tower", "height", "short" ],
506 0x64111:[ "man_made", "tower", "height", "tall" ],
507 0x6412: [ "highway", "marked_trail", "note", "fixme" ],
508 0x6413: [ "tunnel", "yes", "layer", "-1" ],
509 0x64135:[ "natural", "cave_entrance" ],
510 0x6414: [ "amenity", "drinking_water" ],
511 0x6415: [ "historic", "ruins", "building", "fortress" ],
512 0x64155:[ "historic", "ruins", "building", "bunker" ],
513 0x6416: [ "tourism", "hotel" ],
514 0x6500: [ "waterway", "other" ],
515 0x6502: [ "highway", "ford" ],
516 0x6503: [ "natural", "bay" ],
517 0x6504: [ "natural", "water", "waterway", "bend" ],
518 0x6505: [ "waterway", "lock_gate" ],
519 0x6507: [ "natural", "spring", "man_made", "water_works" ],
520 0x6508: [ "waterway", "waterfall" ],
521 0x6509: [ "amenity", "fountain", "note", "fixme" ],
522 0x650a: [ "natural", "glacier" ],
523 0x650b: [ "waterway", "dock" ],
524 0x650c: [ "natural", "land" ], # Island as a POI
525 0x650d: [ "natural", "water" ], # Lake as a POI
526 0x650e: [ "natural", "spring" ], # geyser -> spring or volcano?
527 0x650f: [ "natural", "water" ], # Pond as a POI
528 0x650f5:[ "amenity", "toilets" ],
529 0x6511: [ "natural", "spring" ],
530 0x6512: [ "waterway", "stream" ],
531 0x6513: [ "natural", "water" ], # Swamp as a POI
532 0x6600: [ "place", "locality", "note", "fixme (kurhan?)" ],
533 0x6601: [ "barrier", "sally_port" ],
534 0x6602: [ "landuse", "commetcial" ],
535 0x6603: [ "natural", "bay" ],
536 0x6604: [ "natural", "beach" ],
537 0x6605: [ "lock", "yes" ],
538 0x6606: [ "place", "locality", "locality_type", "cape" ],
539 0x6607: [ "natural", "cliff" ], # Cliff as a POI
540 0x6608: [ "natural", "peak" ],
541 0x6609: [ "natural", "plain" ],
542 0x660a: [ "natural", "tree" ],
543 0x660b: [ "place", "locality", "note", "fixme" ],
544 0x660e: [ "natural", "volcano" ],
545 0x660f: [ "amenity", "signpost" ],
546 0x6610: [ "mountain_pass", "yes" ],
547 0x6611: [ "man_made", "tower" ],
548 0x6612: [ "amenity", "watersports_rental" ],
549 0x6613: [ "natural", "peak", "place", "region" ],
550 0x6614: [ "natural", "scree" ],
551 0x6615: [ "natural", "peak", "place", "locality", "note", "fixme", "sport", "ski" ],
552 0x6616: [ "natural", "peak" ],
553 0x6617: [ "place", "locality", "natural", "valley" ],
554 0x6618: [ "natural", "wood" ], # Wood as a POI
556 0x6701: [ "highway", "footway", "ref", "Zielony szlak",
557 "marked_trail_green", "yes" ],
558 0x6702: [ "highway", "footway", "ref", "Czerwony szlak",
559 "marked_trail_red", "yes" ],
560 0x6703: [ "highway", "footway", "ref", "Niebieski szlak",
561 "marked_trail_blue", "yes" ],
562 0x6704: [ "highway", "footway", "ref", "Żółty szlak",
563 "marked_trail_yellow", "yes" ],
564 0x6705: [ "highway", "footway", "ref", "Czarny szlak",
565 "marked_trail_black", "yes" ],
566 0x6707: [ "highway", "cycleway", "ref", "Żółty szlak",
567 "marked_trail_yellow", "yes" ],
568 0x6708: [ "highway", "cycleway", "ref", "Czerwony szlak",
569 "marked_trail_red", "yes" ],
570 0x6709: [ "highway", "cycleway", "ref", "Niebieski szlak",
571 "marked_trail_blue", "yes" ],
572 0x670a: [ "highway", "cycleway", "ref", "Zielony szlak",
573 "marked_trail_green", "yes" ],
574 0x670b: [ "highway", "cycleway", "ref", "Czarny szlak",
575 "marked_trail_black", "yes" ],
577 interp_types = { "o": "odd", "e": "even", "b": "all" }
578 levels = { 1: "residential", 2: "tertiary", 3: "secondary", 4: "trunk" }
579 maxspeeds = {
580 '0': '8', '1': '20', '2': '40', '3': '56',
581 '4': '72', '5': '93', '6': '108', '7': '128',
583 exceptions = [
584 'emergency', # Found nothing better in Map_Features
585 'goods',
586 'motorcar',
587 'psv',
588 'taxi', # Found nothing better in Map_Features
589 'foot',
590 'bicycle',
591 'hgv',
593 reftype = {
594 0x02: 'ref',
595 0x05: 'ref',
596 0x1d: 'loc_name', # Abbrevations
597 0x1f: 'ele',
598 0x2a: 'int_ref', # FIXME: should differentate the types
599 0x2b: 'int_ref',
600 0x2c: 'int_ref',
601 0x2d: 'ref',
602 0x2e: 'ref',
603 0x2f: 'ref',
606 class Mylist(object):
607 def __init__(self):
608 self.k = {}
609 self.v = [] #
610 def __len__(self):
611 return len(self.k)
612 def __getitem__(self, key):
613 return self.v[key] #
614 for k in self.k:
615 if self.k[k] == key:
616 return k;
617 def index(self, value):
618 return self.k[value]
619 def __setitem__(self, key, value): #
620 if key in v: #
621 del self.k[self.v[key]] #
622 self.k[value] = key #
623 self.v[key] = value #
624 def __contains__(self, value):
625 return value in self.k
626 def append(self, value):
627 self.v.append(value) #
628 self.k[value] = len(self.k)
629 def __iter__(self):
630 return self.v.__iter__() #
631 return self.k.__iter__()
632 # Lines with a # above can be removed to save half of the memory used
633 # (but some look-ups will be slower)
635 points = Mylist()
636 pointattrs = []
637 ways = []
638 relations = []
640 maxtypes = []
641 turnrestrictions = []
642 # FIXME: should use the timestamp of the cvs checkout from src/CVS
643 source = "http://ump.waw.pl/ retrieved " + time.strftime('%X %x')
644 srcidx = 0
646 borders = None
647 borders_resize = 1
649 class Features: # fake enum
650 poi, polyline, polygon, ignore = range(4)
652 class ParsingError(Exception):
653 pass
655 def index_to_nodeid(index):
656 return -index - 1
658 def index_to_wayid(index):
659 return index_to_nodeid(len(points) + index)
661 def index_to_relationid(index):
662 return index_to_wayid(len(ways) + index)
664 def xmlize(str):
665 return saxutils.escape(str, { '\'': ''' })
667 def print_point(point, index, argv):
668 """Prints a pair of coordinates and their ID as XML"""
669 if '_out' in pointattrs[index]:
670 return
671 head = ''.join(("<node id='", str(index_to_nodeid(index)),
672 "' visible='true' lat='", str(point[0]),
673 "' lon='", str(point[1]), "'>"))
674 print(head)
675 src = pointattrs[index].pop('_src')
676 for key in pointattrs[index]:
677 try:
678 print("\t<tag k='%s' v='%s' />" % \
679 (key, xmlize(pointattrs[index][key])))
680 except:
681 sys.stderr.write("converting key " + key + ": " +
682 str(pointattrs[index][key]) + " failed\n")
683 print("\t<tag k='source' v='%s (%s)' />" % (source, argv[src]))
684 print("</node>")
686 def print_way(way, index, argv):
687 """Prints a way given by way together with its ID to stdout as XML"""
688 if way.pop('_c') <= 0:
689 return
690 print("<way id='%d' visible='true'>" % index_to_wayid(index))
691 for nindex in way.pop('_nodes'):
692 print("\t<nd ref='%d' />" % index_to_nodeid(nindex))
694 src = way.pop('_src')
695 for key in way:
696 print("\t<tag k='%s' v='%s' />" % (key, xmlize(way[key])))
697 print("\t<tag k='source' v='%s (%s)' />" % (source, argv[src]))
698 print("</way>")
700 def print_relation(rel, index, argv):
701 """Prints a relation given by rel together with its ID to stdout as XML"""
702 if rel.pop('_c') <= 0:
703 return
704 if not rel.has_key("_members"):
705 sys.stderr.write( "warning: Unable to print relation not having memebers: %r\n" % (rel,) )
706 return
707 print("<relation id='%i' visible='true'>" % index_to_relationid(index))
708 for role, (type, members) in rel.pop('_members').items():
709 for member in members:
710 if type == "node":
711 id = index_to_nodeid(member)
712 elif type == "way":
713 id = index_to_wayid(member)
714 else:
715 id = index_to_relationid(member)
716 print("\t<member type='%s' ref='%i' role='%s' />" % \
717 (type, id, role))
719 src = rel.pop('_src')
720 for key in rel:
721 print("\t<tag k='%s' v='%s' />" % (key, xmlize(rel[key])))
722 print("\t<tag k='source' v='%s (%s)' />" % (source, argv[src]))
723 print("</relation>")
725 def points_append(node, attrs):
726 global borders
727 lat = float(node[0])
728 lon = float(node[1])
729 if borders_resize:
730 if borders is None:
731 borders = [ lat, lon, lat, lon ]
732 else:
733 if lat < borders[0]:
734 borders[0] = lat
735 if lon < borders[1]:
736 borders[1] = lon
737 if lat > borders[2]:
738 borders[2] = lat
739 if lon > borders[3]:
740 borders[3] = lon
741 elif borders is None or \
742 lat < borders[0] or lon < borders[1] or \
743 lat > borders[2] or lon > borders[3]:
744 attrs['_out'] = 1
746 attrs['_src'] = srcidx
747 points.append(node)
748 maxtypes.append(0x100)
749 pointattrs.append(attrs)
751 def prepare_line(nodes_str, closed):
752 """Appends new nodes to the points list"""
753 nodes = []
754 for element in nodes_str.split(','):
755 element = element.strip('()')
756 nodes.append(element)
758 lats = nodes[::2]
759 longs = nodes[1::2]
761 nodes = zip(lats, longs)
763 for node in nodes:
764 if node not in points:
765 points_append(node, {})
766 try:
767 node_indices = map(points.index, nodes)
768 except:
769 print(points)
770 print(node)
771 raise ParsingError('Can\'t map node indices')
772 pts = 0
773 for node in node_indices:
774 if '_out' not in pointattrs[node]:
775 pts += 1
776 if closed:
777 node_indices.append(node_indices[0])
778 return (pts, node_indices)
780 def tag(way, pairs):
781 for key, value in zip(pairs[::2], pairs[1::2]):
782 way[key] = value
784 def convert_tag(way, key, value, feat):
785 if key.lower() in [ 'label', 'lable', 'lablel' ]:
786 label = value
787 refpos = label.find("~[")
788 if refpos > -1:
789 try:
790 ## refstr, sep, right = label[refpos + 2:].partition(' ') # py_ver >= 2.5 version
791 label_split = label[refpos + 2:].split(' ',1) # above line in py_ver = 2.4
792 if len(label_split) == 2:
793 refstr,right = label[refpos + 2:].split(' ',1)
794 else:
795 refstr = label_split[0]
796 right = ""
798 code, ref = refstr.split(']')
799 label = (label[:refpos] + right).strip(' \t')
800 way[reftype[int(code, 0)]] = ref.replace("/", ";")
801 except:
802 if code.lower() == '0x06':
803 pass
804 elif code.lower() == '0x1b':
805 way['loc_name'] = right
806 elif code.lower() == '0x1c':
807 way['loc_name'] = ref
808 else:
809 raise ParsingError('Problem parsing label ' + value)
810 label = ref + label
811 if 'name' not in way and label != "":
812 way['name'] = label
813 elif key.lower() in [ 'label2', 'lanel2', 'lable2', 'level2', 'lbel2' ]:
814 way['name'] = value
815 elif key.lower() == 'label3':
816 way['loc_name'] = value
817 elif key == 'DirIndicator':
818 way['oneway'] = value
819 elif key in [ 'Data0', 'Data1', 'Data2', 'Data3', 'Data4' ]:
820 num = int(key[4:])
821 count, way['_nodes'] = prepare_line(value, feat == Features.polygon)
822 if '_c' in way:
823 way['_c'] += count
824 else:
825 way['_c'] = count
826 # way['layer'] = num ??
827 elif key.startswith('_Inner'):
828 count, nodes = prepare_line(value, feat == Features.polygon)
829 if '_innernodes' not in way:
830 way['_innernodes'] = []
831 if feat != Features.polygon:
832 way['_join'] = 1
833 way['_innernodes'].append(nodes)
834 if '_c' in way:
835 way['_c'] += count
836 else:
837 way['_c'] = count
838 elif key == 'Type':
839 if feat == Features.polygon:
840 tag(way, shape_types[int(value, 0)])
841 elif feat == Features.polyline:
842 tag(way, pline_types[int(value, 0)])
843 else:
844 tag(way, poi_types[int(value, 0)])
845 elif key in [ 'EndLevel', 'Level', 'Levles' ]:
846 # if 'highway' not in way:
847 # way['highway'] = levels[int(value, 0)]
848 # way['layer'] = str(value) ??
849 pass
850 elif key.lower() == 'miasto' or key.lower() == 'miato':
851 way['addr:city'] = value
852 elif key == 'StreetDesc':
853 way['addr:street'] = value
854 elif key == 'RouteParam':
855 params = value.split(',')
856 if params[0] != '0':
857 way['maxspeed'] = maxspeeds[params[0]] # Probably useless
858 if params[2] == '1':
859 way['oneway'] = 'yes'
860 if params[3] == '1':
861 way['toll'] = 'yes'
862 for i, val in enumerate(params[4:]):
863 if val == '1':
864 way[exceptions[i]] = 'no'
865 elif key == 'RestrParam':
866 params = value.split(',')
867 excpts = []
868 for i, val in enumerate(params[4:]):
869 if val == '1':
870 excpts.append(exceptions[i])
871 way['except'] = ','.join(excpts)
872 elif key == 'HLevel0':
873 if feat != Features.polyline:
874 raise ParsingError('HLevel0 used on a polygon')
875 curlevel = 0
876 curnode = 0
877 list = []
878 for level in value.split(')'):
879 if level == "":
880 break
881 pair = level.strip(', ()').split(',')
882 start = int(pair[0], 0)
883 level = int(pair[1], 0)
884 if start > curnode and level != curlevel:
885 list.append((curnode, start, curlevel))
886 curnode = start
887 curlevel = level
888 list.append((curnode, -1, curlevel))
889 way['_levels'] = list
890 elif key == 'Szlak':
891 ref = []
892 for colour in value.split(','):
893 if colour.lower() == 'zolty':
894 ref.append('Żółty szlak')
895 way['marked_trail_yellow'] = 'yes'
896 elif colour.lower() == 'zielony':
897 ref.append('Zielony szlak')
898 way['marked_trail_green'] = 'yes'
899 elif colour.lower() == 'czerwony':
900 ref.append('Czerwony szlak')
901 way['marked_trail_red'] = 'yes'
902 elif colour.lower() == 'niebieski':
903 ref.append('Niebieski szlak')
904 way['marked_trail_blue'] = 'yes'
905 else:
906 ref.append(colour)
907 sys.stderr.write("warning: Unknown 'Szlak' colour: " +
908 colour + "\n")
909 way['ref'] = ";".join(ref)
910 elif key.startswith('NumbersExt'):
911 sys.stderr.write("warning: " + key + " tag discarded\n")
912 elif key.startswith('Numbers'):
913 unused = int(key[7:], 0)
914 value = value.split(',')
915 if len(value) < 7:
916 raise ParsingError("Bad address info specification")
917 if '_addr' not in way:
918 way['_addr'] = {}
919 way['_addr'][int(value[0], 0)] = value[1:]
920 elif key.lower() == 'rampa':
921 way['bridge'] = 'yes'
922 elif key == 'Highway':
923 way['ref'] = value
924 elif key.startswith('Exit'):
925 way[key.lower()] = value
926 elif key == 'OvernightParking':
927 way['overnight_parking'] = 'yes'
928 elif key == 'Phone':
929 way['phone'] = value
930 elif key == 'HouseNumber':
931 way['addr:housenumber'] = value
932 elif key == 'KodPoczt':
933 way['addr:postcode'] = value
934 elif key == 'Time':
935 way['hour_on'] = value
936 elif key == 'ForceClass' or key == 'ForceSpeed':
937 fclass = int(value) # Routing helper
938 # Ignore it for the moment, seems to be used mainly for temporary setups
939 # such as detours.
940 elif key == 'Speed':
941 way['maxspeed'] = value
942 elif key == 'Height_m':
943 way['maxheight'] = value
944 elif key in [ 'Nod0', 'Nod1' ]:
945 # TODO: what does this do?
946 pass
947 else:
948 if key.lower() in [ 'levels', 'lavels', 'city', 'typ', 'plik' ]:
949 pass # Known typo
950 else:
951 raise ParsingError("Unknown key " + key + " in polyline / polygon")
953 # Mercator
954 def projlat(lat):
955 lat = math.radians(lat)
956 return math.degrees(math.log(math.tan(lat) + 1.0 / math.cos(lat)))
957 def projlon(lat, lon):
958 return lon
959 def unproj(lat, lon):
960 lat = math.radians(lat)
961 return (math.degrees(math.atan(math.sinh(lat))), lon)
962 #def unproj(lat, lon):
963 # return (lat, lon / math.cos(lat / 180.0 * math.pi))
964 #def projlat(lat):
965 # return lat
966 #def projlon(lat, lon):
967 # return lon * math.cos(lat / 180.0 * math.pi)
969 def add_addrinfo(nodes, addrs, street, city, right, count):
970 prev_house = "xx"
971 prev_node = None
972 attrs = { 'addr:street': street }
973 if city:
974 attrs['addr:city'] = city
975 for n, node in enumerate(nodes[:-1]):
976 if n in addrs and node != nodes[n + 1]:
977 type = addrs[n][right * 3 + 0].lower()
978 if type not in interp_types:
979 continue
980 type = interp_types[type]
981 low = addrs[n][right * 3 + 1]
982 hi = addrs[n][right * 3 + 2]
984 dist = 0.0002 # degrees
985 lat = projlat(float(points[node][0]))
986 lon = projlon(lat, float(points[node][1]))
987 nlat = projlat(float(points[nodes[n + 1]][0]))
988 nlon = projlon(nlat, float(points[nodes[n + 1]][1]))
989 dlen = math.hypot(nlat - lat, nlon - lon)
990 normlat = (nlat - lat) / dlen * dist
991 normlon = (nlon - lon) / dlen * dist
992 if right:
993 dlat = -normlon
994 dlon = normlat
995 else:
996 dlat = normlon
997 dlon = -normlat
998 if dlen > dist * 5:
999 shortlat = normlat * 2
1000 shortlon = normlon * 2
1001 elif dlen > dist * 3:
1002 shortlat = normlat
1003 shortlon = normlon
1004 else:
1005 shortlat = 0
1006 shortlon = 0
1008 if 0: #prev_house == low:
1009 low_node = prev_node
1010 elif low == hi:
1011 shortlat = (nlat - lat) / 2
1012 shortlon = (nlon - lon) / 2
1013 pt0 = 0
1014 else:
1015 pt0 = len(points)
1016 low_node = unproj(lat + dlat + shortlat, lon + dlon + shortlon)
1017 while low_node in points:
1018 low_node = (low_node[0] + normlat / 10,
1019 low_node[1] + normlon / 10)
1020 attrs['addr:housenumber'] = low
1021 points_append(low_node, attrs.copy())
1023 pt1 = len(points)
1024 hi_node = unproj(nlat + dlat - shortlat, nlon + dlon - shortlon)
1025 while hi_node in points:
1026 hi_node = (hi_node[0] - normlat / 10, hi_node[1] - normlon / 10)
1027 attrs['addr:housenumber'] = hi
1028 points_append(hi_node, attrs.copy())
1030 if len(addrs[n]) >= 8:
1031 if addrs[n][6] != "-1":
1032 pointattrs[pt0]['addr:postcode'] = addrs[n][6]
1033 if addrs[n][7] != "-1":
1034 pointattrs[pt1]['addr:postcode'] = addrs[n][7]
1035 if len(addrs[n]) >= 14:
1036 if addrs[n][8] != "-1":
1037 pointattrs[pt0]['addr:city'] = addrs[n][8]
1038 if addrs[n][9] != "-1":
1039 pointattrs[pt0]['addr:region'] = addrs[n][9]
1040 if addrs[n][10] != "-1":
1041 pointattrs[pt0]['addr:country'] = addrs[n][10]
1042 if addrs[n][11] != "-1":
1043 pointattrs[pt1]['addr:city'] = addrs[n][11]
1044 if addrs[n][12] != "-1":
1045 pointattrs[pt1]['addr:region'] = addrs[n][12]
1046 if addrs[n][13] != "-1":
1047 pointattrs[pt1]['addr:country'] = addrs[n][13]
1049 way = {
1050 '_nodes': [pt0, pt1],
1051 'addr:interpolation': type,
1052 '_c': count,
1053 '_src': srcidx,
1055 if low != hi:
1056 ways.append(way)
1058 prev_house = hi
1059 prev_node = hi_node
1060 else:
1061 prev_house = "xx"
1064 class NodesToWayNotFound(ValueError):
1066 Raised when way of two nodes can not be found
1068 def __init__(self,node_a,node_b):
1069 self.node_a = node_a
1070 self.node_b = node_b
1072 def __str__(self):
1073 return "<NodesToWayNotFound %r,%r>" % (self.node_a,self.node_b,)
1075 def nodes_to_way(a, b):
1076 for way in ways:
1077 ## print("DEBUG: way['_nodes']: %r" % (way['_nodes'],)
1078 if a in way['_nodes'] and b in way['_nodes']:
1079 # Hopefully there's only one
1080 return way
1082 raise NodesToWayNotFound(a, b)
1084 for way in ways:
1085 way_nodes = way['_nodes']
1086 if a in way_nodes:
1087 print("DEBUG: node a: %r found in way: %r" % (a, way))
1088 if b in way_nodes:
1089 print("DEBUG: node b: %r found in way: %r" % (b, way))
1091 ## print "DEBUG: no way nodes: a: %r b: %r" % (a, b)
1092 raise NodesToWayNotFound(a, b)
1094 def signbit(x):
1095 if x > 0:
1096 return 1
1097 if x < 0:
1098 return -1
1100 def next_node(pivot, dir):
1101 way = nodes_to_way(dir, pivot)['_nodes']
1102 pivotidx = way.index(pivot)
1103 return way[pivotidx + signbit(way.index(dir) - pivotidx)]
1105 def split_way(way, node):
1106 l = len(way['_nodes'])
1107 i = way['_nodes'].index(node)
1108 if i == 0 or i == l - 1:
1109 return
1110 newway = way.copy()
1111 ways.append(newway)
1112 newway['_nodes'] = way['_nodes'][:i + 1]
1113 way['_nodes'] = way['_nodes'][i:]
1115 def preprepare_restriction(rel):
1116 rel['_nodes'][0] = next_node(rel['_nodes'][1], rel['_nodes'][0])
1117 rel['_nodes'][-1] = next_node(rel['_nodes'][-2], rel['_nodes'][-1])
1119 def prepare_restriction(rel):
1120 fromnode = rel['_nodes'][0]
1121 fromvia = rel['_nodes'][1]
1122 tonode = rel['_nodes'][-1]
1123 tovia = rel['_nodes'][-2]
1124 split_way(nodes_to_way(fromnode, fromvia), fromvia)
1125 split_way(nodes_to_way(tonode, tovia), tovia)
1127 def make_restriction_fromviato(rel):
1128 nodes = rel.pop('_nodes')
1129 rel['_members'] = {
1130 'from': ('way', [ ways.index(nodes_to_way(nodes[0], nodes[1])) ]),
1131 'via': ('node', nodes[1:-1]),
1132 'to': ('way', [ ways.index(nodes_to_way(nodes[-2], nodes[-1])) ]),
1135 rel['_c'] = ways[rel['_members']['from'][1][0]]['_c'] + \
1136 ways[rel['_members']['to'][1][0]]['_c']
1137 if rel['_c'] > 0:
1138 ways[rel['_members']['from'][1][0]]['_c'] += 1
1139 ways[rel['_members']['to'][1][0]]['_c'] += 1
1141 return nodes
1143 def name_turn_restriction(rel, nodes):
1144 # Try to suss out the type of restriction.. needs to be checked manually
1145 if 'name' in rel and rel['name'].lower().find('nakaz') != -1:
1146 beginning = 'only_'
1147 else:
1148 beginning = 'no_'
1149 if 'name' in rel:
1150 if rel['name'].find('<<') != -1:
1151 rel['restriction'] = 'no_u_turn'
1152 elif rel['name'].find('<') != -1:
1153 rel['restriction'] = beginning + 'left_turn'
1154 elif rel['name'].find('>') != -1:
1155 rel['restriction'] = beginning + 'right_turn'
1157 # Multiple via nodes are not approved by OSM anyway
1158 if 'restriction' not in rel and len(nodes) == 3:
1159 # No projection needed
1160 alat = float(points[nodes[0]][0])
1161 alon = float(points[nodes[0]][1])
1162 blat = float(points[nodes[1]][0])
1163 blon = float(points[nodes[1]][1])
1164 clat = float(points[nodes[2]][0])
1165 clon = float(points[nodes[2]][1])
1166 # Vector cross product (?)
1167 angle = (blat - alat) * (clon - blon) - (blon - alon) * (clat - blat)
1169 if angle > 0.0:
1170 rel['restriction'] = beginning + 'right_turn'
1171 else:
1172 rel['restriction'] = beginning + 'no_left_turn'
1174 def make_multipolygon(outer, holes):
1175 rel = {
1176 'type': 'multipolygon',
1177 'note': 'FIXME: fix roles manually',
1178 '_c': outer['_c'],
1179 '_src': outer['_src'],
1180 '_members': {
1181 'outer': ('way', [ ways.index(outer) ]),
1182 'inner': ('way', []),
1186 for inner in holes:
1187 way = {
1188 '_c': outer['_c'],
1189 '_nodes': inner,
1190 '_src': outer['_src'],
1192 ways.append(way)
1193 rel['_members']['inner'][1].append(ways.index(way))
1194 polygon_make_ccw(way)
1196 # Assume that the polygon with most nodes is the outer shape and
1197 # all other polygons are the holes.
1198 # That's a stupid heuristic but is much simpler than a complete
1199 # check of which polygons lie entirely inside other polygons and
1200 # there might turn up some very complex cases like polygons crossing
1201 # one another and multiple nesting.
1202 if len(inner) > len(outer['_nodes']):
1203 tmp = outer['_nodes']
1204 outer['_nodes'] = inner
1205 way['_nodes'] = tmp
1206 way['_nodes'].reverse()
1208 return rel
1210 def polygon_make_ccw(shape):
1211 nodes = shape['_nodes']
1212 num = len(nodes) - 1
1213 if (num < 3):
1214 return
1216 angle = 0.0
1217 epsilon = 0.001
1218 for i in range(num):
1219 try:
1220 a = (i + 0)
1221 b = (i + 1)
1222 c = (i + 2) % num
1223 # No projection needed
1224 alat = float(points[nodes[a]][0])
1225 alon = float(points[nodes[a]][1])
1226 blat = float(points[nodes[b]][0])
1227 blon = float(points[nodes[b]][1])
1228 clat = float(points[nodes[c]][0])
1229 clon = float(points[nodes[c]][1])
1230 ablen = math.hypot(blat - alat, blon - alon)
1231 bclen = math.hypot(clat - blat, clon - blon)
1232 # Vector cross product (?)
1233 cross = (blat - alat) * (clon - blon) - (blon - alon) * (clat - blat)
1234 # Vector dot product (?)
1235 dot = (blat - alat) * (clat - blat) + (blon - alon) * (clon - blon)
1237 sine = cross / (ablen * bclen)
1238 cosine = dot / (ablen * bclen)
1239 angle += signbit(sine) * math.acos(cosine)
1240 except:
1241 pass
1242 angle = math.degrees(-angle)
1244 if angle > -360.0 - epsilon and angle < -360.0 + epsilon: # CW
1245 nodes.reverse()
1246 elif angle > 360.0 - epsilon and angle < 360.0 + epsilon: # CCW
1247 pass
1248 else:
1249 # Likely an illegal shape
1250 shape['fixme'] = "Weird shape"
1252 def recode(line):
1253 try:
1254 return unicode(line, "cp1250").encode("UTF-8")
1255 except:
1256 sys.stderr.write("warning: couldn't recode " + line + " in UTF-8!\n");
1257 return line
1259 def parse_txt(infile):
1260 polyline = None
1261 feat = None
1262 miasto = None
1263 comment = None
1264 for line in infile:
1265 line = line.strip()
1266 if line == "[POLYLINE]":
1267 polyline = {}
1268 feat = Features.polyline
1269 elif line == "[POLYGON]":
1270 polyline = {}
1271 feat = Features.polygon
1272 elif line == "[POI]":
1273 polyline = {}
1274 feat = Features.poi
1275 elif line == "[IMG ID]":
1276 feat = Features.ignore
1277 elif line == '[END]':
1278 way = { '_src': srcidx }
1279 for key in polyline:
1280 convert_tag(way, key, polyline[key], feat)
1281 if comment is not None:
1282 way['note'] = comment
1283 comment = None
1284 if 'Type' in polyline:
1285 highway = int(polyline['Type'], 0)
1286 for i in way['_nodes']:
1287 if maxtypes[i] > highway:
1288 maxtypes[i] = highway
1289 polyline = None
1291 if '_addr' in way:
1292 addrinfo = way.pop('_addr')
1293 try:
1294 street = way['name']
1295 except:
1296 street = 'fixme'
1297 add_addrinfo(way['_nodes'], addrinfo,
1298 street, miasto, 0, way['_c'])
1299 add_addrinfo(way['_nodes'], addrinfo,
1300 street, miasto, 1, way['_c'])
1301 if 'ele' in way and way['ele'] == '_name':
1302 way['ele'] = way.pop('name').replace(',', '.')
1303 if 'depth' in way and way['depth'] == '_name':
1304 way['depth'] = way.pop('name').replace(',', '.')
1305 if feat == Features.polygon:
1306 polygon_make_ccw(way)
1308 if feat == Features.poi:
1309 # execution order shouldn't matter here, unlike in C
1310 pointattrs[way.pop('_nodes')[0]] = way
1311 if not way.pop('_c'):
1312 way['_out'] = 1
1313 else:
1314 ways.append(way)
1315 elif feat == Features.ignore:
1316 # Ignore everything within e.g. [IMG ID] until some other
1317 # rule picks up something interesting, e.g. a polyline
1318 pass
1319 elif polyline is not None and line != '':
1320 try:
1321 key, value = line.split('=', 1)
1322 except:
1323 print(line)
1324 raise ParsingError('Can\'t split the thing')
1325 key = key.strip()
1326 if key in polyline:
1327 if key.startswith('Data'):
1328 key = "_Inner0"
1329 while key in polyline:
1330 key = "_Inner" + str(int(key[6:]) + 1)
1331 elif key == 'City' and polyline[key] == 'Y':
1332 pass
1333 else:
1334 raise ParsingError('Key ' + key + ' repeats')
1335 polyline[key] = recode(value).strip()
1336 elif line.startswith(';'):
1337 strn = recode(line[1:].strip(" \t\n"))
1338 if comment is not None:
1339 comment = comment + " " + strn
1340 else:
1341 comment = strn
1342 elif line.startswith('Miasto='):
1343 miasto = recode(line[7:].strip(" \t\n"))
1344 elif line != '':
1345 raise ParsingError('Unhandled line ' + line)
1347 def parse_pnt(infile):
1348 # raise ParsingError('No?')
1349 pass
1351 # Main program.
1353 if len(sys.argv) < 2 or sys.argv[1] == "--help":
1354 print("Usage: " + sys.argv[0] + " [files...]")
1355 print("Example:")
1356 print("\t ./txt2osm.py UMP-Warszawa/src/WOLOMIN*.txt -- UMP-Warszawa/src/*.txt > wolomin.osm")
1357 sys.exit()
1358 for n, f in enumerate(sys.argv[1:]):
1359 srcidx = n + 1
1360 if f == '--':
1361 borders_resize = 0
1362 continue
1363 if f in sys.argv[1:srcidx]:
1364 continue
1365 try:
1366 infile = open(f, "r")
1367 except IOError:
1368 sys.stderr.write("Can't open file " + f + "!\n")
1369 sys.exit()
1370 sys.stderr.write("Loading " + f + "\n")
1372 if f.endswith("pnt") or f.endswith("pnt.txt"):
1373 parse_pnt(infile)
1374 elif f.endswith("txt") or f.endswith("mp"):
1375 parse_txt(infile)
1376 infile.close()
1378 # Roundabouts:
1379 # Use the road class of the most important (lowest numbered) road
1380 # that meets the roundabout.
1381 for way in ways:
1382 if 'junction' in way and way['junction'] == 'roundabout':
1383 maxtype = 0x7 # service
1384 for i in way['_nodes']:
1385 if maxtypes[i] < maxtype:
1386 maxtype = maxtypes[i]
1387 tag(way, pline_types[maxtype])
1388 if 'oneway' in way:
1389 del way['oneway']
1390 # TODO make sure nodes are ordered counter-clockwise
1392 # Relations:
1393 # find them, remove from /ways/ and move to /relations/
1395 # For restriction relations: locate members and split member ways
1396 # at the "via" node as required by
1397 # http://wiki.openstreetmap.org/wiki/Relation:restriction
1398 relations = [rel for rel in ways if '_rel' in rel]
1399 for rel in relations:
1400 rel['type'] = rel.pop('_rel')
1401 ways.remove(rel)
1403 for rel in relations:
1404 if rel['type'] in [ 'restriction', 'lane_restriction' ]:
1405 try:
1406 preprepare_restriction(rel)
1407 ## print "DEBUG: preprepare_restriction(rel:%r) OK." % (rel,)
1408 except NodesToWayNotFound:
1409 sys.stderr.write("warning: Unable to find nodes to preprepare "
1410 "restriction from rel: %r\n" % (rel,))
1412 # Way level: split ways on level changes
1413 # TODO: possibly emit a relation to group the ways
1414 levelledways = [way for way in ways if '_levels' in way]
1415 for way in levelledways:
1416 ways.remove(way)
1417 if '_levels' in way:
1418 nodes = way['_nodes']
1419 levels = way.pop('_levels')
1420 for segment in levels:
1421 subway = way.copy()
1422 if segment[1] == -1:
1423 subway['_nodes'] = nodes[segment[0]:]
1424 else:
1425 subway['_nodes'] = nodes[segment[0]:segment[1] + 1]
1426 if segment[2] != 0:
1427 subway['layer'] = str(segment[2])
1428 if segment[2] > 0:
1429 subway['bridge'] = 'yes'
1430 else:
1431 subway['tunnel'] = 'yes'
1432 ways.append(subway)
1434 for way in ways:
1435 if '_innernodes' in way:
1436 if '_join' in way:
1437 del way['_join']
1438 for segment in way.pop('_innernodes'):
1439 subway = way.copy()
1440 subway['_nodes'] = segment
1441 ways.append(subway)
1442 else:
1443 relations.append(make_multipolygon(way, way.pop('_innernodes')))
1445 for rel in relations:
1446 if rel['type'] in [ 'restriction', 'lane_restriction' ]:
1447 try:
1448 prepare_restriction(rel)
1449 except NodesToWayNotFound:
1450 sys.stderr.write("warning: Unable to find nodes to " +
1451 "preprepare restriction from rel: %r\n" % (rel,))
1452 for rel in relations:
1453 if rel['type'] in [ 'restriction', 'lane_restriction' ]:
1454 try:
1455 rnodes = make_restriction_fromviato(rel)
1457 if rel['type'] == 'restriction':
1458 name_turn_restriction(rel, rnodes)
1459 except NodesToWayNotFound:
1460 sys.stderr.write("warning: Unable to find nodes to " +
1461 "preprepare restriction from rel: %r\n" % (rel,))
1463 # Quirks
1464 for way in ways:
1465 if 'highway' in way and way['highway'] == 'unclassified':
1466 if 'name' in way:
1467 way['highway'] = 'residential'
1468 way['surface'] = 'unpaved'
1469 else:
1470 way['highway'] = 'track'
1471 if 'note' not in way:
1472 way['note'] = 'FIXME: select one of: residential, unclassified, track'
1474 for index, point in enumerate(pointattrs):
1475 if 'shop' in point and point['shop'] == 'fixme':
1476 for way in ways:
1477 if index in way['_nodes'] and 'highway' in way:
1478 del point['shop']
1479 point['noexit'] = 'yes'
1480 break
1482 for way in ways:
1483 if way['_c'] > 0:
1484 for node in way['_nodes']:
1485 if '_out' in pointattrs[node]:
1486 del pointattrs[node]['_out']
1488 print("<?xml version='1.0' encoding='UTF-8'?>")
1489 print("<osm version='0.6' generator='txt2osm %s converter for UMP-PL'>" \
1490 % __version__)
1492 for index, point in enumerate(points):
1493 print_point(point, index, sys.argv)
1495 for index, way in enumerate(ways):
1496 print_way(way, index, sys.argv)
1498 for index, rel in enumerate(relations):
1499 print_relation(rel, index, sys.argv)
1501 print("</osm>")