+ Fundacul
[osm-ro-tools.git] / OsmApi.py
blob3b1b9b1b20b3de17407ac827bbcbbf34de296c2f
1 #-*- coding: utf-8 -*-
3 ###########################################################################
4 ## ##
5 ## Copyrights Etienne Chové <chove@crans.org> 2009-2010 ##
6 ## ##
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. ##
11 ## ##
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. ##
16 ## ##
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/>. ##
19 ## ##
20 ###########################################################################
22 ## HomePage : http://wiki.openstreetmap.org/wiki/PythonOsmApi
24 ###########################################################################
25 ## History ##
26 ###########################################################################
27 ## 0.2.19 2010-05-24 Add debug message on ApiError ##
28 ## 0.2.18 2010-04-20 Fix ChangesetClose and _http_request ##
29 ## 0.2.17 2010-01-02 Capabilities implementation ##
30 ## 0.2.16 2010-01-02 ChangesetsGet by Alexander Rampp ##
31 ## 0.2.15 2009-12-16 xml encoding error for < and > ##
32 ## 0.2.14 2009-11-20 changesetautomulti parameter ##
33 ## 0.2.13 2009-11-16 modify instead update for osc ##
34 ## 0.2.12 2009-11-14 raise ApiError on 4xx errors -- Xoff ##
35 ## 0.2.11 2009-10-14 unicode error on ChangesetUpload ##
36 ## 0.2.10 2009-10-14 RelationFullRecur definition ##
37 ## 0.2.9 2009-10-13 automatic changeset management ##
38 ## ChangesetUpload implementation ##
39 ## 0.2.8 2009-10-13 *(Create|Update|Delete) use not unique _do method ##
40 ## 0.2.7 2009-10-09 implement all missing fonctions except ##
41 ## ChangesetsGet and GetCapabilities ##
42 ## 0.2.6 2009-10-09 encoding clean-up ##
43 ## 0.2.5 2009-10-09 implements NodesGet, WaysGet, RelationsGet ##
44 ## ParseOsm, ParseOsc ##
45 ## 0.2.4 2009-10-06 clean-up ##
46 ## 0.2.3 2009-09-09 keep http connection alive for multiple request ##
47 ## (Node|Way|Relation)Get return None when object ##
48 ## have been deleted (raising error before) ##
49 ## 0.2.2 2009-07-13 can identify applications built on top of the lib ##
50 ## 0.2.1 2009-05-05 some changes in constructor -- chove@crans.org ##
51 ## 0.2 2009-05-01 initial import ##
52 ###########################################################################
54 __version__ = '0.2.19'
56 import httplib, base64, xml.dom.minidom, time, sys, urllib
58 class ApiError(Exception):
60 def __init__(self, status, reason, payload):
61 self.status = status
62 self.reason = reason
63 self.payload = payload
65 def __str__(self):
66 return "Request failed: " + str(self.status) + " - " + self.reason + " - " + self.payload
68 ###########################################################################
69 ## Main class ##
71 class OsmApi:
73 def __init__(self,
74 username = None,
75 password = None,
76 passwordfile = None,
77 appid = "",
78 created_by = "PythonOsmApi/"+__version__,
79 api = "www.openstreetmap.org",
80 changesetauto = False,
81 changesetautotags = {},
82 changesetautosize = 500,
83 changesetautomulti = 1,
84 debug = False
87 # debug
88 self._debug = debug
90 # Get username
91 if username:
92 self._username = username
93 elif passwordfile:
94 self._username = open(passwordfile).readline().split(":")[0].strip()
96 # Get password
97 if password:
98 self._password = password
99 elif passwordfile:
100 for l in open(passwordfile).readlines():
101 l = l.strip().split(":")
102 if l[0] == self._username:
103 self._password = l[1]
105 # Changest informations
106 self._changesetauto = changesetauto # auto create and close changesets
107 self._changesetautotags = changesetautotags # tags for automatic created changesets
108 self._changesetautosize = changesetautosize # change count for auto changeset
109 self._changesetautosize = changesetautosize # change count for auto changeset
110 self._changesetautomulti = changesetautomulti # close a changeset every # upload
111 self._changesetautocpt = 0
112 self._changesetautodata = [] # data to upload for auto group
114 # Get API
115 self._api = api
117 # Get created_by
118 if not appid:
119 self._created_by = created_by
120 else:
121 self._created_by = appid + " (" + created_by + ")"
123 # Initialisation
124 self._CurrentChangesetId = 0
126 # Http connection
127 self._conn = httplib.HTTPConnection(self._api, 80)
129 def __del__(self):
130 if self._changesetauto:
131 self._changesetautoflush(True)
132 return None
134 #######################################################################
135 # Capabilities #
136 #######################################################################
138 def Capabilities(self):
139 """ Returns ApiCapabilities. """
140 uri = "/api/capabilities"
141 data = self._get(uri)
142 data = xml.dom.minidom.parseString(data)
143 print data.getElementsByTagName("osm")
144 data = data.getElementsByTagName("osm")[0].getElementsByTagName("api")[0]
145 result = {}
146 for elem in data.childNodes:
147 if elem.nodeType <> elem.ELEMENT_NODE:
148 continue
149 result[elem.nodeName] = {}
150 print elem.nodeName
151 for k, v in elem.attributes.items():
152 try:
153 result[elem.nodeName][k] = float(v)
154 except:
155 result[elem.nodeName][k] = v
156 return result
158 #######################################################################
159 # Node #
160 #######################################################################
162 def NodeGet(self, NodeId, NodeVersion = -1):
163 """ Returns NodeData for node #NodeId. """
164 uri = "/api/0.6/node/"+str(NodeId)
165 if NodeVersion <> -1: uri += "/"+str(NodeVersion)
166 data = self._get(uri)
167 if not data: return data
168 data = xml.dom.minidom.parseString(data)
169 data = data.getElementsByTagName("osm")[0].getElementsByTagName("node")[0]
170 return self._DomParseNode(data)
172 def NodeCreate(self, NodeData):
173 """ Creates a node. Returns updated NodeData (without timestamp). """
174 return self._do("create", "node", NodeData)
176 def NodeUpdate(self, NodeData):
177 """ Updates node with NodeData. Returns updated NodeData (without timestamp). """
178 return self._do("modify", "node", NodeData)
180 def NodeDelete(self, NodeData):
181 """ Delete node with NodeData. Returns updated NodeData (without timestamp). """
182 return self._do("delete", "node", NodeData)
184 def NodeHistory(self, NodeId):
185 """ Returns dict(NodeVerrsion: NodeData). """
186 uri = "/api/0.6/node/"+str(NodeId)+"/history"
187 data = self._get(uri)
188 data = xml.dom.minidom.parseString(data)
189 result = {}
190 for data in data.getElementsByTagName("osm")[0].getElementsByTagName("node"):
191 data = self._DomParseNode(data)
192 result[data[u"version"]] = data
193 return result
195 def NodeWays(self, NodeId):
196 """ Returns [WayData, ... ] containing node #NodeId. """
197 uri = "/api/0.6/node/%d/ways"%NodeId
198 data = self._get(uri)
199 data = xml.dom.minidom.parseString(data)
200 result = []
201 for data in data.getElementsByTagName("osm")[0].getElementsByTagName("way"):
202 data = self._DomParseRelation(data)
203 result.append(data)
204 return result
206 def NodeRelations(self, NodeId):
207 """ Returns [RelationData, ... ] containing node #NodeId. """
208 uri = "/api/0.6/node/%d/relations"%NodeId
209 data = self._get(uri)
210 data = xml.dom.minidom.parseString(data)
211 result = []
212 for data in data.getElementsByTagName("osm")[0].getElementsByTagName("relation"):
213 data = self._DomParseRelation(data)
214 result.append(data)
215 return result
217 def NodesGet(self, NodeIdList):
218 """ Returns dict(NodeId: NodeData) for each node in NodeIdList """
219 uri = "/api/0.6/nodes?nodes=" + ",".join([str(x) for x in NodeIdList])
220 data = self._get(uri)
221 data = xml.dom.minidom.parseString(data)
222 result = {}
223 for data in data.getElementsByTagName("osm")[0].getElementsByTagName("node"):
224 data = self._DomParseNode(data)
225 result[data[u"id"]] = data
226 return result
228 #######################################################################
229 # Way #
230 #######################################################################
232 def WayGet(self, WayId, WayVersion = -1):
233 """ Returns WayData for way #WayId. """
234 uri = "/api/0.6/way/"+str(WayId)
235 if WayVersion <> -1: uri += "/"+str(WayVersion)
236 data = self._get(uri)
237 if not data: return data
238 data = xml.dom.minidom.parseString(data)
239 data = data.getElementsByTagName("osm")[0].getElementsByTagName("way")[0]
240 return self._DomParseWay(data)
242 def WayCreate(self, WayData):
243 """ Creates a way. Returns updated WayData (without timestamp). """
244 return self._do("create", "way", WayData)
246 def WayUpdate(self, WayData):
247 """ Updates way with WayData. Returns updated WayData (without timestamp). """
248 return self._do("modify", "way", WayData)
250 def WayDelete(self, WayData):
251 """ Delete way with WayData. Returns updated WayData (without timestamp). """
252 return self._do("delete", "way", WayData)
254 def WayHistory(self, WayId):
255 """ Returns dict(WayVerrsion: WayData). """
256 uri = "/api/0.6/way/"+str(WayId)+"/history"
257 data = self._get(uri)
258 data = xml.dom.minidom.parseString(data)
259 result = {}
260 for data in data.getElementsByTagName("osm")[0].getElementsByTagName("way"):
261 data = self._DomParseWay(data)
262 result[data[u"version"]] = data
263 return result
265 def WayRelations(self, WayId):
266 """ Returns [RelationData, ...] containing way #WayId. """
267 uri = "/api/0.6/way/%d/relations"%WayId
268 data = self._get(uri)
269 data = xml.dom.minidom.parseString(data)
270 result = []
271 for data in data.getElementsByTagName("osm")[0].getElementsByTagName("relation"):
272 data = self._DomParseRelation(data)
273 result.append(data)
274 return result
276 def WayFull(self, WayId):
277 """ Return full data for way WayId as list of {type: node|way|relation, data: {}}. """
278 uri = "/api/0.6/way/"+str(WayId)+"/full"
279 data = self._get(uri)
280 return self.ParseOsm(data)
282 def WaysGet(self, WayIdList):
283 """ Returns dict(WayId: WayData) for each way in WayIdList """
284 uri = "/api/0.6/ways?ways=" + ",".join([str(x) for x in WayIdList])
285 data = self._get(uri)
286 data = xml.dom.minidom.parseString(data)
287 result = {}
288 for data in data.getElementsByTagName("osm")[0].getElementsByTagName("way"):
289 data = self._DomParseWay(data)
290 result[data[u"id"]] = data
291 return result
293 #######################################################################
294 # Relation #
295 #######################################################################
297 def RelationGet(self, RelationId, RelationVersion = -1):
298 """ Returns RelationData for relation #RelationId. """
299 uri = "/api/0.6/relation/"+str(RelationId)
300 if RelationVersion <> -1: uri += "/"+str(RelationVersion)
301 data = self._get(uri)
302 if not data: return data
303 data = xml.dom.minidom.parseString(data)
304 data = data.getElementsByTagName("osm")[0].getElementsByTagName("relation")[0]
305 return self._DomParseRelation(data)
307 def RelationCreate(self, RelationData):
308 """ Creates a relation. Returns updated RelationData (without timestamp). """
309 return self._do("create", "relation", RelationData)
311 def RelationUpdate(self, RelationData):
312 """ Updates relation with RelationData. Returns updated RelationData (without timestamp). """
313 return self._do("modify", "relation", RelationData)
315 def RelationDelete(self, RelationData):
316 """ Delete relation with RelationData. Returns updated RelationData (without timestamp). """
317 return self._do("delete", "relation", RelationData)
319 def RelationHistory(self, RelationId):
320 """ Returns dict(RelationVerrsion: RelationData). """
321 uri = "/api/0.6/relation/"+str(RelationId)+"/history"
322 data = self._get(uri)
323 data = xml.dom.minidom.parseString(data)
324 result = {}
325 for data in data.getElementsByTagName("osm")[0].getElementsByTagName("relation"):
326 data = self._DomParseRelation(data)
327 result[data[u"version"]] = data
328 return result
330 def RelationRelations(self, RelationId):
331 """ Returns list of RelationData containing relation #RelationId. """
332 uri = "/api/0.6/relation/%d/relations"%RelationId
333 data = self._get(uri)
334 data = xml.dom.minidom.parseString(data)
335 result = []
336 for data in data.getElementsByTagName("osm")[0].getElementsByTagName("relation"):
337 data = self._DomParseRelation(data)
338 result.append(data)
339 return result
341 def RelationFullRecur(self, RelationId):
342 """ Return full data for relation RelationId. Recurisve version relation of relations. """
343 data = []
344 todo = [RelationId]
345 done = []
346 while todo:
347 rid = todo.pop(0)
348 done.append(rid)
349 temp = self.RelationFull(rid)
350 for item in temp:
351 if item["type"] <> "relation":
352 continue
353 if item["data"]["id"] in done:
354 continue
355 todo.append(item["data"]["id"])
356 data += temp
357 return data
359 def RelationFull(self, RelationId):
360 """ Return full data for relation RelationId as list of {type: node|way|relation, data: {}}. """
361 uri = "/api/0.6/relation/"+str(RelationId)+"/full"
362 data = self._get(uri)
363 return self.ParseOsm(data)
365 def RelationsGet(self, RelationIdList):
366 """ Returns dict(RelationId: RelationData) for each relation in RelationIdList """
367 uri = "/api/0.6/relations?relations=" + ",".join([str(x) for x in RelationIdList])
368 data = self._get(uri)
369 data = xml.dom.minidom.parseString(data)
370 result = {}
371 for data in data.getElementsByTagName("osm")[0].getElementsByTagName("relation"):
372 data = self._DomParseRelation(data)
373 result[data[u"id"]] = data
374 return result
376 #######################################################################
377 # Changeset #
378 #######################################################################
380 def ChangesetGet(self, ChangesetId):
381 """ Returns ChangesetData for changeset #ChangesetId. """
382 data = self._get("/api/0.6/changeset/"+str(ChangesetId))
383 data = xml.dom.minidom.parseString(data)
384 data = data.getElementsByTagName("osm")[0].getElementsByTagName("changeset")[0]
385 return self._DomParseChangeset(data)
387 def ChangesetUpdate(self, ChangesetTags = {}):
388 """ Updates current changeset with ChangesetTags. """
389 if self._CurrentChangesetId == -1:
390 raise Exception, "No changeset currently opened"
391 if u"created_by" not in ChangesetTags:
392 ChangesetTags[u"created_by"] = self._created_by
393 result = self._put("/api/0.6/changeset/"+str(self._CurrentChangesetId), self._XmlBuild("changeset", {u"tag": ChangesetTags}))
394 return self._CurrentChangesetId
396 def ChangesetCreate(self, ChangesetTags = {}):
397 """ Opens a changeset. Returns #ChangesetId. """
398 if self._CurrentChangesetId:
399 raise Exception, "Changeset alreadey opened"
400 if u"created_by" not in ChangesetTags:
401 ChangesetTags[u"created_by"] = self._created_by
402 result = self._put("/api/0.6/changeset/create", self._XmlBuild("changeset", {u"tag": ChangesetTags}))
403 self._CurrentChangesetId = int(result)
404 return self._CurrentChangesetId
406 def ChangesetClose(self):
407 """ Closes current changeset. Returns #ChangesetId. """
408 if not self._CurrentChangesetId:
409 raise Exception, "No changeset currently opened"
410 result = self._put("/api/0.6/changeset/"+str(self._CurrentChangesetId)+"/close", u"")
411 CurrentChangesetId = self._CurrentChangesetId
412 self._CurrentChangesetId = 0
413 return CurrentChangesetId
415 def ChangesetUpload(self, ChangesData):
416 """ Upload data. ChangesData is a list of dict {type: node|way|relation, action: create|delete|modify, data: {}}. Returns list with updated ids. """
417 data = ""
418 data += u"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
419 data += u"<osmChange version=\"0.6\" generator=\"" + self._created_by + "\">\n"
420 for change in ChangesData:
421 data += u"<"+change["action"]+">\n"
422 change["data"]["changeset"] = self._CurrentChangesetId
423 data += self._XmlBuild(change["type"], change["data"], False).decode("utf-8")
424 data += u"</"+change["action"]+">\n"
425 data += u"</osmChange>"
426 data = self._http("POST", "/api/0.6/changeset/"+str(self._CurrentChangesetId)+"/upload", True, data.encode("utf-8"))
427 data = xml.dom.minidom.parseString(data)
428 data = data.getElementsByTagName("diffResult")[0]
429 data = [x for x in data.childNodes if x.nodeType == x.ELEMENT_NODE]
430 for i in range(len(ChangesData)):
431 if ChangesData[i]["action"] == "delete":
432 ChangesData[i]["data"].pop("version")
433 else:
434 ChangesData[i]["data"]["version"] = int(data[i].getAttribute("new_id"))
435 return ChangesData
437 def ChangesetDownload(self, ChangesetId):
438 """ Download data from a changeset. Returns list of dict {type: node|way|relation, action: create|delete|modify, data: {}}. """
439 uri = "/api/0.6/changeset/"+str(ChangesetId)+"/download"
440 data = self._get(uri)
441 return self.ParseOsc(data)
443 def ChangesetsGet(self, min_lon=None, min_lat=None, max_lon=None, max_lat=None,
444 userid=None, username=None,
445 closed_after=None, created_before=None,
446 only_open=False, only_closed=False):
447 """ Returns dict(ChangsetId: ChangesetData) matching all criteria. """
449 uri = "/api/0.6/changesets"
450 params = {}
451 if min_lon or min_lat or max_lon or max_lat:
452 params["bbox"] = ",".join([str(min_lon),str(min_lat),str(max_lon),str(max_lat)])
453 if userid:
454 params["user"] = userid
455 if username:
456 params["display_name"] = username
457 if closed_after and not created_before:
458 params["time"] = closed_after
459 if created_before:
460 if not closed_after:
461 closed_after = "1970-01-01T00:00:00Z"
462 params["time"] = closed_after + "," + created_before
463 if only_open:
464 params["open"] = 1
465 if only_closed:
466 params["closed"] = 1
468 if params:
469 uri += "?" + urllib.urlencode(params)
471 data = self._get(uri)
472 data = xml.dom.minidom.parseString(data)
473 data = data.getElementsByTagName("osm")[0].getElementsByTagName("changeset")
474 result = {}
475 for curChangeset in data:
476 tmpCS = self._DomParseChangeset(curChangeset)
477 result[tmpCS["id"]] = tmpCS
478 return result
480 #######################################################################
481 # Other #
482 #######################################################################
484 def Map(self, min_lon, min_lat, max_lon, max_lat):
485 """ Download data in bounding box. Returns list of dict {type: node|way|relation, data: {}}. """
486 uri = "/api/0.6/map?bbox=%f,%f,%f,%f"%(min_lon, min_lat, max_lon, max_lat)
487 data = self._get(uri)
488 return self.ParseOsm(data)
490 #######################################################################
491 # Data parser #
492 #######################################################################
494 def ParseOsm(self, data):
495 """ Parse osm data. Returns list of dict {type: node|way|relation, data: {}}. """
496 data = xml.dom.minidom.parseString(data)
497 data = data.getElementsByTagName("osm")[0]
498 result = []
499 for elem in data.childNodes:
500 if elem.nodeName == u"node":
501 result.append({u"type": elem.nodeName, u"data": self._DomParseNode(elem)})
502 elif elem.nodeName == u"way":
503 result.append({u"type": elem.nodeName, u"data": self._DomParseWay(elem)})
504 elif elem.nodeName == u"relation":
505 result.append({u"type": elem.nodeName, u"data": self._DomParseRelation(elem)})
506 return result
508 def ParseOsc(self, data):
509 """ Parse osc data. Returns list of dict {type: node|way|relation, action: create|delete|modify, data: {}}. """
510 data = xml.dom.minidom.parseString(data)
511 data = data.getElementsByTagName("osmChange")[0]
512 result = []
513 for action in data.childNodes:
514 if action.nodeName == u"#text": continue
515 for elem in action.childNodes:
516 if elem.nodeName == u"node":
517 result.append({u"action":action.nodeName, u"type": elem.nodeName, u"data": self._DomParseNode(elem)})
518 elif elem.nodeName == u"way":
519 result.append({u"action":action.nodeName, u"type": elem.nodeName, u"data": self._DomParseWay(elem)})
520 elif elem.nodeName == u"relation":
521 result.append({u"action":action.nodeName, u"type": elem.nodeName, u"data": self._DomParseRelation(elem)})
522 return result
524 #######################################################################
525 # Internal http function #
526 #######################################################################
528 def _do(self, action, OsmType, OsmData):
529 if self._changesetauto:
530 self._changesetautodata.append({"action":action, "type":OsmType, "data":OsmData})
531 self._changesetautoflush()
532 return None
533 else:
534 return self._do_manu(action, OsmType, OsmData)
536 def _do_manu(self, action, OsmType, OsmData):
537 if not self._CurrentChangesetId:
538 raise Exception, "You need to open a changeset before uploading data"
539 if u"timestamp" in OsmData:
540 OsmData.pop(u"timestamp")
541 OsmData[u"changeset"] = self._CurrentChangesetId
542 if action == "create":
543 if OsmData.get(u"id", -1) > 0:
544 raise Exception, "This "+OsmType+" already exists"
545 result = self._put("/api/0.6/"+OsmType+"/create", self._XmlBuild(OsmType, OsmData))
546 OsmData[u"id"] = int(result.strip())
547 OsmData[u"version"] = 1
548 return OsmData
549 elif action == "modify":
550 result = self._put("/api/0.6/"+OsmType+"/"+str(OsmData[u"id"]), self._XmlBuild(OsmType, OsmData))
551 OsmData[u"version"] = int(result.strip())
552 return OsmData
553 elif action =="delete":
554 result = self._delete("/api/0.6/"+OsmType+"/"+str(OsmData[u"id"]), self._XmlBuild(OsmType, OsmData))
555 OsmData[u"version"] = int(result.strip())
556 OsmData[u"visible"] = False
557 return OsmData
559 def flush(self):
560 return self._changesetautoflush(True)
562 def _changesetautoflush(self, force = False):
563 while (len(self._changesetautodata) >= self._changesetautosize) or (force and self._changesetautodata):
564 if self._changesetautocpt == 0:
565 self.ChangesetCreate(self._changesetautotags)
566 self.ChangesetUpload(self._changesetautodata[:self._changesetautosize])
567 self._changesetautodata = self._changesetautodata[self._changesetautosize:]
568 self._changesetautocpt += 1
569 if self._changesetautocpt == self._changesetautomulti:
570 self.ChangesetClose()
571 self._changesetautocpt = 0
572 if self._changesetautocpt and force:
573 self.ChangesetClose()
574 self._changesetautocpt = 0
575 return None
577 def _http_request(self, cmd, path, auth, send):
578 if self._debug:
579 path2 = path
580 if len(path2) > 50:
581 path2 = path2[:50]+"[...]"
582 print >>sys.stderr, "%s %s %s"%(time.strftime("%Y-%m-%d %H:%M:%S"),cmd,path2)
583 self._conn.putrequest(cmd, path)
584 self._conn.putheader('User-Agent', self._created_by)
585 if auth:
586 self._conn.putheader('Authorization', 'Basic ' + base64.encodestring(self._username + ':' + self._password).strip())
587 if send <> None:
588 self._conn.putheader('Content-Length', len(send))
589 self._conn.endheaders()
590 if send:
591 self._conn.send(send)
592 response = self._conn.getresponse()
593 if response.status <> 200:
594 payload = response.read().strip()
595 if response.status == 410:
596 return None
597 raise ApiError(response.status, response.reason, payload)
598 if self._debug:
599 print >>sys.stderr, "%s %s %s done"%(time.strftime("%Y-%m-%d %H:%M:%S"),cmd,path2)
600 return response.read()
602 def _http(self, cmd, path, auth, send):
603 i = 0
604 while True:
605 i += 1
606 try:
607 return self._http_request(cmd, path, auth, send)
608 except ApiError, e:
609 if e.status >= 500:
610 if i == 5: raise
611 if i <> 1: time.sleep(5)
612 self._conn = httplib.HTTPConnection(self._api, 80)
613 else: raise
614 except Exception:
615 if i == 5: raise
616 if i <> 1: time.sleep(5)
617 self._conn = httplib.HTTPConnection(self._api, 80)
619 def _get(self, path):
620 return self._http('GET', path, False, None)
622 def _put(self, path, data):
623 return self._http('PUT', path, True, data)
625 def _delete(self, path, data):
626 return self._http('DELETE', path, True, data)
628 #######################################################################
629 # Internal dom function #
630 #######################################################################
632 def _DomGetAttributes(self, DomElement):
633 """ Returns a formated dictionnary of attributes of a DomElement. """
634 result = {}
635 for k, v in DomElement.attributes.items():
636 if k == u"uid" : v = int(v)
637 elif k == u"changeset" : v = int(v)
638 elif k == u"version" : v = int(v)
639 elif k == u"id" : v = int(v)
640 elif k == u"lat" : v = float(v)
641 elif k == u"lon" : v = float(v)
642 elif k == u"open" : v = v=="true"
643 elif k == u"visible" : v = v=="true"
644 elif k == u"ref" : v = int(v)
645 result[k] = v
646 return result
648 def _DomGetTag(self, DomElement):
649 """ Returns the dictionnary of tags of a DomElement. """
650 result = {}
651 for t in DomElement.getElementsByTagName("tag"):
652 k = t.attributes["k"].value
653 v = t.attributes["v"].value
654 result[k] = v
655 return result
657 def _DomGetNd(self, DomElement):
658 """ Returns the list of nodes of a DomElement. """
659 result = []
660 for t in DomElement.getElementsByTagName("nd"):
661 result.append(int(int(t.attributes["ref"].value)))
662 return result
664 def _DomGetMember(self, DomElement):
665 """ Returns a list of relation members. """
666 result = []
667 for m in DomElement.getElementsByTagName("member"):
668 result.append(self._DomGetAttributes(m))
669 return result
671 def _DomParseNode(self, DomElement):
672 """ Returns NodeData for the node. """
673 result = self._DomGetAttributes(DomElement)
674 result[u"tag"] = self._DomGetTag(DomElement)
675 return result
677 def _DomParseWay(self, DomElement):
678 """ Returns WayData for the way. """
679 result = self._DomGetAttributes(DomElement)
680 result[u"tag"] = self._DomGetTag(DomElement)
681 result[u"nd"] = self._DomGetNd(DomElement)
682 return result
684 def _DomParseRelation(self, DomElement):
685 """ Returns RelationData for the relation. """
686 result = self._DomGetAttributes(DomElement)
687 result[u"tag"] = self._DomGetTag(DomElement)
688 result[u"member"] = self._DomGetMember(DomElement)
689 return result
691 def _DomParseChangeset(self, DomElement):
692 """ Returns ChangesetData for the changeset. """
693 result = self._DomGetAttributes(DomElement)
694 result[u"tag"] = self._DomGetTag(DomElement)
695 return result
697 #######################################################################
698 # Internal xml builder #
699 #######################################################################
701 def _XmlBuild(self, ElementType, ElementData, WithHeaders = True):
703 xml = u""
704 if WithHeaders:
705 xml += u"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
706 xml += u"<osm version=\"0.6\" generator=\"" + self._created_by + "\">\n"
708 # <element attr="val">
709 xml += u" <" + ElementType
710 if u"id" in ElementData:
711 xml += u" id=\"" + str(ElementData[u"id"]) + u"\""
712 if u"lat" in ElementData:
713 xml += u" lat=\"" + str(ElementData[u"lat"]) + u"\""
714 if u"lon" in ElementData:
715 xml += u" lon=\"" + str(ElementData[u"lon"]) + u"\""
716 if u"version" in ElementData:
717 xml += u" version=\"" + str(ElementData[u"version"]) + u"\""
718 xml += u" visible=\"" + str(ElementData.get(u"visible", True)).lower() + u"\""
719 if ElementType in [u"node", u"way", u"relation"]:
720 xml += u" changeset=\"" + str(self._CurrentChangesetId) + u"\""
721 xml += u">\n"
723 # <tag... />
724 for k, v in ElementData.get(u"tag", {}).items():
725 xml += u" <tag k=\""+self._XmlEncode(k)+u"\" v=\""+self._XmlEncode(v)+u"\"/>\n"
727 # <member... />
728 for member in ElementData.get(u"member", []):
729 xml += u" <member type=\""+member[u"type"]+"\" ref=\""+str(member[u"ref"])+u"\" role=\""+self._XmlEncode(member[u"role"])+"\"/>\n"
731 # <nd... />
732 for ref in ElementData.get(u"nd", []):
733 xml += u" <nd ref=\""+str(ref)+u"\"/>\n"
735 # </element>
736 xml += u" </" + ElementType + u">\n"
738 if WithHeaders:
739 xml += u"</osm>\n"
741 return xml.encode("utf8")
743 def _XmlEncode(self, text):
744 return text.replace("&", "&amp;").replace("\"", "&quot;").replace("<","&lt;").replace(">","&gt;")
746 ## End of main class ##
747 ###########################################################################