2ac806f9d5256a81814c1bdbedbf995cdb0f0daf
[blender-addons-contrib.git] / io_scene_open_street_map.py
1 # ***** BEGIN GPL LICENSE BLOCK *****
2 #
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16 #
17 # ***** END GPL LICENCE BLOCK *****
18
19 # <pep8 compliant>
20
21 bl_info = {
22     "name": "Open Street Map (.osm)",
23     "author": "Michael Anthrax Schlachter",
24     "version": (0, 1),
25     "blender": (2, 6, 3),
26     "location": "File > Import",
27     "description": "Load Open Street Map File",
28     "wiki_url": "",
29     "tracker_url": "",
30     "category": "Import-Export"}
31
32 # originally written for blender 2.4x by (manthrax _at_ hotmail.com),
33 # updated by for blender 2.6x by ideasman42
34 # If you use it for something cool, send me an email and let me know!
35
36 import bpy
37 from mathutils import Vector, Matrix
38
39
40 def parseBranch(nodes, bm, nmap, scale=100.0):
41     tidx = 0
42     inNode = 0
43     dlat = dlong = clat = clong = minlat = maxlat = minlong = maxlong = 0.0
44     for node in nodes:
45         if node.localName == "bounds":
46             if node.hasAttributes():
47                 for i in range(node.attributes.length):
48                     at = node.attributes.item(i)
49                     if at.name == "minlat":
50                         minlat = float(at.nodeValue)
51                     elif at.name == "minlon":
52                         minlong = float(at.nodeValue)
53                     elif at.name == "maxlat":
54                         maxlat = float(at.nodeValue)
55                     elif at.name == "maxlon":
56                         maxlong = float(at.nodeValue)
57                 dlat = maxlat - minlat
58                 dlong = maxlong - minlong
59                 clat = (maxlat + minlat) * 0.5
60                 clong = (maxlong + minlong) * 0.5
61
62                 print(dlat, dlong, clat, clong)
63
64         if node.localName == "way":
65             nid = None
66             refs = []
67             '''
68             if node.hasAttributes():
69                 for i in range(node.attributes.length):
70                     at=node.attributes.item(i)
71                     print(at.name)
72             '''
73
74             for ch in node.childNodes:
75                 if ch.localName == "nd":
76                     for i in range(ch.attributes.length):
77                         at = ch.attributes.item(i)
78                         #print(at.name)
79                         if at.name == "ref":
80                             refs.append(int(at.nodeValue))
81
82             first = 1
83             for r in refs:
84                 if first == 0:
85                     edge = bm.edges.get((nmap[pr], nmap[r]))
86                     if edge is None:
87                         edge = bm.edges.new((nmap[pr], nmap[r]))
88                     del edge  # don't actually use it
89                 else:
90                     first = 0
91                 pr = r
92
93         if node.localName == "node":
94             if node.hasAttributes():
95                 nid = None
96                 nlong = None
97                 nlat = None
98                 logged = 0
99                 for i in range(node.attributes.length):
100                     at = node.attributes.item(i)
101                     #print(at.name)
102                     if at.name == "id":
103                         nid = at.nodeValue
104                     elif at.name == "lon":
105                         nlong = at.nodeValue
106                     elif at.name == "lat":
107                         nlat = at.nodeValue
108
109                     if (nid is not None) and (nlat is not None) and (nlong is not None):
110                         fla = (float(nlat) - clat) * scale / dlat
111                         flo = (float(nlong) - clong) * scale / dlat
112                         vert = bm.verts.new((fla, flo, 0.0))
113                         nmap[int(nid)] = vert
114                         logged = 1
115                         break
116         tidx += 1
117         #if tidx > 1000:
118         #    break
119         tidx += parseBranch(node.childNodes, bm, nmap)
120
121     return tidx
122
123
124 def read(context, filepath, scale=100.0):
125     import bmesh
126     from xml.dom import minidom
127
128     xmldoc = minidom.parse(filepath)
129
130     print("Starting parse: %r..." % filepath)
131     bm = bmesh.new()
132
133     nmap = {}
134     tidx = parseBranch(xmldoc.childNodes, bm, nmap)
135
136     # create mesh
137     name = bpy.path.display_name_from_filepath(filepath)
138     me = bpy.data.meshes.new(name)
139     bm.to_mesh(me)
140     obj = bpy.data.objects.new(name, me)
141
142     # scale by 1.5 is odd, need to look into that
143     global_matrix = Matrix(((+0.0, +1.0, +0.0, +0.0),
144                             (+1.5, -0.0, +0.0, +0.0),
145                             (+0.0, -0.0, -1.0, +0.0),
146                             (+0.0, +0.0, +0.0, +1.0)))
147     me.transform(global_matrix)
148
149     # create the object in the scene
150     scene = context.scene
151     scene.objects.link(obj)
152     scene.objects.active = obj
153     obj.select = True
154
155     print("Parse done... %d" % tidx)
156
157     return {'FINISHED'}
158
159 ## for testing
160 #if __name__ == "__main__":
161 #    read("/data/downloads/osm_parser/map.osm", bpy.context)
162
163
164 # ----------------------------------------------------------------------------
165 # blender integration
166
167 from bpy.types import Operator
168 from bpy_extras.io_utils import ImportHelper
169
170 from bpy.props import StringProperty, FloatProperty
171
172
173 class ImportOSM(Operator, ImportHelper):
174     '''Import OSM'''
175     bl_idname = "import.open_street_map"
176     bl_label = "Import OpenStreetMap (.osm)"
177
178     # ExportHelper mixin class uses this
179     filename_ext = ".osm"
180
181     filter_glob = StringProperty(
182             default="*.osm",
183             options={'HIDDEN'},
184             )
185
186     # List of operator properties, the attributes will be assigned
187     # to the class instance from the operator settings before calling.
188     scale = FloatProperty(
189             name="Scale",
190             default=100.0,
191             )
192
193     def execute(self, context):
194         return read(context, self.filepath, self.scale)
195
196
197 # Only needed if you want to add into a dynamic menu
198 def menu_func_export(self, context):
199     self.layout.operator(ImportOSM.bl_idname)
200
201
202 def register():
203     bpy.utils.register_class(ImportOSM)
204     bpy.types.INFO_MT_file_import.append(menu_func_export)
205
206
207 def unregister():
208     bpy.utils.unregister_class(ImportOSM)
209     bpy.types.INFO_MT_file_import.remove(menu_func_export)