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.10 2009-10-14 RelationFullRecur definition ##
28 ## 0.2.9 2009-10-13 automatic changeset management ##
29 ## ChangesetUpload implementation ##
30 ## 0.2.8 2009-10-13 *(Create|Update|Delete) use not unique _do method ##
31 ## 0.2.7 2009-10-09 implement all missing fonctions except ##
32 ## ChangesetsGet and GetCapabilities ##
33 ## 0.2.6 2009-10-09 encoding clean-up ##
34 ## 0.2.5 2009-10-09 implements NodesGet, WaysGet, RelationsGet ##
35 ## ParseOsm, ParseOsc ##
36 ## 0.2.4 2009-10-06 clean-up ##
37 ## 0.2.3 2009-09-09 keep http connection alive for multiple request ##
38 ## (Node|Way|Relation)Get return None when object ##
39 ## have been deleted (raising error before) ##
40 ## 0.2.2 2009-07-13 can identify applications built on top of the lib ##
41 ## 0.2.1 2009-05-05 some changes in constructor -- chove@crans.org ##
42 ## 0.2 2009-05-01 initial import ##
43 ###########################################################################
45 __version__
= '0.2.10'
47 import httplib
, base64
, xml
.dom
.minidom
, time
49 ###########################################################################
59 created_by
= "PythonOsmApi/"+__version__
,
60 api
= "www.openstreetmap.org",
61 changesetauto
= False,
62 changesetautotags
= {},
63 changesetautosize
= 500):
67 self
._username
= username
69 self
._username
= open(passwordfile
).readline().split(":")[0].strip()
73 self
._password
= password
75 for l
in open(passwordfile
).readlines():
76 l
= l
.strip().split(":")
77 if l
[0] == self
._username
:
80 # Changest informations
81 self
._changesetauto
= changesetauto
# auto create and close changesets
82 self
._changesetautotags
= changesetautotags
# tags for automatic created changesets
83 self
._changesetautosize
= changesetautosize
# change count for auto changeset
84 self
._changesetautodata
= [] # data to upload for auto group
91 self
._created
_by
= created_by
93 self
._created
_by
= appid
+ " (" + created_by
+ ")"
96 self
._CurrentChangesetId
= 0
99 self
._conn
= httplib
.HTTPConnection(self
._api
, 80)
102 if self
._changesetauto
:
103 self
._changesetautoflush
(True)
106 #######################################################################
108 #######################################################################
110 def Capabilities(self
):
113 #######################################################################
115 #######################################################################
117 def NodeGet(self
, NodeId
, NodeVersion
= -1):
118 """ Returns NodeData for node #NodeId. """
119 uri
= "/api/0.6/node/"+str(NodeId
)
120 if NodeVersion
<> -1: uri
+= "/"+str(NodeVersion
)
121 data
= self
._get
(uri
)
122 if not data
: return data
123 data
= xml
.dom
.minidom
.parseString(data
)
124 data
= data
.getElementsByTagName("osm")[0].getElementsByTagName("node")[0]
125 return self
._DomParseNode
(data
)
127 def NodeCreate(self
, NodeData
):
128 """ Creates a node. Returns updated NodeData (without timestamp). """
129 return self
._do
("create", "node", NodeData
)
131 def NodeUpdate(self
, NodeData
):
132 """ Updates node with NodeData. Returns updated NodeData (without timestamp). """
133 return self
._do
("update", "node", NodeData
)
135 def NodeDelete(self
, NodeData
):
136 """ Delete node with NodeData. Returns updated NodeData (without timestamp). """
137 return self
._do
("delete", "node", NodeData
)
139 def NodeHistory(self
, NodeId
):
140 """ Returns dict(NodeVerrsion: NodeData). """
141 uri
= "/api/0.6/node/"+str(NodeId
)+"/history"
142 data
= self
._get
(uri
)
143 data
= xml
.dom
.minidom
.parseString(data
)
145 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("node"):
146 data
= self
._DomParseNode
(data
)
147 result
[data
[u
"version"]] = data
150 def NodeWays(self
, NodeId
):
151 """ Returns [WayData, ... ] containing node #NodeId. """
152 uri
= "/api/0.6/node/%d/ways"%NodeId
153 data
= self
._get
(uri
)
154 data
= xml
.dom
.minidom
.parseString(data
)
156 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("way"):
157 data
= self
._DomParseRelation
(data
)
161 def NodeRelations(self
, NodeId
):
162 """ Returns [RelationData, ... ] containing node #NodeId. """
163 uri
= "/api/0.6/node/%d/relations"%NodeId
164 data
= self
._get
(uri
)
165 data
= xml
.dom
.minidom
.parseString(data
)
167 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("relation"):
168 data
= self
._DomParseRelation
(data
)
172 def NodesGet(self
, NodeIdList
):
173 """ Returns dict(NodeId: NodeData) for each node in NodeIdList """
174 uri
= "/api/0.6/nodes?nodes=" + ",".join([str(x
) for x
in NodeIdList
])
175 data
= self
._get
(uri
)
176 data
= xml
.dom
.minidom
.parseString(data
)
178 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("node"):
179 data
= self
._DomParseNode
(data
)
180 result
[data
[u
"id"]] = data
183 #######################################################################
185 #######################################################################
187 def WayGet(self
, WayId
, WayVersion
= -1):
188 """ Returns WayData for way #WayId. """
189 uri
= "/api/0.6/way/"+str(WayId
)
190 if WayVersion
<> -1: uri
+= "/"+str(WayVersion
)
191 data
= self
._get
(uri
)
192 if not data
: return data
193 data
= xml
.dom
.minidom
.parseString(data
)
194 data
= data
.getElementsByTagName("osm")[0].getElementsByTagName("way")[0]
195 return self
._DomParseWay
(data
)
197 def WayCreate(self
, WayData
):
198 """ Creates a way. Returns updated WayData (without timestamp). """
199 return self
._do
("create", "way", WayData
)
201 def WayUpdate(self
, WayData
):
202 """ Updates way with WayData. Returns updated WayData (without timestamp). """
203 return self
._do
("update", "way", WayData
)
205 def WayDelete(self
, WayData
):
206 """ Delete way with WayData. Returns updated WayData (without timestamp). """
207 return self
._do
("delete", "way", WayData
)
209 def WayHistory(self
, WayId
):
210 """ Returns dict(WayVerrsion: WayData). """
211 uri
= "/api/0.6/way/"+str(WayId
)+"/history"
212 data
= self
._get
(uri
)
213 data
= xml
.dom
.minidom
.parseString(data
)
215 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("way"):
216 data
= self
._DomParseWay
(data
)
217 result
[data
[u
"version"]] = data
220 def WayRelations(self
, WayId
):
221 """ Returns [RelationData, ...] containing way #WayId. """
222 uri
= "/api/0.6/way/%d/relations"%WayId
223 data
= self
._get
(uri
)
224 data
= xml
.dom
.minidom
.parseString(data
)
226 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("relation"):
227 data
= self
._DomParseRelation
(data
)
231 def WayFull(self
, WayId
):
232 """ Return full data for way WayId as list of {type: node|way|relation, data: {}}. """
233 uri
= "/api/0.6/way/"+str(WayId
)+"/full"
234 data
= self
._get
(uri
)
235 return self
.ParseOsm(data
)
237 def WaysGet(self
, WayIdList
):
238 """ Returns dict(WayId: WayData) for each way in WayIdList """
239 uri
= "/api/0.6/ways?ways=" + ",".join([str(x
) for x
in WayIdList
])
240 data
= self
._get
(uri
)
241 data
= xml
.dom
.minidom
.parseString(data
)
243 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("way"):
244 data
= self
._DomParseWay
(data
)
245 result
[data
[u
"id"]] = data
248 #######################################################################
250 #######################################################################
252 def RelationGet(self
, RelationId
, RelationVersion
= -1):
253 """ Returns RelationData for relation #RelationId. """
254 uri
= "/api/0.6/relation/"+str(RelationId
)
255 if RelationVersion
<> -1: uri
+= "/"+str(RelationVersion
)
256 data
= self
._get
(uri
)
257 if not data
: return data
258 data
= xml
.dom
.minidom
.parseString(data
)
259 data
= data
.getElementsByTagName("osm")[0].getElementsByTagName("relation")[0]
260 return self
._DomParseRelation
(data
)
262 def RelationCreate(self
, RelationData
):
263 """ Creates a relation. Returns updated RelationData (without timestamp). """
264 return self
._do
("create", "relation", RelationData
)
266 def RelationUpdate(self
, RelationData
):
267 """ Updates relation with RelationData. Returns updated RelationData (without timestamp). """
268 return self
._do
("update", "relation", RelationData
)
270 def RelationDelete(self
, RelationData
):
271 """ Delete relation with RelationData. Returns updated RelationData (without timestamp). """
272 return self
._do
("delete", "relation", RelationData
)
274 def RelationHistory(self
, RelationId
):
275 """ Returns dict(RelationVerrsion: RelationData). """
276 uri
= "/api/0.6/relation/"+str(RelationId
)+"/history"
277 data
= self
._get
(uri
)
278 data
= xml
.dom
.minidom
.parseString(data
)
280 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("relation"):
281 data
= self
._DomParseRelation
(data
)
282 result
[data
[u
"version"]] = data
285 def RelationRelations(self
, RelationId
):
286 """ Returns list of RelationData containing relation #RelationId. """
287 uri
= "/api/0.6/relation/%d/relations"%RelationId
288 data
= self
._get
(uri
)
289 data
= xml
.dom
.minidom
.parseString(data
)
291 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("relation"):
292 data
= self
._DomParseRelation
(data
)
296 def RelationFullRecur(self
, RelationId
):
297 """ Return full data for relation RelationId. Recurisve version relation of relations. """
304 temp
= self
.RelationFull(rid
)
306 if item
["type"] <> "relation":
308 if item
["data"]["id"] in done
:
310 todo
.append(item
["data"]["id"])
314 def RelationFull(self
, RelationId
):
315 """ Return full data for relation RelationId as list of {type: node|way|relation, data: {}}. """
316 uri
= "/api/0.6/relation/"+str(RelationId
)+"/full"
317 data
= self
._get
(uri
)
318 return self
.ParseOsm(data
)
320 def RelationsGet(self
, RelationIdList
):
321 """ Returns dict(RelationId: RelationData) for each relation in RelationIdList """
322 uri
= "/api/0.6/relations?relations=" + ",".join([str(x
) for x
in RelationIdList
])
323 data
= self
._get
(uri
)
324 data
= xml
.dom
.minidom
.parseString(data
)
326 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("relation"):
327 data
= self
._DomParseRelation
(data
)
328 result
[data
[u
"id"]] = data
331 #######################################################################
333 #######################################################################
335 def ChangesetGet(self
, ChangesetId
):
336 """ Returns ChangesetData for changeset #ChangesetId. """
337 data
= self
._get
("/api/0.6/changeset/"+str(ChangesetId
))
338 data
= xml
.dom
.minidom
.parseString(data
)
339 data
= data
.getElementsByTagName("osm")[0].getElementsByTagName("changeset")[0]
340 return self
._DomParseChangeset
(data
)
342 def ChangesetUpdate(self
, ChangesetTags
= {}):
343 """ Updates current changeset with ChangesetTags. """
344 if self
._CurrentChangesetId
== -1:
345 raise Exception, "No changeset currently opened"
346 if u
"created_by" not in ChangesetTags
:
347 ChangesetTags
[u
"created_by"] = self
._created
_by
348 result
= self
._put
("/api/0.6/changeset/"+str(self
._CurrentChangesetId
), self
._XmlBuild
("changeset", {u
"tag": ChangesetTags
}))
349 return self
._CurrentChangesetId
351 def ChangesetCreate(self
, ChangesetTags
= {}):
352 """ Opens a changeset. Returns #ChangesetId. """
353 if self
._CurrentChangesetId
:
354 raise Exception, "Changeset alreadey opened"
355 if u
"created_by" not in ChangesetTags
:
356 ChangesetTags
[u
"created_by"] = self
._created
_by
357 result
= self
._put
("/api/0.6/changeset/create", self
._XmlBuild
("changeset", {u
"tag": ChangesetTags
}))
358 self
._CurrentChangesetId
= int(result
)
359 return self
._CurrentChangesetId
361 def ChangesetClose(self
):
362 """ Closes current changeset. Returns #ChangesetId. """
363 if not self
._CurrentChangesetId
:
364 raise Exception, "No changeset currently opened"
365 result
= self
._put
("/api/0.6/changeset/"+str(self
._CurrentChangesetId
)+"/close", u
"")
366 CurrentChangesetId
= self
._CurrentChangesetId
367 self
._CurrentChangesetId
= 0
368 return CurrentChangesetId
370 def ChangesetUpload(self
, ChangesData
):
371 """ Upload data. ChangesData is a list of dict {type: node|way|relation, action: create|delete|modify, data: {}}. Returns list with updated ids. """
373 data
+= u
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
374 data
+= u
"<osmChange version=\"0.6\" generator=\"" + self
._created
_by
+ "\">\n"
375 for change
in ChangesData
:
376 data
+= u
"<"+change
["action"]+">\n"
377 change
["data"]["changeset"] = self
._CurrentChangesetId
378 data
+= self
._XmlBuild
(change
["type"], change
["data"], False)
379 data
+= u
"</"+change
["action"]+">\n"
380 data
+= u
"</osmChange>"
381 data
= self
._http
("POST", "/api/0.6/changeset/"+str(self
._CurrentChangesetId
)+"/upload", True, data
)
382 data
= xml
.dom
.minidom
.parseString(data
)
383 data
= data
.getElementsByTagName("diffResult")[0]
384 data
= [x
for x
in data
.childNodes
if x
.nodeType
== x
.ELEMENT_NODE
]
385 for i
in range(len(ChangesData
)):
386 if ChangesData
[i
]["action"] == "delete":
387 ChangesData
[i
]["data"].pop("version")
389 ChangesData
[i
]["data"]["version"] = int(data
[i
].getAttribute("new_id"))
392 def ChangesetDownload(self
, ChangesetId
):
393 """ Download data from a changeset. Returns list of dict {type: node|way|relation, action: create|delete|modify, data: {}}. """
394 uri
= "/api/0.6/changeset/"+str(ChangesetId
)+"/download"
395 data
= self
._get
(uri
)
396 return self
.ParseOsc(data
)
398 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):
399 """ Returns dict(ChangsetId: ChangesetData) matching all criteria. """
402 #######################################################################
404 #######################################################################
406 def Map(self
, min_lon
, min_lat
, max_lon
, max_lat
):
407 """ Download data in bounding box. Returns list of dict {type: node|way|relation, data: {}}. """
408 uri
= "/api/0.6/map?bbox=%f,%f,%f,%f"%(min_lon
, min_lat
, max_lon
, max_lat
)
409 data
= self
._get
(uri
)
410 return self
.ParseOsm(data
)
412 #######################################################################
414 #######################################################################
416 def ParseOsm(self
, data
):
417 """ Parse osm data. Returns list of dict {type: node|way|relation, data: {}}. """
418 data
= xml
.dom
.minidom
.parseString(data
)
419 data
= data
.getElementsByTagName("osm")[0]
421 for elem
in data
.childNodes
:
422 if elem
.nodeName
== u
"node":
423 result
.append({u
"type": elem
.nodeName
, u
"data": self
._DomParseNode
(elem
)})
424 elif elem
.nodeName
== u
"way":
425 result
.append({u
"type": elem
.nodeName
, u
"data": self
._DomParseWay
(elem
)})
426 elif elem
.nodeName
== u
"relation":
427 result
.append({u
"type": elem
.nodeName
, u
"data": self
._DomParseRelation
(elem
)})
430 def ParseOsc(self
, data
):
431 """ Parse osc data. Returns list of dict {type: node|way|relation, action: create|delete|modify, data: {}}. """
432 data
= xml
.dom
.minidom
.parseString(data
)
433 data
= data
.getElementsByTagName("osmChange")[0]
435 for action
in data
.childNodes
:
436 if action
.nodeName
== u
"#text": continue
437 for elem
in action
.childNodes
:
438 if elem
.nodeName
== u
"node":
439 result
.append({u
"action":action
.nodeName
, u
"type": elem
.nodeName
, u
"data": self
._DomParseNode
(elem
)})
440 elif elem
.nodeName
== u
"way":
441 result
.append({u
"action":action
.nodeName
, u
"type": elem
.nodeName
, u
"data": self
._DomParseWay
(elem
)})
442 elif elem
.nodeName
== u
"relation":
443 result
.append({u
"action":action
.nodeName
, u
"type": elem
.nodeName
, u
"data": self
._DomParseRelation
(elem
)})
446 #######################################################################
447 # Internal http function #
448 #######################################################################
450 def _do(self
, action
, OsmType
, OsmData
):
451 if self
._changesetauto
:
452 self
._changesetautodata
.append({"action":action
, "type":OsmType
, "data":OsmData
})
453 self
._changesetautoflush
()
456 return self
._do
_manu
(action
, OsmType
, OsmData
)
458 def _do_manu(self
, action
, OsmType
, OsmData
):
459 if not self
._CurrentChangesetId
:
460 raise Exception, "You need to open a changeset before uploading data"
461 if u
"timestamp" in OsmData
:
462 OsmData
.pop(u
"timestamp")
463 OsmData
[u
"changeset"] = self
._CurrentChangesetId
464 if action
== "create":
465 if OsmData
.get(u
"id", -1) > 0:
466 raise Exception, "This "+OsmType
+" already exists"
467 result
= self
._put
("/api/0.6/"+OsmType
+"/create", self
._XmlBuild
(OsmType
, OsmData
))
468 OsmData
[u
"id"] = int(result
.strip())
469 OsmData
[u
"version"] = 1
471 elif action
== "update":
472 result
= self
._put
("/api/0.6/"+OsmType
+"/"+str(OsmData
[u
"id"]), self
._XmlBuild
(OsmType
, OsmData
))
473 OsmData
[u
"version"] = int(result
.strip())
475 elif action
=="delete":
476 result
= self
._delete
("/api/0.6/"+OsmType
+"/"+str(OsmData
[u
"id"]), self
._XmlBuild
(OsmType
, OsmData
))
477 OsmData
[u
"version"] = int(result
.strip())
478 OsmData
[u
"visible"] = False
481 def _changesetautoflush(self
, force
= False):
482 while (len(self
._changesetautodata
) >= self
._changesetautosize
) or (force
and self
._changesetautodata
):
483 self
.ChangesetCreate(self
._changesetautotags
)
484 self
.ChangesetUpload(self
._changesetautodata
[:self
._changesetautosize
])
485 self
._changesetautodata
= self
._changesetautodata
[self
._changesetautosize
:]
486 self
.ChangesetClose()
489 def _http_request(self
, cmd
, path
, auth
, send
):
490 self
._conn
.putrequest(cmd
, path
)
491 self
._conn
.putheader('User-Agent', self
._created
_by
)
493 self
._conn
.putheader('Authorization', 'Basic ' + base64
.encodestring(self
._username
+ ':' + self
._password
).strip())
495 self
._conn
.putheader('Content-Length', len(send
))
496 self
._conn
.endheaders()
498 self
._conn
.send(send
)
499 response
= self
._conn
.getresponse()
500 if response
.status
<> 200:
502 if response
.status
== 410:
504 raise Exception, "API returns unexpected status code "+str(response
.status
)+" ("+response
.reason
+")"
505 return response
.read()
507 def _http(self
, cmd
, path
, auth
, send
):
512 return self
._http
_request
(cmd
, path
, auth
, send
)
515 if i
<> 1: time
.sleep(2)
516 self
._conn
= httplib
.HTTPConnection(self
._api
, 80)
518 def _get(self
, path
):
519 return self
._http
('GET', path
, False, None)
521 def _put(self
, path
, data
):
522 return self
._http
('PUT', path
, True, data
)
524 def _delete(self
, path
, data
):
525 return self
._http
('DELETE', path
, True, data
)
527 #######################################################################
528 # Internal dom function #
529 #######################################################################
531 def _DomGetAttributes(self
, DomElement
):
532 """ Returns a formated dictionnary of attributes of a DomElement. """
534 for k
, v
in DomElement
.attributes
.items():
535 if k
== u
"uid" : v
= int(v
)
536 elif k
== u
"changeset" : v
= int(v
)
537 elif k
== u
"version" : v
= int(v
)
538 elif k
== u
"id" : v
= int(v
)
539 elif k
== u
"lat" : v
= float(v
)
540 elif k
== u
"lon" : v
= float(v
)
541 elif k
== u
"open" : v
= v
=="true"
542 elif k
== u
"visible" : v
= v
=="true"
543 elif k
== u
"ref" : v
= int(v
)
547 def _DomGetTag(self
, DomElement
):
548 """ Returns the dictionnary of tags of a DomElement. """
550 for t
in DomElement
.getElementsByTagName("tag"):
551 k
= t
.attributes
["k"].value
552 v
= t
.attributes
["v"].value
556 def _DomGetNd(self
, DomElement
):
557 """ Returns the list of nodes of a DomElement. """
559 for t
in DomElement
.getElementsByTagName("nd"):
560 result
.append(int(int(t
.attributes
["ref"].value
)))
563 def _DomGetMember(self
, DomElement
):
564 """ Returns a list of relation members. """
566 for m
in DomElement
.getElementsByTagName("member"):
567 result
.append(self
._DomGetAttributes
(m
))
570 def _DomParseNode(self
, DomElement
):
571 """ Returns NodeData for the node. """
572 result
= self
._DomGetAttributes
(DomElement
)
573 result
[u
"tag"] = self
._DomGetTag
(DomElement
)
576 def _DomParseWay(self
, DomElement
):
577 """ Returns WayData for the way. """
578 result
= self
._DomGetAttributes
(DomElement
)
579 result
[u
"tag"] = self
._DomGetTag
(DomElement
)
580 result
[u
"nd"] = self
._DomGetNd
(DomElement
)
583 def _DomParseRelation(self
, DomElement
):
584 """ Returns RelationData for the relation. """
585 result
= self
._DomGetAttributes
(DomElement
)
586 result
[u
"tag"] = self
._DomGetTag
(DomElement
)
587 result
[u
"member"] = self
._DomGetMember
(DomElement
)
590 def _DomParseChangeset(self
, DomElement
):
591 """ Returns ChangesetData for the changeset. """
592 result
= self
._DomGetAttributes
(DomElement
)
593 result
[u
"tag"] = self
._DomGetTag
(DomElement
)
596 #######################################################################
597 # Internal xml builder #
598 #######################################################################
600 def _XmlBuild(self
, ElementType
, ElementData
, WithHeaders
= True):
604 xml
+= u
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
605 xml
+= u
"<osm version=\"0.6\" generator=\"" + self
._created
_by
+ "\">\n"
607 # <element attr="val">
608 xml
+= u
" <" + ElementType
609 if u
"id" in ElementData
:
610 xml
+= u
" id=\"" + str(ElementData
[u
"id"]) + u
"\""
611 if u
"lat" in ElementData
:
612 xml
+= u
" lat=\"" + str(ElementData
[u
"lat"]) + u
"\""
613 if u
"lon" in ElementData
:
614 xml
+= u
" lon=\"" + str(ElementData
[u
"lon"]) + u
"\""
615 if u
"version" in ElementData
:
616 xml
+= u
" version=\"" + str(ElementData
[u
"version"]) + u
"\""
617 xml
+= u
" visible=\"" + str(ElementData
.get(u
"visible", True)).lower() + u
"\""
618 if ElementType
in [u
"node", u
"way", u
"relation"]:
619 xml
+= u
" changeset=\"" + str(self
._CurrentChangesetId
) + u
"\""
623 for k
, v
in ElementData
.get(u
"tag", {}).items():
624 xml
+= u
" <tag k=\""+self
._XmlEncode
(k
)+u
"\" v=\""+self
._XmlEncode
(v
)+u
"\"/>\n"
627 for member
in ElementData
.get(u
"member", []):
628 xml
+= u
" <member type=\""+member
[u
"type"]+"\" ref=\""+str(member
[u
"ref"])+u
"\" role=\""+self
._XmlEncode
(member
[u
"role"])+"\"/>\n"
631 for ref
in ElementData
.get(u
"nd", []):
632 xml
+= u
" <nd ref=\""+str(ref
)+u
"\"/>\n"
635 xml
+= u
" </" + ElementType
+ u
">\n"
640 return xml
.encode("utf8")
642 def _XmlEncode(self
, text
):
643 return text
.replace("&", "&").replace("\"", """)
645 ## End of main class ##
646 ###########################################################################