From 676fd9d928d47c787b38ca66011de179ae034beb Mon Sep 17 00:00:00 2001 From: Fredrik Unger Date: Fri, 9 Nov 2012 11:39:55 +0100 Subject: [PATCH] Objectified Coord and Address methods, made it main callable 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 | 203 ++++++++++++++++++++++++++------------------ 1 file changed, 121 insertions(+), 82 deletions(-) diff --git a/xinclude/address.py b/xinclude/address.py index 8ce6a6a..c99f51a 100755 --- a/xinclude/address.py +++ b/xinclude/address.py @@ -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)) -- 2.30.2