3d60b10469ec1652302504d81fb26fffeb419719
[treecutter.git] / xinclude / address.py
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 import time
5 from os import path
6 from httplib2 import Http
7 import urllib
8 from math import *
9 from lxml import etree
10 from PIL import Image, ImageDraw
11 from StringIO import StringIO
12 from geohash import encode
13 from geographiclib.geodesic import Geodesic
14
15 # EC Equator lenght
16 EC = 40075016.686 # meter
17 # ER Earth radius
18 ER = 6372798.2 # meter
19 # Availible zoom levels in OSM
20 ZOOMRANGE = range(1, 18)
21 # tile size
22 TS = 256
23
24 DB_NS="http://docbook.org/ns/docbook"
25 DB = "{%s}" % DB_NS
26 XI_NS="http://www.w3.org/2001/XInclude"
27 XI = "{%s}" % XI_NS
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,
33          'xlink' : XLINK_NS}
34
35
36 h = Http(".cache")
37
38 class Coord(object):
39     def __init__(self, lat, lon):
40         self.latitude  = float(lat)
41         self.longitude = float(lon)
42         self.image = None
43
44     def osmlink(self):
45         return "http://www.openstreetmap.org/?mlat=%s&mlon=%s&zoom=18&layers=M"\
46             % (self.latitude,self.longitude)
47
48     def dms(self):
49         ns = self.latitude
50         ew = 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')
57         return out
58
59     def lontile(lon, zoom):
60         tile = ((lon + 180) / 360) * (2**zoom)
61         return tile
62
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
66         return tile
67
68     def coordtile(coord, zoom):
69         x = lontile(coord[1],zoom)
70         y = lattile(coord[0],zoom)
71         return (y,x)
72
73     def tile(x,y,z):
74         return 'http://tile.openstreetmap.org/%s/%s/%s.png' % (z,x,y)
75
76     def link(coord,zoom):
77         (x, y) = coordtile(coord,zoom)
78         x = int(floor(x))
79         y = int(floor(y))
80         return tile(x,y,zoom)
81
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))
86         return (xo, yo)
87
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:
92                 return
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)
99         if x < TS/2:
100             e = 1
101             w = 0
102         if x > TS/2:
103             e = 0
104             w = 1
105         if y < TS/2:
106             n = 1
107             s = 0
108         if y > TS/2:
109             n = 0
110             s = 1
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)
115 #        url = link(c,zoom)
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):
120                 url = tile(x,y,zoom)
121                 time.sleep(1)
122                 request, content = h.request(url)
123                 img = Image.open(StringIO(content))
124                 box = (xi*TS, yi*TS)
125                 grid.paste(img, box)
126         t = coordtile(co,zoom)
127         o = offset(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]
130         mark(grid, (xp,yp))
131         gridc = grid.crop((xp-TS/2,yp-TS/2,xp+TS/2,yp+TS/2))
132         gridc.save(filename)
133
134
135     def db_xml(self):
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',
143                                 format='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()
149         return uri
150
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)
156         self._coord = None
157
158     def geocode(self,country=None):
159         params = { 'q': self._address_string,
160                    'addressdetails': 1,
161                    'limit': 1,
162                    'format': 'xml',
163                    'polygon': 0 }
164
165         if country:
166             params['countrycodes'] = country
167
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"))
176             return 1
177         else:
178             print resp
179             print content
180             return 0
181
182     def db_xml(self):
183         return self._coord.db_xml()
184
185
186 def distance(p1, p2):
187     res = Geodesic.WGS84.Inverse(p1[0], p1[1], p2[0], p2[1])
188     return res['s12']
189
190 def mark(image, coord):
191     draw = ImageDraw.Draw(image)
192     x, y = coord
193     r = 5
194     bbox = (x-r, y-r, x+r, y+r)
195     draw.ellipse(bbox, outline="red")
196
197 def mapimages(coords, zoom=15,size=(TS,TS)):
198         minlat = 1000
199         maxlat = 0
200         minlon = 1000
201         maxlon = 0
202         for c in coords:
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))
209         hyp = hyp*0.05
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)
214
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])))
227             toppix = t*top
228             leftpix = t*left
229             b = 2**(zoom)*TS/(ER*2*pi*abs(cos(ll[0])))
230             bottompix = b*bottom
231             rightpix  = b*right
232 #            print "Zoom: %s : %s %s %s %s" % (zoom, toppix, bottompix, leftpix, rightpix)
233             if max(toppix,bottompix,leftpix,rightpix) > TS*4:
234                 break
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)
242         img = []
243         for yi, y in enumerate(lattiles):
244             for xi, x in enumerate(lontiles):
245                 url = tile(x,y,zoom)
246 #                print url
247                 time.sleep(1)
248                 request, content = h.request(url)
249                 img = Image.open(StringIO(content))
250                 box = (xi*TS, yi*TS)
251                 grid.paste(img, box)
252         for c in coords:
253             t = coordtile(c,zoom)
254             o = offset(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]
257             mark(grid, (xp,yp))
258
259         t = coordtile(ul,zoom)
260         o = offset(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)
264         o = offset(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))
268         gridc.show()
269 #        gridc.save("cap-un.png")