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 import httplib
, base64
, xml
.dom
.minidom
26 def __init__(self
, api
= "www.openstreetmap.org", username
= "EtienneChove", password
= None, created_by
= "PythonOsmApi/0.2"):
29 self
._username
= username
30 self
._password
= password
31 self
._created
_by
= created_by
35 for l
in open("/home/etienne/osm/passwords"):
36 if l
.strip().split(":")[0] == username
:
37 self
._password
= l
.strip().split(":")[1]
41 self
._CurrentChangesetId
= -1
43 #######################################################################
45 #######################################################################
47 def Capabilities(self
):
50 #######################################################################
52 #######################################################################
54 def NodeGet(self
, NodeId
, NodeVersion
= -1):
55 """ Returns NodeData for node #NodeId. """
56 uri
= "/api/0.6/node/"+str(NodeId
)
57 if NodeVersion
<> -1: uri
+= "/"+str(NodeVersion
)
59 data
= xml
.dom
.minidom
.parseString(data
.encode("utf-8"))
60 data
= data
.getElementsByTagName("osm")[0].getElementsByTagName("node")[0]
61 return self
._DomParseNode
(data
)
63 def NodeUpdate(self
, NodeData
):
64 """ Updates node with NodeData. Returns updated NodeData (without timestamp). """
65 if self
._CurrentChangesetId
== -1:
66 raise Execption
, "No changeset currently opened"
67 NodeData
[u
"changeset"] = self
._CurrentChangesetId
68 result
= self
._put
("/api/0.6/node/"+str(NodeData
[u
"id"]), self
._XmlBuild
("node", NodeData
))
69 NodeData
[u
"version"] = int(result
.strip())
70 if u
"timestamp" in NodeData
: NodeData
.pop(u
"timestamp")
73 def NodeDelete(self
, NodeData
):
74 """ Delete node with NodeData. Returns updated NodeData (without timestamp). """
75 if self
._CurrentChangesetId
== -1:
76 raise Execption
, "No changeset currently opened"
77 NodeData
[u
"changeset"] = self
._CurrentChangesetId
78 result
= self
._delete
("/api/0.6/node/"+str(NodeData
[u
"id"]), self
._XmlBuild
("node", NodeData
))
79 NodeData
[u
"version"] = int(result
.strip())
80 NodeData
[u
"visible"] = False
81 if u
"timestamp" in NodeData
: NodeData
.pop(u
"timestamp")
84 def NodeCreate(self
, NodeData
):
85 """ Creates a node. Returns updated NodeData (without timestamp). """
86 if self
._CurrentChangesetId
== -1:
87 raise Execption
, "No changeset currently opened"
88 NodeData
[u
"changeset"] = self
._CurrentChangesetId
89 result
= self
._put
("/api/0.6/node/create", self
._XmlBuild
("node", NodeData
))
90 NodeData
[u
"id"] = int(result
.strip())
91 NodeData
[u
"version"] = 1
92 if u
"timestamp" in NodeData
: NodeData
.pop(u
"timestamp")
95 def NodeHistory(self
, NodeId
):
96 """ Returns dict(NodeVerrsion: NodeData). """
97 uri
= "/api/0.6/node/"+str(NodeId
)+"/history"
99 data
= xml
.dom
.minidom
.parseString(data
.encode("utf-8"))
101 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("node"):
102 data
= self
._DomParseNode
(data
)
103 result
[data
[u
"version"]] = data
106 def NodeWays(self
, NodeId
):
107 """ Returns [WayData, ... ] containing node #NodeId. """
108 # GET node/#/ways TODO
111 def NodeRelations(self
, NodeId
):
112 """ Returns [RelationData, ... ] containing node #NodeId. """
113 # GET node/#/relations TODO
116 def NodesGet(self
, NodeIdList
):
117 """ Will not be implemented. """
120 #######################################################################
122 #######################################################################
124 def WayGet(self
, WayId
, WayVersion
= -1):
125 """ Returns WayData for way #WayId. """
126 uri
= "/api/0.6/way/"+str(WayId
)
127 if WayVersion
<> -1: uri
+= "/"+str(WayVersion
)
128 data
= self
._get
(uri
)
129 data
= xml
.dom
.minidom
.parseString(data
.encode("utf-8"))
130 data
= data
.getElementsByTagName("osm")[0].getElementsByTagName("way")[0]
131 return self
._DomParseWay
(data
)
133 def WayUpdate(self
, WayData
):
134 """ Updates way with WayData. Returns updated WayData (without timestamp). """
135 if self
._CurrentChangesetId
== -1:
136 raise Exception, "No changeset currently opened"
137 WayData
[u
"changeset"] = self
._CurrentChangesetId
138 result
= self
._put
("/api/0.6/way/"+str(WayData
[u
"id"]), self
._XmlBuild
("way", WayData
))
139 WayData
[u
"version"] = int(result
.strip())
140 if u
"timestamp" in WayData
: WayData
.pop(u
"timestamp")
143 def WayDelete(self
, WayData
):
144 """ Delete way with WayData. Returns updated WayData (without timestamp). """
145 if self
._CurrentChangesetId
== -1:
146 raise Exception, "No changeset currently opened"
147 WayData
[u
"changeset"] = self
._CurrentChangesetId
148 result
= self
._delete
("/api/0.6/way/"+str(WayData
[u
"id"]), self
._XmlBuild
("way", WayData
))
149 WayData
[u
"version"] = int(result
.strip())
150 WayData
[u
"visible"] = False
151 if u
"timestamp" in WayData
: WayData
.pop(u
"timestamp")
154 def WayCreate(self
, WayData
):
155 """ Creates a way. Returns updated WayData (without timestamp). """
156 if self
._CurrentChangesetId
== -1:
157 raise Exception, "No changeset currently opened"
158 WayData
[u
"changeset"] = self
._CurrentChangesetId
159 result
= self
._put
("/api/0.6/way/create", self
._XmlBuild
("way", WayData
))
160 WayData
[u
"id"] = int(result
.strip())
161 WayData
[u
"version"] = 1
162 if u
"timestamp" in WayData
: WayData
.pop(u
"timestamp")
165 def WayHistory(self
, WayId
):
166 """ Returns dict(WayVerrsion: WayData). """
167 uri
= "/api/0.6/way/"+str(WayId
)+"/history"
168 data
= self
._get
(uri
)
169 data
= xml
.dom
.minidom
.parseString(data
.encode("utf-8"))
171 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("way"):
172 data
= self
._DomParseWay
(data
)
173 result
[data
[u
"version"]] = data
176 def WayRelations(self
, WayId
):
177 """ Returns [RelationData, ...] containing way #WayId. """
178 # GET way/#/relations
181 def WayFull(self
, WayId
):
182 """ Will not be implemented. """
185 def WaysGet(self
, WayIdList
):
186 """ Will not be implemented. """
189 #######################################################################
191 #######################################################################
193 def RelationGet(self
, RelationId
, RelationVersion
= -1):
194 """ Returns RelationData for relation #RelationId. """
195 uri
= "/api/0.6/relation/"+str(RelationId
)
196 if RelationVersion
<> -1: uri
+= "/"+str(RelationVersion
)
197 data
= self
._get
(uri
)
199 data
= xml
.dom
.minidom
.parseString(data
.encode("utf-8"))
200 data
= data
.getElementsByTagName("osm")[0].getElementsByTagName("relation")[0]
201 return self
._DomParseRelation
(data
)
203 def RelationUpdate(self
, RelationData
):
204 """ Updates relation with RelationData. Returns updated RelationData (without timestamp). """
205 if self
._CurrentChangesetId
== -1:
206 raise Exception, "No changeset currently opened"
207 RelationData
[u
"changeset"] = self
._CurrentChangesetId
208 result
= self
._put
("/api/0.6/relation/"+str(RelationData
[u
"id"]), self
._XmlBuild
("relation", RelationData
))
209 RelationData
[u
"version"] = int(result
.strip())
210 if u
"timestamp" in RelationData
: RelationData
.pop(u
"timestamp")
213 def RelationDelete(self
, RelationData
):
214 """ Delete relation with RelationData. Returns updated RelationData (without timestamp). """
215 if self
._CurrentChangesetId
== -1:
216 raise Exception, "No changeset currently opened"
217 RelationData
[u
"changeset"] = self
._CurrentChangesetId
218 result
= self
._delete
("/api/0.6/relation/"+str(RelationData
[u
"id"]), self
._XmlBuild
("relation", RelationData
))
219 RelationData
[u
"version"] = int(result
.strip())
220 RelationData
[u
"visible"] = False
221 if u
"timestamp" in RelationData
: RelationData
.pop(u
"timestamp")
224 def RelationCreate(self
, RelationData
):
225 """ Creates a relation. Returns updated RelationData (without timestamp). """
226 if self
._CurrentChangesetId
== -1:
227 raise Exception, "No changeset currently opened"
228 RelationData
[u
"changeset"] = self
._CurrentChangesetId
229 result
= self
._put
("/api/0.6/relation/create", self
._XmlBuild
("relation", RelationData
))
230 RelationData
[u
"id"] = int(result
.strip())
231 RelationData
[u
"version"] = 1
232 if u
"timestamp" in RelationData
: RelationData
.pop(u
"timestamp")
235 def RelationHistory(self
, RelationId
):
236 """ Returns dict(RelationVerrsion: RelationData). """
237 uri
= "/api/0.6/relation/"+str(RelationId
)+"/history"
238 data
= self
._get
(uri
)
239 data
= xml
.dom
.minidom
.parseString(data
.encode("utf-8"))
241 for data
in data
.getElementsByTagName("osm")[0].getElementsByTagName("relation"):
242 data
= self
._DomParseRelation
(data
)
243 result
[data
[u
"version"]] = data
246 def RelationRelations(self
, RelationId
):
247 """ Returns list of RelationData containing relation #RelationId. """
248 # GET relation/#/relations TODO
251 def RelationFull(self
, RelationId
):
252 """ Will not be implemented. """
255 def RelationsGet(self
, RelationIdList
):
256 """ Will not be implemented. """
259 #######################################################################
261 #######################################################################
263 def ChangesetGet(self
, ChangesetId
):
264 """ Returns ChangesetData for changeset #ChangesetId. """
265 data
= self
._get
("/api/0.6/changeset/"+str(ChangesetId
))
266 data
= xml
.dom
.minidom
.parseString(data
.encode("utf-8"))
267 data
= data
.getElementsByTagName("osm")[0].getElementsByTagName("changeset")[0]
268 return self
._DomParseChangeset
(data
)
270 def ChangesetUpdate(self
, ChangesetTags
= {}):
271 """ Updates current changeset with ChangesetTags. """
272 if self
._CurrentChangesetId
== -1:
273 raise Execption
, "No changeset currently opened"
274 result
= self
._put
("/api/0.6/changeset/"+str(self
._CurrentChangesetId
), self
._XmlBuild
("changeset", {u
"tag": ChangesetTags
}))
275 return self
._CurrentChangesetId
277 def ChangesetCreate(self
, ChangesetTags
= {}):
278 """ Opens a changeset. Returns #ChangesetId. """
279 result
= self
._put
("/api/0.6/changeset/create", self
._XmlBuild
("changeset", {u
"tag": ChangesetTags
}))
280 self
._CurrentChangesetId
= int(result
)
281 self
._CurrentChangesetTags
= ChangesetTags
282 self
._CurrentChangesetCpt
= 0
283 return self
._CurrentChangesetId
285 def ChangesetClose(self
):
286 """ Closes current changeset. Returns #ChangesetId. """
287 if self
._CurrentChangesetId
== -1:
288 raise Execption
, "No changeset currently opened"
289 result
= self
._put
("/api/0.6/changeset/"+str(self
._CurrentChangesetId
)+"/close", u
"")
290 CurrentChangesetId
= self
._CurrentChangesetId
291 self
._CurrentChangesetId
= -1
292 return CurrentChangesetId
294 def ChangesetUpload(self
):
297 def ChangesetDownload(self
):
300 def ChangesetsGet(self
):
303 #######################################################################
305 #######################################################################
310 def Trackpoints(self
):
316 #######################################################################
317 # Internal http function #
318 #######################################################################
320 def _http(self
, cmd
, path
, auth
, send
):
321 h
= httplib
.HTTPConnection(self
._api
, 80)
322 h
.putrequest(cmd
, path
)
323 h
.putheader('User-Agent', self
._created
_by
)
325 h
.putheader('Authorization', 'Basic ' + base64
.encodestring(self
._username
+ ':' + self
._password
).strip())
327 send
= send
.encode("utf-8")
328 h
.putheader('Content-Length', len(send
))
332 response
= h
.getresponse()
333 if response
.status
<> 200:
334 raise Exception, "API returns unexpected status code "+str(response
.status
)+" ("+response
.reason
+")"
335 return response
.read().decode("utf-8")
337 def _get(self
, path
):
338 return self
._http
('GET', path
, False, None)
340 def _put(self
, path
, data
):
341 return self
._http
('PUT', path
, True, data
)
343 def _delete(self
, path
, data
):
344 return self
._http
('DELETE', path
, True, data
)
346 #######################################################################
347 # Internal dom function #
348 #######################################################################
350 def _DomGetAttributes(self
, DomElement
):
351 """ Returns a formated dictionnary of attributes of a DomElement. """
353 for k
, v
in DomElement
.attributes
.items():
354 k
= k
#.decode("utf8")
355 v
= v
#.decode("utf8")
356 if k
== u
"uid" : v
= int(v
)
357 elif k
== u
"changeset" : v
= int(v
)
358 elif k
== u
"version" : v
= int(v
)
359 elif k
== u
"id" : v
= int(v
)
360 elif k
== u
"lat" : v
= float(v
)
361 elif k
== u
"lon" : v
= float(v
)
362 elif k
== u
"open" : v
= v
=="true"
363 elif k
== u
"visible" : v
= v
=="true"
364 elif k
== u
"ref" : v
= int(v
)
368 def _DomGetTag(self
, DomElement
):
369 """ Returns the dictionnary of tags of a DomElement. """
371 for t
in DomElement
.getElementsByTagName("tag"):
372 k
= t
.attributes
["k"].value
#.decode("utf8")
373 v
= t
.attributes
["v"].value
#.decode("utf8")
377 def _DomGetNd(self
, DomElement
):
378 """ Returns the list of nodes of a DomElement. """
380 for t
in DomElement
.getElementsByTagName("nd"):
381 result
.append(int(int(t
.attributes
["ref"].value
)))
384 def _DomGetMember(self
, DomElement
):
385 """ Returns a list of relation members. """
387 for m
in DomElement
.getElementsByTagName("member"):
388 result
.append(self
._DomGetAttributes
(m
))
391 def _DomParseNode(self
, DomElement
):
392 """ Returns NodeData for the node. """
393 result
= self
._DomGetAttributes
(DomElement
)
394 result
[u
"tag"] = self
._DomGetTag
(DomElement
)
397 def _DomParseWay(self
, DomElement
):
398 """ Returns WayData for the way. """
399 result
= self
._DomGetAttributes
(DomElement
)
400 result
[u
"tag"] = self
._DomGetTag
(DomElement
)
401 result
[u
"nd"] = self
._DomGetNd
(DomElement
)
404 def _DomParseRelation(self
, DomElement
):
405 """ Returns RelationData for the relation. """
406 result
= self
._DomGetAttributes
(DomElement
)
407 result
[u
"tag"] = self
._DomGetTag
(DomElement
)
408 result
[u
"member"] = self
._DomGetMember
(DomElement
)
411 def _DomParseChangeset(self
, DomElement
):
412 """ Returns ChangesetData for the changeset. """
413 result
= self
._DomGetAttributes
(DomElement
)
414 result
[u
"tag"] = self
._DomGetTag
(DomElement
)
417 #######################################################################
418 # Internal xml builder #
419 #######################################################################
421 def _XmlBuild(self
, ElementType
, ElementData
):
424 xml
+= u
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
425 xml
+= u
"<osm version=\"0.6\" generator=\"" + self
._created
_by
+ "\">\n"
427 # <element attr="val">
428 xml
+= u
" <" + ElementType
429 if u
"id" in ElementData
:
430 xml
+= u
" id=\"" + str(ElementData
[u
"id"]) + u
"\""
431 if u
"lat" in ElementData
:
432 xml
+= u
" lat=\"" + str(ElementData
[u
"lat"]) + u
"\""
433 if u
"lon" in ElementData
:
434 xml
+= u
" lon=\"" + str(ElementData
[u
"lon"]) + u
"\""
435 if u
"version" in ElementData
:
436 xml
+= u
" version=\"" + str(ElementData
[u
"version"]) + u
"\""
437 xml
+= u
" visible=\"" + str(ElementData
.get(u
"visible", True)).lower() + u
"\""
438 if ElementType
in [u
"node", u
"way", u
"relation"]:
439 xml
+= u
" changeset=\"" + str(self
._CurrentChangesetId
) + u
"\""
443 for k
, v
in ElementData
.get(u
"tag", {}).items():
444 xml
+= u
" <tag k=\""+self
._XmlEncode
(k
)+u
"\" v=\""+self
._XmlEncode
(v
)+u
"\"/>\n"
447 for member
in ElementData
.get(u
"member", []):
448 xml
+= u
" <member type=\""+member
[u
"type"]+"\" ref=\""+str(member
[u
"ref"])+u
"\" role=\""+self
._XmlEncode
(member
[u
"role"])+"\"/>\n"
451 for ref
in ElementData
.get(u
"nd", []):
452 xml
+= u
" <nd ref=\""+str(ref
)+u
"\"/>\n"
455 xml
+= u
" </" + ElementType
+ u
">\n"
461 def _XmlEncode(self
, text
):
462 return text
.replace("&", "&").replace("\"", """)
464 #######################################################################
465 # End of class OsmApi #
466 #######################################################################
468 if __name__
== "__main__":
470 z
= OsmApi(api
="api06.dev.openstreetmap.org")
474 c1
= {u
"note": u
"Python OsmApi tests"}
475 print "ChangesetCreate : " + str(c1
)
476 c1
= z
.ChangesetCreate(c1
)
477 print " => " + str(c1
)
481 n1
= {u
"lat":1, u
"lon":1, u
"tag":{u
"name":u
"Etienne Chové"}}
482 print "NodeCreate : " + str(n1
)
483 n1
= z
.NodeCreate(n1
)
484 print " => " + str(n1
)
486 print "NodeGet : " + str(n1
[u
"id"])
487 n1
= z
.NodeGet(n1
[u
"id"])
488 print " => " + str(n1
)
490 n1
[u
"tag"][u
"note"] = u
"This is a test"
491 print "NodeUpdate : " + str(n1
)
492 n1
= z
.NodeUpdate(n1
)
493 print " => " + str(n1
)
495 print "NodeDelete : " + str(n1
)
496 n1
= z
.NodeDelete(n1
)
497 print " => " + str(n1
)
503 n1
= {u
"lat":1, u
"lon":1, u
"tag":{u
"name":u
"node 1"}}
504 n2
= {u
"lat":1.1, u
"lon":1.1, u
"tag":{u
"name":u
"node 2"}}
505 n1
= z
.NodeCreate(n1
)
506 n2
= z
.NodeCreate(n2
)
507 w1
= {u
"nd":[n1
[u
"id"], n2
[u
"id"]], u
"tag":{}}
509 print "WayCreate : " + str(w1
)
511 print " => " + str(w1
)
513 print "WayGet : " + str(w1
[u
"id"])
514 w1
= z
.WayGet(w1
[u
"id"])
515 print " => " + str(w1
)
517 w1
[u
"nd"].append(n1
[u
"id"])
518 print "WayUpdate : " + str(w1
)
520 print " => " + str(w1
)
522 print "WayDelete : " + str(w1
)
524 print " => " + str(w1
)
533 n1
= {u
"lat":1, u
"lon":1, u
"tag":{u
"name":u
"node 1"}}
534 n2
= {u
"lat":1.1, u
"lon":1.1, u
"tag":{u
"name":u
"node 2"}}
535 n1
= z
.NodeCreate(n1
)
536 n2
= z
.NodeCreate(n2
)
537 r1
= {u
"member":[{u
"type": u
"node", u
"ref": n1
[u
"id"], u
"role": u
"role1"}], u
"tag":{}}
539 print "RelationCreate : " + str(r1
)
540 r1
= z
.RelationCreate(r1
)
541 print " => " + str(r1
)
543 print "RelationGet : " + str(r1
[u
"id"])
544 r1
= z
.RelationGet(r1
[u
"id"])
545 print " => " + str(r1
)
547 r1
[u
"member"].append({u
"type": u
"node", u
"ref": n2
[u
"id"], u
"role": u
""})
548 print "RelationUpdate : " + str(w1
)
549 r1
= z
.RelationUpdate(r1
)
550 print " => " + str(r1
)
552 print "RelationDelete : " + str(r1
)
553 r1
= z
.RelationDelete(r1
)
554 print " => " + str(r1
)
560 print "ChangesetClose"
561 c1
= z
.ChangesetClose()
562 print " => " + str(c1
)