size: printing size of style
[treecutter.git] / xinclude / address.py
index fd5fe9664d7d30ff031343129ba796c45c251709..6a30d7accc13006f17d337ff30ddc6a2c86f8c18 100755 (executable)
@@ -2,15 +2,18 @@
 # -*- coding: utf-8 -*-
 
 import time
+import sys
 from os import path
 from httplib2 import Http
-import urllib
-from math import *
+from urllib import urlencode
+from math import log,tan,pi,cos,radians,ceil,floor
 from lxml import etree
+from lxml.builder import ElementMaker
 from PIL import Image, ImageDraw
 from StringIO import StringIO
 from geohash import encode
 from geographiclib.geodesic import Geodesic
+from treecutter import constants as const
 
 # EC Equator lenght
 EC = 40075016.686 # meter
@@ -21,140 +24,216 @@ ZOOMRANGE = range(1, 18)
 # tile size
 TS = 256
 
-
 h = Http(".cache")
 
-def lontile(lon, zoom):
-    tile = ((lon + 180) / 360) * (2**zoom)
-    return tile
-
-def lattile(lat, zoom):
-    tile = (1 - log(tan(lat * pi/180) + 1 / cos(lat* pi/180)) / pi) /2 * 2**zoom
-#    tile = (1-log(tan(radians(lat))+1/cos(radians(lat)))/pi)/2*2**zoom
-    return tile
-
-def coordtile(coord, zoom):
-    x = lontile(coord[1],zoom)
-    y = lattile(coord[0],zoom)
-    return (y,x)
-
-def tile(x,y,z):
-    return 'http://tile.openstreetmap.org/%s/%s/%s.png' % (z,x,y)
-
-def link(coord,zoom):
-    z = zoom
-    x = int(floor(lontile(coord[1],z)))
-    y = int(floor(lattile(coord[0],z)))
-    return tile(x,y,z)
-
-def offset(coord, zoom):
-    z = zoom
-    x = lontile(coord[1],z)
-    y = lattile(coord[0],z)
-    xo = int(floor((x-floor(x))*TS))
-    yo = int(floor((y-floor(y))*TS))
-    return (xo, yo)
+class Coord(object):
+    def __init__(self, lat, lon):
+        self.latitude  = float(lat)
+        self.longitude = float(lon)
+        self.image = None
 
-def distance(p1, p2):
-    res = Geodesic.WGS84.Inverse(p1[0], p1[1], p2[0], p2[1])
-    return res['s12']
+    def osmlink(self):
+        return "http://www.openstreetmap.org/?mlat=%s&mlon=%s&zoom=18&layers=M"\
+            % (self.latitude,self.longitude)
 
-def geocode(address,country=None):
-    params = { 'q': address,
-               'addressdetails': 1,
-               'limit': 1,
-               'format': 'xml',
-               'polygon': 0 }
-    time.sleep(1)
-    if country:
-        params['countrycodes'] = country
-
-    base_url = 'http://nominatim.openstreetmap.org/search?%s'
-    url = base_url % urllib.urlencode(params)
-    resp, content = h.request(url)
-#    print resp
-#    print content
-    root = etree.fromstring(content)
-    etree.tostring(root)
-#    print "%s" % (address)
-    lat = None
-    lon = None
-    for element in root.iter("place"):
-        lat = element.get("lat")
-        lon = element.get("lon")
-#        print(" : %s,%s" % (lat, lon))
-        break
-    if not lat or not lon:
-        print resp
-        print content
-    return (float(lat),float(lon))
+    def dms(self):
+        ns = self.latitude
+        ew = self.longitude
+        mnt,sec = divmod(ns*3600,60)
+        deg,mnt = divmod(mnt,60)
+        out = u'''%d°%2d'%5.2f"%s''' % ( deg,mnt,sec,'N')
+        mnt,sec = divmod(ew*3600,60)
+        deg,mnt = divmod(mnt,60)
+        out +=  u''' %d°%2d'%05.2f"%s''' % ( deg,mnt,sec,'E')
+        return out
 
-def mark(image, coord):
-    draw = ImageDraw.Draw(image)
-    x, y = coord
-    r = 5
-    bbox = (x-r, y-r, x+r, y+r)
-    draw.ellipse(bbox, outline="red")
+    def lontile(self, zoom):
+        tile = ((self.longitude + 180) / 360) * (2**zoom)
+        return tile
 
-def mapimage(coords, zoom=15,size=(TS,TS)):
-    if len(coords) == 1:
-        co = coords[0]
-        filename = encode(co[0],co[1])+'.png'
-        if path.isfile(filename):
-            if path.getctime(filename) > time.time() - 60*60*24*2:
-                return
+    def lattile(self, zoom):
+        rad = radians(self.latitude)
+        tile = (1-log(tan(rad)+1/cos(rad))/pi)/2*2**zoom
+        return tile
+
+    def offset(self,zoom):
+        x = self.lontile(zoom)
+        y = self.lattile(zoom)
+        xo = int(floor((x-floor(x))*TS))
+        yo = int(floor((y-floor(y))*TS))
+        return (xo, yo)
+
+    def distance(self, point):
+        res = Geodesic.WGS84.Inverse(self.latitude, self.longitude,
+                                     point.latitude, point.longitude)
+        return res['s12']
+
+    def direct(self, direction, lenght):
+        point = Geodesic.WGS84.Direct(self.latitude, self.longitude,
+                                      direction, length)
+        return self.__class__(point['lat2'],point['lon2'])
+
+    def png(self,zoom=15,size=(400,150)):
+        filename = encode(self.latitude, self.longitude)+'.png'
+#        if path.isfile(filename):
+#            if path.getctime(filename) > time.time() - 60*60*24*2:
+#                return
         im = Image.new("RGB", size, None)
-        center = (size[0]/2, size[1]/2)
-        nst = ceil(center[0]-TS/2)/TS
-        ewt = ceil(center[1]-TS/2)/TS
-        x, y = offset(co,zoom)
-        (ns, ew) = coordtile(co,zoom)
-        if x < TS/2:
-            e = 1
-            w = 0
-        if x > TS/2:
-            e = 0
-            w = 1
-        if y < TS/2:
-            n = 1
-            s = 0
-        if y > TS/2:
-            n = 0
-            s = 1
-        ul = (ns-nst-n, ew-ewt-e)
-        lr = (ns+nst+s, ew+ewt+w)
-        lattiles = range(int(ul[0]),int(lr[0])+1)
-        lontiles =  range(int(ul[1]),int(lr[1])+1)
-#        url = link(c,zoom)
-        size = (len(lontiles)*TS,len(lattiles)*TS)
-        grid = Image.new("RGB", size, None)
-        img = []
+
+        ew = int(self.lontile(zoom))
+        ns = int(self.lattile(zoom))
+
+        (xo, yo) = self.offset(zoom)
+        et = int(floor((xo - ceil(size[0]/2))/TS))
+        nt = int(floor((yo - ceil(size[1]/2))/TS))
+        wt = int(floor((xo + ceil(size[0]/2))/TS))
+        st = int(floor((yo + ceil(size[1]/2))/TS))
+
+        lontiles =  range(ew+et,ew+wt+1)
+        lattiles = range(ns+nt,ns+st+1)
+        imsize = (len(lontiles)*TS,len(lattiles)*TS)
+        grid = Image.new("RGB", imsize, None)
         for yi, y in enumerate(lattiles):
             for xi, x in enumerate(lontiles):
-                url = tile(x,y,zoom)
-#                print url
-                time.sleep(1)
+                url = 'http://tile.openstreetmap.org/%s/%s/%s.png' % (zoom,x,y)
                 request, content = h.request(url)
                 img = Image.open(StringIO(content))
+#                dr = ImageDraw.Draw(img)
+#                dr.rectangle([0,0,TS,TS], outline=0)
                 box = (xi*TS, yi*TS)
                 grid.paste(img, box)
-        t = coordtile(co,zoom)
-        o = offset(co,zoom)
-        yp = [i for i,x in enumerate(lattiles) if x == int(t[0])][0]*TS+o[1]
-        xp = [i for i,x in enumerate(lontiles) if x == int(t[1])][0]*TS+o[0]
+
+        yp = [i for i,j in enumerate(lattiles) if j == int(ns)][0]*TS+yo
+        xp = [i for i,j in enumerate(lontiles) if j == int(ew)][0]*TS+xo
         mark(grid, (xp,yp))
-        gridc = grid.crop((xp-TS/2,yp-TS/2,xp+TS/2,yp+TS/2))
+        xc = int(ceil(size[0]/2))
+        yc = int(ceil(size[1]/2))
+
+#        draw = ImageDraw.Draw(grid)
+#        draw.rectangle([xp-xc,yp-yc,xp+xc,yp+yc], outline="red")
+        gridc = grid.crop((xp-xc,yp-yc,xp+xc,yp+yc))
         gridc.save(filename)
-    else:
-        minlat = 1000
-        maxlat = 0
-        minlon = 1000
-        maxlon = 0
-        for c in coords:
-            minlat = min(minlat,c[0])
-            maxlat = max(maxlat,c[0])
-            minlon = min(minlon,c[1])
-            maxlon = max(maxlon,c[1])
+
+    def db_xml(self):
+        self.png()
+        img = encode(self.latitude, self.longitude)+'.png'
+        phr = "geo:"+str(self.latitude)+","+str(self.longitude)
+
+        db = ElementMaker(namespace=const.DB_NS, nsmap=const.NSMAP)
+        uri = db.uri(db.link(
+                db.inlinemediaobject(
+                    db.imageobject(db.imagedata(
+                            fileref=img,
+                            format='PNG')),
+                    db.textobject(db.phrase(phr))
+                    ),
+                self.dms(),
+                **{const.XLINK+"href": self.osmlink()}))
+        return uri
+
+def c(s):
+    return s or ''
+
+def s(s,n):
+    if n is not None:
+        return s or n.text
+    return s
+
+
+class Address(object):
+    """Address object to contain everything known about an address"""
+    def __init__(self,street=None,postcode=None,city=None,country=None):
+        self.street = street
+        self.postcode = postcode
+        self.city = city
+        self.country = country
+        self.country_code = None
+        self.phone = None
+        self.name = None
+        self.coord = None
+
+
+    def geocode(self, language='en'):
+        base_url = 'http://nominatim.openstreetmap.org/search?%s'
+        params = { 'addressdetails': 1,
+                   'limit': 1,
+                   'format': 'xml',
+                   'polygon': 0,
+                   'accept-language': language}
+
+        if self.country:
+            t = etree.parse('/usr/share/xml/iso-codes/iso_3166.xml')
+            r = t.xpath('//iso_3166_entry[@name="'+self.country+'"]')
+            if len(r)==1:
+                self.country_code = r[0].get("alpha_2_code")
+        if self.country_code:
+            params['countrycodes'] = self.country_code
+
+        addrlist=[]
+        addrlist.append(u''+c(self.name)+', '+c(self.street)+', '+c(self.city))
+        addrlist.append(u''+c(self.street)+', '+c(self.postcode)+', '+c(self.city))
+        addrlist.append(u''+c(self.street)+', '+c(self.city))
+
+        for addr in addrlist:
+            params['q'] = addr.encode('utf-8')
+            url = base_url % urlencode(params)
+            time.sleep(1)
+            resp, content = h.request(url)
+            root = etree.fromstring(content)
+            places = int(root.xpath('count(//place[@place_id])'))
+            if places == 1:
+                place = root.find("place")
+#                print etree.tostring(place,encoding='UTF-8',pretty_print=True)
+                self.postcode = s(self.postcode,place.find("postcode"))
+                self.city = s(self.city,place.find("city"))
+                self.country = s(self.country,place.find("country"))
+                self.country_code = s(self.country_code,place.find("country_code"))
+                self.coord=Coord(place.get("lat"),place.get("lon"))
+                return
+
+    def add_phone(self, phone):
+        self.phone = phone
+
+    def set_name(self, name):
+        self.name = name
+
+    def db_xml(self):
+        db = ElementMaker(namespace=const.DB_NS, nsmap=const.NSMAP)
+        co = ''
+        if self.coord:
+            co = self.coord.db_xml()
+        adr = db.address(db.street(c(self.street)),
+                         db.postcode(c(self.postcode)),
+                         db.city(c(self.city)),
+                         db.country(c(self.country)),
+                         db.phone(c(self.phone)),
+                         co)
+#                       type=self.type,
+        return adr
+
+
+def distance(p1, p2):
+    res = Geodesic.WGS84.Inverse(p1[0], p1[1], p2[0], p2[1])
+    return res['s12']
+
+def mark(image, coord):
+    draw = ImageDraw.Draw(image)
+    x, y = coord
+    r = 5
+    bbox = (x-r, y-r, x+r, y+r)
+    draw.ellipse(bbox, outline="red")
+
+def box(image,box):
+    draw = ImageDraw.Draw(image)
+    draw.rectangle(box, outline="red")
+
+def mapimages(coords, zoom=15,size=(TS,TS)):
+
+        minlat = min(coords,key=attrgetter('latitude'))
+        maxlat = max(coords,key=attrgetter('latitude'))
+        minlon = min(coords,key=attrgetter('longitude'))
+        maxlon = max(coords,key=attrgetter('longitude'))
+
         # Find minimal bounding box and expand it 5%
         hyp = distance((maxlat,minlon),(minlat,maxlon))
         hyp = hyp*0.05
@@ -163,6 +242,8 @@ def mapimage(coords, zoom=15,size=(TS,TS)):
         lld =  Geodesic.WGS84.Direct(minlat, minlon, 225, hyp)
         lrd =  Geodesic.WGS84.Direct(minlat, maxlon, 135, hyp)
 
+        ul = Coord(maxlat,minlon).direct(315, hyp)
+
         ul = (uld['lat2'],uld['lon2'])
         ur = (urd['lat2'],urd['lon2'])
         ll = (lld['lat2'],lld['lon2'])
@@ -218,3 +299,24 @@ def mapimage(coords, zoom=15,size=(TS,TS)):
         gridc = grid.crop((xp,yp,xpl,ypl))
         gridc.show()
 #        gridc.save("cap-un.png")
+
+if __name__ == "__main__":
+    for arg in sys.argv[1:]:
+        al = arg.split("=")
+        if al[0] == "lang":
+            lang = al[1]
+        if al[0] == "xptr":
+            argument = al[1].decode('utf-8')
+
+    addrlist = argument.split(',')
+    addrfmt = "street,postcode,city,country"
+    adict = addrfmt.split(',')
+    argdict = dict(zip(adict,addrlist))
+    addr = Address(**argdict)
+    addr.geocode()
+    axml = addr.db_xml()
+#    clean_db(axml)
+
+    #print(etree.tostring(cxml, pretty_print=True))
+    #sys.stdout.write(out.encode('utf-8'))
+    sys.stdout.write(etree.tostring(axml,encoding='UTF-8',pretty_print=False))