2 # -*- coding: utf-8 -*-
6 from httplib2 import Http
10 from PIL import Image, ImageDraw
11 from StringIO import StringIO
12 from geohash import encode
13 from geographiclib.geodesic import Geodesic
16 EC = 40075016.686 # meter
18 ER = 6372798.2 # meter
19 # Availible zoom levels in OSM
20 ZOOMRANGE = range(1, 18)
24 DB_NS="http://docbook.org/ns/docbook"
26 XI_NS="http://www.w3.org/2001/XInclude"
28 XLINK_NS="http://www.w3.org/1999/xlink"
29 XLINK = "{%s}" % XLINK_NS
30 HTML_NS="http://www.w3.org/1999/xhtml"
31 HTML = "{%s}" % HTML_NS
32 NSMAP = {None : DB_NS,
39 def __init__(self, lat, lon):
40 self.latitude = float(lat)
41 self.longitude = float(lon)
45 return "http://www.openstreetmap.org/?mlat=%s&mlon=%s&zoom=18&layers=M"\
46 % (self.latitude,self.longitude)
51 mnt,sec = divmod(ns*3600,60)
52 deg,mnt = divmod(mnt,60)
53 out = u'''%d°%2d'%5.2f"%s''' % ( deg,mnt,sec,'N')
54 mnt,sec = divmod(ew*3600,60)
55 deg,mnt = divmod(mnt,60)
56 out += u''' %d°%2d'%05.2f"%s''' % ( deg,mnt,sec,'E')
59 def lontile(lon, zoom):
60 tile = ((lon + 180) / 360) * (2**zoom)
63 def lattile(lat, zoom):
64 tile = (1 - log(tan(lat * pi/180) + 1 / cos(lat* pi/180)) / pi) /2 * 2**zoom
65 # tile = (1-log(tan(radians(lat))+1/cos(radians(lat)))/pi)/2*2**zoom
68 def coordtile(coord, zoom):
69 x = lontile(coord[1],zoom)
70 y = lattile(coord[0],zoom)
74 return 'http://tile.openstreetmap.org/%s/%s/%s.png' % (z,x,y)
77 (x, y) = coordtile(coord,zoom)
82 def offset(coord, zoom):
83 (x, y) = coordtile(coord,zoom)
84 xo = int(floor((x-floor(x))*TS))
85 yo = int(floor((y-floor(y))*TS))
88 def png(self,zoom=15,size=(TS,TS)):
89 filename = encode(self.latitude, self.longitude)+'.png'
90 if path.isfile(filename):
91 if path.getctime(filename) > time.time() - 60*60*24*2:
93 im = Image.new("RGB", size, None)
94 center = (size[0]/2, size[1]/2)
95 nst = ceil(center[0]-TS/2)/TS
96 ewt = ceil(center[1]-TS/2)/TS
97 x, y = offset(co,zoom)
98 (ns, ew) = coordtile(co,zoom)
111 ul = (ns-nst-n, ew-ewt-e)
112 lr = (ns+nst+s, ew+ewt+w)
113 lattiles = range(int(ul[0]),int(lr[0])+1)
114 lontiles = range(int(ul[1]),int(lr[1])+1)
116 size = (len(lontiles)*TS,len(lattiles)*TS)
117 grid = Image.new("RGB", size, None)
118 for yi, y in enumerate(lattiles):
119 for xi, x in enumerate(lontiles):
122 request, content = h.request(url)
123 img = Image.open(StringIO(content))
126 t = coordtile(co,zoom)
128 yp = [i for i,x in enumerate(lattiles) if x == int(t[0])][0]*TS+o[1]
129 xp = [i for i,x in enumerate(lontiles) if x == int(t[1])][0]*TS+o[0]
131 gridc = grid.crop((xp-TS/2,yp-TS/2,xp+TS/2,yp+TS/2))
136 uri = etree.Element(DB+'uri',nsmap=NSMAP)
137 ln = etree.SubElement(uri, DB+'link')
138 ln.set(XLINK+'href',self.osmlink())
139 imo = etree.SubElement(ln, DB+'inlinemediaobject')
140 io = etree.SubElement(imo, DB+'imageobject', condition="web")
141 idat = etree.SubElement(io , DB+'imagedata',
142 fileref=encode(self.latitude, self.longitude)+'.png',
144 to = etree.SubElement(imo, DB+'textobject')
145 ph = etree.SubElement(to, DB+'phrase')
146 ph.text = "geo:"+str(self.latitude)+","+str(self.longitude)
147 para = etree.SubElement(ln, DB+'para')
148 para.text = self.dms()
151 class Address(object):
152 """Address object to contain everything known about an address"""
153 def __init__(self,address):
154 self._address_string = address
155 self._root = etree.Element(DB+'address',nsmap=NSMAP)
158 def geocode(self,country=None):
159 params = { 'q': self._address_string,
166 params['countrycodes'] = country
168 base_url = 'http://nominatim.openstreetmap.org/search?%s'
169 url = base_url % urllib.urlencode(params)
170 resp, content = h.request(url)
171 root = etree.fromstring(content)
172 place = root.find("place")
173 if place is not None:
174 print (etree.tostring(root, pretty_print=True))
175 self._coord=Coord(place.get("lat"),place.get("lon"))
183 return self._coord.db_xml()
186 def distance(p1, p2):
187 res = Geodesic.WGS84.Inverse(p1[0], p1[1], p2[0], p2[1])
190 def mark(image, coord):
191 draw = ImageDraw.Draw(image)
194 bbox = (x-r, y-r, x+r, y+r)
195 draw.ellipse(bbox, outline="red")
197 def mapimages(coords, zoom=15,size=(TS,TS)):
203 minlat = min(minlat,c[0])
204 maxlat = max(maxlat,c[0])
205 minlon = min(minlon,c[1])
206 maxlon = max(maxlon,c[1])
207 # Find minimal bounding box and expand it 5%
208 hyp = distance((maxlat,minlon),(minlat,maxlon))
210 uld = Geodesic.WGS84.Direct(maxlat, minlon, 315, hyp)
211 urd = Geodesic.WGS84.Direct(maxlat, maxlon, 45, hyp)
212 lld = Geodesic.WGS84.Direct(minlat, minlon, 225, hyp)
213 lrd = Geodesic.WGS84.Direct(minlat, maxlon, 135, hyp)
215 ul = (uld['lat2'],uld['lon2'])
216 ur = (urd['lat2'],urd['lon2'])
217 ll = (lld['lat2'],lld['lon2'])
218 lr = (lrd['lat2'],lrd['lon2'])
219 top = distance(ul,ur)
220 bottom = distance(ll,lr)
221 left = distance(ul,ll)
222 right = distance(ur,lr)
223 # m_per_pix = EC*abs(cos(lat))/2**(zoomlevel+8)
224 # m_per_pix = ER*2*pi*abs(cos(lat))/2**(zoomlevel+8)
225 for zoom in range(1,18):
226 t = 2**(zoom)*TS/(ER*2*pi*abs(cos(ul[0])))
229 b = 2**(zoom)*TS/(ER*2*pi*abs(cos(ll[0])))
232 # print "Zoom: %s : %s %s %s %s" % (zoom, toppix, bottompix, leftpix, rightpix)
233 if max(toppix,bottompix,leftpix,rightpix) > TS*4:
235 # print "Zoom to use : %s" % (zoom)
236 ult = coordtile(ul,zoom)
237 lrt = coordtile(lr,zoom)
238 lattiles = range(int(ult[0]),int(lrt[0])+1)
239 lontiles = range(int(ult[1]),int(lrt[1])+1)
240 size = (len(lontiles)*TS,len(lattiles)*TS)
241 grid = Image.new("RGB", size, None)
243 for yi, y in enumerate(lattiles):
244 for xi, x in enumerate(lontiles):
248 request, content = h.request(url)
249 img = Image.open(StringIO(content))
253 t = coordtile(c,zoom)
255 yp = [i for i,x in enumerate(lattiles) if x == int(t[0])][0]*TS+o[1]
256 xp = [i for i,x in enumerate(lontiles) if x == int(t[1])][0]*TS+o[0]
259 t = coordtile(ul,zoom)
261 yp = [i for i,x in enumerate(lattiles) if x == int(t[0])][0]*TS+o[1]
262 xp = [i for i,x in enumerate(lontiles) if x == int(t[1])][0]*TS+o[0]
263 t = coordtile(lr,zoom)
265 ypl = [i for i,x in enumerate(lattiles) if x == int(t[0])][0]*TS+o[1]
266 xpl = [i for i,x in enumerate(lontiles) if x == int(t[1])][0]*TS+o[0]
267 gridc = grid.crop((xp,yp,xpl,ypl))
269 # gridc.save("cap-un.png")