Objectified Coord and Address methods, made it main callable
authorFredrik Unger <fred@tree.se>
Fri, 9 Nov 2012 10:39:55 +0000 (11:39 +0100)
committerFredrik Unger <fred@tree.se>
Fri, 9 Nov 2012 10:39:55 +0000 (11:39 +0100)
Rewrote the classes to use internal data for their methods, fixed sizing
of the png images (made the size really settable).
Geocoding does try several times with different strings before failing.
Address is also now directly callable for simple addresses, without
contact.

xinclude/address.py

index 8ce6a6a7b77c581068cb8974afdd21c18b5a5357..c99f51ad0e86bca424f21a1d613f77a7d26e268b 100755 (executable)
@@ -2,10 +2,11 @@
 # -*- 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
@@ -46,31 +47,18 @@ class Coord(object):
         out +=  u''' %d°%2d'%05.2f"%s''' % ( deg,mnt,sec,'E')
         return out
 
-    def lontile(lon, zoom):
-        tile = ((lon + 180) / 360) * (2**zoom)
+    def lontile(self, zoom):
+        tile = ((self.longitude + 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
+    def lattile(self, zoom):
+        rad = radians(self.latitude)
+        tile = (1-log(tan(rad)+1/cos(rad))/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):
-        (x, y) = coordtile(coord,zoom)
-        x = int(floor(x))
-        y = int(floor(y))
-        return tile(x,y,zoom)
-
-    def offset(coord, zoom):
-        (x, y) = coordtile(coord,zoom)
+    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)
@@ -80,53 +68,54 @@ class Coord(object):
                                      point.latitude, point.longitude)
         return res['s12']
 
-    def png(self,zoom=15,size=(TS,TS)):
+    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
+#        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)
+
+        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)
-                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)
 
     def db_xml(self):
+        self.png()
         img = encode(self.latitude, self.longitude)+'.png'
         phr = "geo:"+str(self.latitude)+","+str(self.longitude)
 
@@ -135,46 +124,75 @@ class Coord(object):
                 db.inlinemediaobject(
                     db.imageobject(db.imagedata(
                             fileref=img,
-                            format='PNG'))
+                            format='PNG')),
                     db.textobject(db.phrase(phr))
                     ),
-                db.para(self.dms())
+                self.dms(),
                 **{const.XLINK+"href": self.osmlink()}))
         return uri
 
 
 class Address(object):
     """Address object to contain everything known about an address"""
-    def __init__(self,address):
-        self._address_string = address
-        self._coord = None
+    def __init__(self,street=None,postcode=None,city=None,country=None):
+        self.name = None
+        self.street = street
+        self.postcode = postcode
+        self.city = city
+        self.country = country
+        self.phone = ''
+        self.coord = None
 
-    def geocode(self,country=None):
-        params = { 'q': self._address_string,
-                   'addressdetails': 1,
+    def geocode(self):
+        base_url = 'http://nominatim.openstreetmap.org/search?%s'
+        params = { 'addressdetails': 1,
                    'limit': 1,
                    'format': 'xml',
                    'polygon': 0 }
 
-        if country:
-            params['countrycodes'] = country
+        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:
+                params['countrycodes'] = r[0].get("alpha_2_code")
 
-        base_url = 'http://nominatim.openstreetmap.org/search?%s'
-        url = base_url % urllib.urlencode(params)
-        resp, content = h.request(url)
-        root = etree.fromstring(content)
-        place = root.find("place")
-        if place is not None:
-            print (etree.tostring(root, pretty_print=True))
-            self._coord=Coord(place.get("lat"),place.get("lon"))
-            return 1
-        else:
-            print resp
-            print content
-            return 0
+        addrlist=[]
+        if self.name and len(self.name)>0:
+            addrlist.append(u''+self.name+', '+self.street+', '+self.city)
+        addrlist.append(u''+self.street+', '+self.postcode+', '+self.city)
+        addrlist.append(u''+self.street+', '+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")
+                self.coord=Coord(place.get("lat"),place.get("lon"))
+                return
+        sys.stderr.write(u'FAILURE: Did not find:\n')
+        sys.stderr.write(addrlist[0].encode('utf-8'))
+        sys.stderr.write(url)
+
+    def add_phone(self, phone):
+        self.phone = phone
+
+    def set_name(self, name):
+        self.name = name
 
     def db_xml(self):
-        return self._coord.db_xml()
+        db = ElementMaker(namespace=const.DB_NS, nsmap=const.NSMAP)
+        adr = db.address(db.street(self.street),
+                         db.postcode(self.postcode),
+                         db.city(self.city),
+                         db.country(self.country),
+                         db.phone(self.phone),
+                         self.coord.db_xml())
+#                       type=self.type,
+        return adr
 
 
 def distance(p1, p2):
@@ -261,3 +279,24 @@ def mapimages(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))