3 ###########################################################################
5 ## Copyrights Etienne Chové <chove@crans.org> 2009 ##
7 ## This program is free software: you can redistribute it and/or modify ##
8 ## it under the terms of the GNU General Public License as published by ##
9 ## the Free Software Foundation, either version 3 of the License, or ##
10 ## (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, see <http://www.gnu.org/licenses/>. ##
20 ###########################################################################
22 ## HomePage : http://wiki.openstreetmap.org/wiki/PythonOsmApi
24 ###########################################################################
26 ###########################################################################
27 ## 0.2.7 2009-10-09 implement all missing fonctions except ##
28 ## ChangesetsGet and GetCapabilities ##
29 ## 0.2.6 2009-10-09 encoding clean-up ##
30 ## 0.2.5 2009-10-09 implements NodesGet, WaysGet, RelationsGet ##
31 ## ParseOsm, ParseOsc ##
32 ## 0.2.4 2009-10-06 clean-up ##
33 ## 0.2.3 2009-09-09 keep http connection alive for multiple request ##
34 ## (Node|Way|Relation)Get return None when object ##
35 ## have been deleted (raising error before) ##
36 ## 0.2.2 2009-07-13 can identify applications built on top of the lib ##
37 ## 0.2.1 2009-05-05 some changes in constructor -- chove@crans.org ##
38 ## 0.2 2009-05-01 initial import ##
39 ###########################################################################
43 import httplib
, base64
, xml
.dom
.minidom
, time
45 ###########################################################################
50 def __init__(self
, username
= None, password
= None, passwordfile
= None, appid
= "", created_by
= "PythonOsmApi/"+__version__
, api
= "www.openstreetmap.org"):
54 self
._username
= username
56 self
._username
= open(passwordfile
).readline().split(":")[0].strip()
60 self
._password
= password
62 for l
in open(passwordfile
).readlines():
63 l
= l
.strip().split(":")
64 if l
[0] == self
._username
:
72 self
._created
_by
= created_by
74 self
._created
_by
= appid
+ " (" + created_by
+ ")"
77 self
._CurrentChangesetId
= -1
80 self
._conn
= httplib
.HTTPConnection(self
._api
, 80)
82 #######################################################################
84 #######################################################################
86 def Capabilities(self
):
89 #######################################################################
91 #######################################################################
93 def NodeGet(self
, NodeId
, NodeVersion
= -1):
94 """ Returns NodeData for node #NodeId. """
95 uri
= "/api/0.6/node/"+str(NodeId
)
96 if NodeVersion
<> -1: uri
+= "/"+str(NodeVersion
)
98 if not data
: return data
99 data
= xml
.dom
.minidom
.parseString(data
)
100 data
= data
.getElementsByTagName("osm")[0].getElementsByTagName("node")[0]
101 return self
._DomParseNode
(data
)
103 def NodeUpdate(self
, NodeData
):
104 """ Updates node with NodeData. Returns updated NodeData (without timestamp). """
105 if self
._CurrentChangesetId
== -1:
106 raise Exception, "No changeset currently opened"
107 NodeData
[u
"changeset"] = self
._CurrentChangesetId
108 result
= self
._put
("/api/0.6/node/"+str(NodeData
[u
"id"]), self
._XmlBuild
("node", NodeData
))
109 NodeData
[u
"version"] = int(result
.strip())
110 if u
"timestamp" in NodeData
: NodeData
.pop(u
"timestamp")
113 def NodeDelete(self
, NodeData
):
114 """ Delete node with NodeData. Returns updated NodeData (without timestamp). """
115 if self
._CurrentChangesetId
== -1:
116 raise Exception, "No changeset currently opened"
117 NodeData
[u
"changeset"] = self
._CurrentChangesetId
118 result
= self
._delete
("/api/0.6/node/"+str(NodeData
[u
"id"]), self
._XmlBuild
("node", NodeData
))
119 NodeData
[u
"version"] = int(result
.strip())
120 NodeData
[u
"visible"] = False
121 if u
"timestamp" in NodeData
: NodeData
.pop(u
"timestamp")
124 def NodeCreate(self
, NodeData
):
125 """ Creates a node. Returns updated NodeData (without timestamp). """
126 if self
._CurrentChangesetId
== -1:
127 raise Exception, "No changeset currently opened"
128 if NodeData
.get(u
"id", -1) > 0:
129 raise Exception, "This node already exists"
130 NodeData
[u
"changeset"] = self
._CurrentChangesetId
131 result
= self
._put
("/api/0.6/node/create", self
._XmlBuild
("node", NodeData
))
132 NodeData
[u
"id"] = int(result
.strip())
133 NodeData
[u
"version"] = 1
134 if u
"timestamp" in NodeData
: NodeData
.pop(u
"timestamp")
137 def NodeHistory(self
, NodeId
):
138 """ Returns dict(NodeVerrsion: NodeData). """
139 uri
= "/api/0.6/node/"+str(NodeId
)+"/history"
140 data
= self
._get
(uri
)
141 data
= xml
.dom
.minidom
.parseString(data
)
143 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("node"):
144 data
= self
._DomParseNode
(data
)
145 result
[data
[u
"version"]] = data
148 def NodeWays(self
, NodeId
):
149 """ Returns [WayData, ... ] containing node #NodeId. """
150 uri
= "/api/0.6/node/%d/ways"%NodeId
151 data
= self
._get
(uri
)
152 data
= xml
.dom
.minidom
.parseString(data
)
154 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("way"):
155 data
= self
._DomParseRelation
(data
)
159 def NodeRelations(self
, NodeId
):
160 """ Returns [RelationData, ... ] containing node #NodeId. """
161 uri
= "/api/0.6/node/%d/relations"%NodeId
162 data
= self
._get
(uri
)
163 data
= xml
.dom
.minidom
.parseString(data
)
165 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("relation"):
166 data
= self
._DomParseRelation
(data
)
170 def NodesGet(self
, NodeIdList
):
171 """ Returns dict(NodeId: NodeData) for each node in NodeIdList """
172 uri
= "/api/0.6/nodes?nodes=" + ",".join([str(x
) for x
in NodeIdList
])
173 data
= self
._get
(uri
)
174 data
= xml
.dom
.minidom
.parseString(data
)
176 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("node"):
177 data
= self
._DomParseNode
(data
)
178 result
[data
[u
"id"]] = data
181 #######################################################################
183 #######################################################################
185 def WayGet(self
, WayId
, WayVersion
= -1):
186 """ Returns WayData for way #WayId. """
187 uri
= "/api/0.6/way/"+str(WayId
)
188 if WayVersion
<> -1: uri
+= "/"+str(WayVersion
)
189 data
= self
._get
(uri
)
190 if not data
: return data
191 data
= xml
.dom
.minidom
.parseString(data
)
192 data
= data
.getElementsByTagName("osm")[0].getElementsByTagName("way")[0]
193 return self
._DomParseWay
(data
)
195 def WayUpdate(self
, WayData
):
196 """ Updates way with WayData. Returns updated WayData (without timestamp). """
197 if self
._CurrentChangesetId
== -1:
198 raise Exception, "No changeset currently opened"
199 WayData
[u
"changeset"] = self
._CurrentChangesetId
200 result
= self
._put
("/api/0.6/way/"+str(WayData
[u
"id"]), self
._XmlBuild
("way", WayData
))
201 WayData
[u
"version"] = int(result
.strip())
202 if u
"timestamp" in WayData
: WayData
.pop(u
"timestamp")
205 def WayDelete(self
, WayData
):
206 """ Delete way with WayData. Returns updated WayData (without timestamp). """
207 if self
._CurrentChangesetId
== -1:
208 raise Exception, "No changeset currently opened"
209 WayData
[u
"changeset"] = self
._CurrentChangesetId
210 result
= self
._delete
("/api/0.6/way/"+str(WayData
[u
"id"]), self
._XmlBuild
("way", WayData
))
211 WayData
[u
"version"] = int(result
.strip())
212 WayData
[u
"visible"] = False
213 if u
"timestamp" in WayData
: WayData
.pop(u
"timestamp")
216 def WayCreate(self
, WayData
):
217 """ Creates a way. Returns updated WayData (without timestamp). """
218 if self
._CurrentChangesetId
== -1:
219 raise Exception, "No changeset currently opened"
220 if NodeData
.get(u
"id", -1) > 0:
221 raise Exception, "This way already exists"
222 WayData
[u
"changeset"] = self
._CurrentChangesetId
223 result
= self
._put
("/api/0.6/way/create", self
._XmlBuild
("way", WayData
))
224 WayData
[u
"id"] = int(result
.strip())
225 WayData
[u
"version"] = 1
226 if u
"timestamp" in WayData
: WayData
.pop(u
"timestamp")
229 def WayHistory(self
, WayId
):
230 """ Returns dict(WayVerrsion: WayData). """
231 uri
= "/api/0.6/way/"+str(WayId
)+"/history"
232 data
= self
._get
(uri
)
233 data
= xml
.dom
.minidom
.parseString(data
)
235 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("way"):
236 data
= self
._DomParseWay
(data
)
237 result
[data
[u
"version"]] = data
240 def WayRelations(self
, WayId
):
241 """ Returns [RelationData, ...] containing way #WayId. """
242 uri
= "/api/0.6/way/%d/relations"%WayId
243 data
= self
._get
(uri
)
244 data
= xml
.dom
.minidom
.parseString(data
)
246 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("relation"):
247 data
= self
._DomParseRelation
(data
)
251 def WayFull(self
, WayId
):
252 """ Return full data for way WayId as list of {type: node|way|relation, data: {}}. """
253 uri
= "/api/0.6/way/"+str(WayId
)+"/full"
254 data
= self
._get
(uri
)
255 return self
.ParseOsm(data
)
257 def WaysGet(self
, WayIdList
):
258 """ Returns dict(WayId: WayData) for each way in WayIdList """
259 uri
= "/api/0.6/ways?ways=" + ",".join([str(x
) for x
in WayIdList
])
260 data
= self
._get
(uri
)
261 data
= xml
.dom
.minidom
.parseString(data
)
263 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("way"):
264 data
= self
._DomParseWay
(data
)
265 result
[data
[u
"id"]] = data
268 #######################################################################
270 #######################################################################
272 def RelationGet(self
, RelationId
, RelationVersion
= -1):
273 """ Returns RelationData for relation #RelationId. """
274 uri
= "/api/0.6/relation/"+str(RelationId
)
275 if RelationVersion
<> -1: uri
+= "/"+str(RelationVersion
)
276 data
= self
._get
(uri
)
277 if not data
: return data
278 data
= xml
.dom
.minidom
.parseString(data
)
279 data
= data
.getElementsByTagName("osm")[0].getElementsByTagName("relation")[0]
280 return self
._DomParseRelation
(data
)
282 def RelationUpdate(self
, RelationData
):
283 """ Updates relation with RelationData. Returns updated RelationData (without timestamp). """
284 if self
._CurrentChangesetId
== -1:
285 raise Exception, "No changeset currently opened"
286 RelationData
[u
"changeset"] = self
._CurrentChangesetId
287 result
= self
._put
("/api/0.6/relation/"+str(RelationData
[u
"id"]), self
._XmlBuild
("relation", RelationData
))
288 RelationData
[u
"version"] = int(result
.strip())
289 if u
"timestamp" in RelationData
: RelationData
.pop(u
"timestamp")
292 def RelationDelete(self
, RelationData
):
293 """ Delete relation with RelationData. Returns updated RelationData (without timestamp). """
294 if self
._CurrentChangesetId
== -1:
295 raise Exception, "No changeset currently opened"
296 RelationData
[u
"changeset"] = self
._CurrentChangesetId
297 result
= self
._delete
("/api/0.6/relation/"+str(RelationData
[u
"id"]), self
._XmlBuild
("relation", RelationData
))
298 RelationData
[u
"version"] = int(result
.strip())
299 RelationData
[u
"visible"] = False
300 if u
"timestamp" in RelationData
: RelationData
.pop(u
"timestamp")
303 def RelationCreate(self
, RelationData
):
304 """ Creates a relation. Returns updated RelationData (without timestamp). """
305 if self
._CurrentChangesetId
== -1:
306 raise Exception, "No changeset currently opened"
307 if NodeData
.get(u
"id", -1) > 0:
308 raise Exception, "This relation already exists"
309 RelationData
[u
"changeset"] = self
._CurrentChangesetId
310 result
= self
._put
("/api/0.6/relation/create", self
._XmlBuild
("relation", RelationData
))
311 RelationData
[u
"id"] = int(result
.strip())
312 RelationData
[u
"version"] = 1
313 if u
"timestamp" in RelationData
: RelationData
.pop(u
"timestamp")
316 def RelationHistory(self
, RelationId
):
317 """ Returns dict(RelationVerrsion: RelationData). """
318 uri
= "/api/0.6/relation/"+str(RelationId
)+"/history"
319 data
= self
._get
(uri
)
320 data
= xml
.dom
.minidom
.parseString(data
)
322 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("relation"):
323 data
= self
._DomParseRelation
(data
)
324 result
[data
[u
"version"]] = data
327 def RelationRelations(self
, RelationId
):
328 """ Returns list of RelationData containing relation #RelationId. """
329 uri
= "/api/0.6/relation/%d/relations"%RelationId
330 data
= self
._get
(uri
)
331 data
= xml
.dom
.minidom
.parseString(data
)
333 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("relation"):
334 data
= self
._DomParseRelation
(data
)
338 def RelationFull(self
, RelationId
):
339 """ Return full data for way WayId as list of {type: node|way|relation, data: {}}. """
340 uri
= "/api/0.6/relation/"+str(RelationId
)+"/full"
341 data
= self
._get
(uri
)
342 return self
.ParseOsm(data
)
344 def RelationsGet(self
, RelationIdList
):
345 """ Returns dict(RelationId: RelationData) for each relation in RelationIdList """
346 uri
= "/api/0.6/relations?relations=" + ",".join([str(x
) for x
in RelationIdList
])
347 data
= self
._get
(uri
)
348 data
= xml
.dom
.minidom
.parseString(data
)
350 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("relation"):
351 data
= self
._DomParseRelation
(data
)
352 result
[data
[u
"id"]] = data
355 #######################################################################
357 #######################################################################
359 def ChangesetGet(self
, ChangesetId
):
360 """ Returns ChangesetData for changeset #ChangesetId. """
361 data
= self
._get
("/api/0.6/changeset/"+str(ChangesetId
))
362 data
= xml
.dom
.minidom
.parseString(data
)
363 data
= data
.getElementsByTagName("osm")[0].getElementsByTagName("changeset")[0]
364 return self
._DomParseChangeset
(data
)
366 def ChangesetUpdate(self
, ChangesetTags
= {}):
367 """ Updates current changeset with ChangesetTags. """
368 if self
._CurrentChangesetId
== -1:
369 raise Exception, "No changeset currently opened"
370 if u
"created_by" not in ChangesetTags
:
371 ChangesetTags
[u
"created_by"] = self
._created
_by
372 result
= self
._put
("/api/0.6/changeset/"+str(self
._CurrentChangesetId
), self
._XmlBuild
("changeset", {u
"tag": ChangesetTags
}))
373 return self
._CurrentChangesetId
375 def ChangesetCreate(self
, ChangesetTags
= {}):
376 """ Opens a changeset. Returns #ChangesetId. """
377 if self
._CurrentChangesetId
<> -1:
378 raise Exception, "Changeset alreadey opened"
379 if u
"created_by" not in ChangesetTags
:
380 ChangesetTags
[u
"created_by"] = self
._created
_by
381 result
= self
._put
("/api/0.6/changeset/create", self
._XmlBuild
("changeset", {u
"tag": ChangesetTags
}))
382 self
._CurrentChangesetId
= int(result
)
383 self
._CurrentChangesetTags
= ChangesetTags
384 self
._CurrentChangesetCpt
= 0
385 return self
._CurrentChangesetId
387 def ChangesetClose(self
):
388 """ Closes current changeset. Returns #ChangesetId. """
389 if self
._CurrentChangesetId
== -1:
390 raise Exception, "No changeset currently opened"
391 result
= self
._put
("/api/0.6/changeset/"+str(self
._CurrentChangesetId
)+"/close", u
"")
392 CurrentChangesetId
= self
._CurrentChangesetId
393 self
._CurrentChangesetId
= -1
394 return CurrentChangesetId
396 def ChangesetUpload(self
):
399 def ChangesetDownload(self
, ChangesetId
):
400 """ Download data from a changeset. Returns list of dict {type: node|way|relation, action: create|delete|modify, data: {}}. """
401 uri
= "/api/0.6/changeset/"+str(ChangesetId
)+"/download"
402 data
= self
._get
(uri
)
403 return self
.ParseOsc(data
)
405 def ChangesetsGet(self
, min_lon
=None, min_lat
=None, max_lon
=None, max_lat
=None, userid
=None, closed_after
=None, created_before
=None, only_open
=False, only_closed
=False):
406 """ Returns dict(ChangsetId: ChangesetData) matching all criteria. """
409 #######################################################################
411 #######################################################################
413 def Map(self
, min_lon
, min_lat
, max_lon
, max_lat
):
414 """ Download data in bounding box. Returns list of dict {type: node|way|relation, data: {}}. """
415 uri
= "/api/0.6/map?bbox=%f,%f,%f,%f"%(min_lon
, min_lat
, max_lon
, max_lat
)
416 data
= self
._get
(uri
)
417 return self
.ParseOsm(data
)
419 #######################################################################
421 #######################################################################
423 def ParseOsm(self
, data
):
424 """ Parse osm data. Returns list of dict {type: node|way|relation, data: {}}. """
425 data
= xml
.dom
.minidom
.parseString(data
)
426 data
= data
.getElementsByTagName("osm")[0]
428 for elem
in data
.childNodes
:
429 if elem
.nodeName
== u
"node":
430 result
.append({u
"type": elem
.nodeName
, u
"data": self
._DomParseNode
(elem
)})
431 elif elem
.nodeName
== u
"way":
432 result
.append({u
"type": elem
.nodeName
, u
"data": self
._DomParseWay
(elem
)})
433 elif elem
.nodeName
== u
"relation":
434 result
.append({u
"type": elem
.nodeName
, u
"data": self
._DomParseRelation
(elem
)})
437 def ParseOsc(self
, data
):
438 """ Parse osc data. Returns list of dict {type: node|way|relation, action: create|delete|modify, data: {}}. """
439 data
= xml
.dom
.minidom
.parseString(data
)
440 data
= data
.getElementsByTagName("osmChange")[0]
442 for action
in data
.childNodes
:
443 if action
.nodeName
== u
"#text": continue
444 for elem
in action
.childNodes
:
445 if elem
.nodeName
== u
"node":
446 result
.append({u
"action":action
.nodeName
, u
"type": elem
.nodeName
, u
"data": self
._DomParseNode
(elem
)})
447 elif elem
.nodeName
== u
"way":
448 result
.append({u
"action":action
.nodeName
, u
"type": elem
.nodeName
, u
"data": self
._DomParseWay
(elem
)})
449 elif elem
.nodeName
== u
"relation":
450 result
.append({u
"action":action
.nodeName
, u
"type": elem
.nodeName
, u
"data": self
._DomParseRelation
(elem
)})
453 #######################################################################
454 # Internal http function #
455 #######################################################################
457 def _http_request(self
, cmd
, path
, auth
, send
):
458 self
._conn
.putrequest(cmd
, path
)
459 self
._conn
.putheader('User-Agent', self
._created
_by
)
461 self
._conn
.putheader('Authorization', 'Basic ' + base64
.encodestring(self
._username
+ ':' + self
._password
).strip())
463 self
._conn
.putheader('Content-Length', len(send
))
464 self
._conn
.endheaders()
466 self
._conn
.send(send
)
467 response
= self
._conn
.getresponse()
468 if response
.status
<> 200:
470 if response
.status
== 410:
472 raise Exception, "API returns unexpected status code "+str(response
.status
)+" ("+response
.reason
+")"
473 return response
.read()
475 def _http(self
, cmd
, path
, auth
, send
):
480 return self
._http
_request
(cmd
, path
, auth
, send
)
483 if i
<> 1: time
.sleep(2)
484 self
._conn
= httplib
.HTTPConnection(self
._api
, 80)
486 def _get(self
, path
):
487 return self
._http
('GET', path
, False, None)
489 def _put(self
, path
, data
):
490 return self
._http
('PUT', path
, True, data
)
492 def _delete(self
, path
, data
):
493 return self
._http
('DELETE', path
, True, data
)
495 #######################################################################
496 # Internal dom function #
497 #######################################################################
499 def _DomGetAttributes(self
, DomElement
):
500 """ Returns a formated dictionnary of attributes of a DomElement. """
502 for k
, v
in DomElement
.attributes
.items():
503 if k
== u
"uid" : v
= int(v
)
504 elif k
== u
"changeset" : v
= int(v
)
505 elif k
== u
"version" : v
= int(v
)
506 elif k
== u
"id" : v
= int(v
)
507 elif k
== u
"lat" : v
= float(v
)
508 elif k
== u
"lon" : v
= float(v
)
509 elif k
== u
"open" : v
= v
=="true"
510 elif k
== u
"visible" : v
= v
=="true"
511 elif k
== u
"ref" : v
= int(v
)
515 def _DomGetTag(self
, DomElement
):
516 """ Returns the dictionnary of tags of a DomElement. """
518 for t
in DomElement
.getElementsByTagName("tag"):
519 k
= t
.attributes
["k"].value
520 v
= t
.attributes
["v"].value
524 def _DomGetNd(self
, DomElement
):
525 """ Returns the list of nodes of a DomElement. """
527 for t
in DomElement
.getElementsByTagName("nd"):
528 result
.append(int(int(t
.attributes
["ref"].value
)))
531 def _DomGetMember(self
, DomElement
):
532 """ Returns a list of relation members. """
534 for m
in DomElement
.getElementsByTagName("member"):
535 result
.append(self
._DomGetAttributes
(m
))
538 def _DomParseNode(self
, DomElement
):
539 """ Returns NodeData for the node. """
540 result
= self
._DomGetAttributes
(DomElement
)
541 result
[u
"tag"] = self
._DomGetTag
(DomElement
)
544 def _DomParseWay(self
, DomElement
):
545 """ Returns WayData for the way. """
546 result
= self
._DomGetAttributes
(DomElement
)
547 result
[u
"tag"] = self
._DomGetTag
(DomElement
)
548 result
[u
"nd"] = self
._DomGetNd
(DomElement
)
551 def _DomParseRelation(self
, DomElement
):
552 """ Returns RelationData for the relation. """
553 result
= self
._DomGetAttributes
(DomElement
)
554 result
[u
"tag"] = self
._DomGetTag
(DomElement
)
555 result
[u
"member"] = self
._DomGetMember
(DomElement
)
558 def _DomParseChangeset(self
, DomElement
):
559 """ Returns ChangesetData for the changeset. """
560 result
= self
._DomGetAttributes
(DomElement
)
561 result
[u
"tag"] = self
._DomGetTag
(DomElement
)
564 #######################################################################
565 # Internal xml builder #
566 #######################################################################
568 def _XmlBuild(self
, ElementType
, ElementData
):
571 xml
+= u
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
572 xml
+= u
"<osm version=\"0.6\" generator=\"" + self
._created
_by
+ "\">\n"
574 # <element attr="val">
575 xml
+= u
" <" + ElementType
576 if u
"id" in ElementData
:
577 xml
+= u
" id=\"" + str(ElementData
[u
"id"]) + u
"\""
578 if u
"lat" in ElementData
:
579 xml
+= u
" lat=\"" + str(ElementData
[u
"lat"]) + u
"\""
580 if u
"lon" in ElementData
:
581 xml
+= u
" lon=\"" + str(ElementData
[u
"lon"]) + u
"\""
582 if u
"version" in ElementData
:
583 xml
+= u
" version=\"" + str(ElementData
[u
"version"]) + u
"\""
584 xml
+= u
" visible=\"" + str(ElementData
.get(u
"visible", True)).lower() + u
"\""
585 if ElementType
in [u
"node", u
"way", u
"relation"]:
586 xml
+= u
" changeset=\"" + str(self
._CurrentChangesetId
) + u
"\""
590 for k
, v
in ElementData
.get(u
"tag", {}).items():
591 xml
+= u
" <tag k=\""+self
._XmlEncode
(k
)+u
"\" v=\""+self
._XmlEncode
(v
)+u
"\"/>\n"
594 for member
in ElementData
.get(u
"member", []):
595 xml
+= u
" <member type=\""+member
[u
"type"]+"\" ref=\""+str(member
[u
"ref"])+u
"\" role=\""+self
._XmlEncode
(member
[u
"role"])+"\"/>\n"
598 for ref
in ElementData
.get(u
"nd", []):
599 xml
+= u
" <nd ref=\""+str(ref
)+u
"\"/>\n"
602 xml
+= u
" </" + ElementType
+ u
">\n"
606 return xml
.encode("utf8")
608 def _XmlEncode(self
, text
):
609 return text
.replace("&", "&").replace("\"", """)
611 ## End of main class ##
612 ###########################################################################