def build_geometries(diff, srid): srid_txt = "EPSG:{}".format(srid) geom = {'line': [], 'point': [], 'polygon': []} for osm_id, node in diff['add']['node'].iteritems(): x, y = projections.from4326(node["geom"], srid_txt) geom["point"].append({"geom": "SRID={};POINT({} {})".format(srid, x, y), "osm_id": osm_id, "tags": node["tags"]}) for osm_id, way in diff['add']['way'].iteritems(): line = projections.from4326(way["geom"], srid_txt) polygonize = check_tags_if_polygon(way["tags"]) if polygonize: result, dangles, cuts, invalids = polygonize_full([line]) #print result, dangles, cuts, invalids for poly in result: geom["polygon"].append({"geom": "SRID={};{}".format(srid, poly.wkt), "osm_id": osm_id, "way_area":poly.area, "tags": way["tags"]}) for line in dangles: geom["line"].append({"geom": "SRID={};{}".format(srid, line.wkt), "osm_id": osm_id, "tags": way["tags"]}) for line in cuts: geom["line"].append({"geom": "SRID={};{}".format(srid, line.wkt), "osm_id": osm_id, "tags": way["tags"]}) for line in invalids: geom["line"].append({"geom": "SRID={};{}".format(srid, line.wkt), "osm_id": osm_id, "tags": way["tags"]}) else: ll = ",".join([a[0] + a[1] for a in line]) geom["line"].append({"geom": "SRID={};LINESTRING({})".format(srid, ll), "osm_id": osm_id, "tags": way["tags"]}) for osm_id, relation in diff['add']['relation'].iteritems(): lines = [projections.from4326(way, srid_txt) for way in relation["geom"]] polygonize = check_tags_if_polygon(relation["tags"]) if polygonize: merged = linemerge(lines) polys = [] lines = [] for line in merged: if line.is_ring: polys.append(line) else: lines.append(line) if polys: # TODO: repair geometry poly = Polygon(polys[0], polys[1:]) geom["polygon"].append({"geom": "SRID={};{}".format(srid, poly.wkt), "osm_id": -osm_id, "way_area":0, "tags": relation["tags"]}) for line in lines: geom["line"].append({"geom": "SRID={};{}".format(srid, line.wkt), "osm_id": -osm_id, "tags": relation["tags"]}) #result, dangles, cuts, invalids = polygonize_full(lines) ##print result, dangles, cuts, invalids #result = list(result) #sd = result[0] #for poly in result[1:]: #sd = sd.symmetric_difference(poly) #geom["polygon"].append({"geom": "SRID={};{}".format(srid, sd.wkt), "osm_id": -osm_id, "way_area":sd.area, "tags": relation["tags"]}) #for line in dangles: #geom["line"].append({"geom": "SRID={};{}".format(srid, line.wkt), "osm_id": -osm_id, "tags": relation["tags"]}) #for line in cuts: #geom["line"].append({"geom": "SRID={};{}".format(srid, line.wkt), "osm_id": -osm_id, "tags": relation["tags"]}) else: ll = ",".join([a[0] + a[1] for a in line]) geom["line"].append({"geom": "SRID={};LINESTRING({})".format(srid, ll), "osm_id": osm_id, "tags": relation["tags"]}) return geom
def expose_ev(self, widget, event): if(widget.allocation.width != self.width or widget.allocation.height != self.height ): #debug("Rrresize!") self.width = widget.allocation.width self.height = widget.allocation.height cr = widget.window.cairo_create() if self.old_center_coord != self.center_coord or self.old_zoom != self.zoom: #print "Recentered!" xy = projections.from4326(self.center_coord,"EPSG:3857") xy1 = projections.to4326((xy[0]-40075016.*(0.5**(self.zoom))/self.tiles.tilewidth*self.width, xy[1]-40075016.*(0.5**(self.zoom))/self.tiles.tileheight*self.height), "EPSG:3857") xy2 = projections.to4326((xy[0]+40075016.*(0.5**(self.zoom))/self.tiles.tilewidth*self.width, xy[1]+40075016.*(0.5**(self.zoom))/self.tiles.tileheight*self.height), "EPSG:3857") self.bbox = (xy1[0],xy1[1],xy2[0],xy2[1]) self.tilebox = projections.tile_by_bbox(self.bbox, self.zoom, "EPSG:3857") self.old_center_coord = self.center_coord self.old_zoom = self.zoom from_tile_x, from_tile_y, to_tile_x, to_tile_y = self.tilebox dx = 1.*(from_tile_x - int(from_tile_x))*self.tiles.tilewidth dy = 1.*(from_tile_y - int(from_tile_y))*self.tiles.tileheight print dx,dy #print self.dx, self.dy onscreen_tiles = set() for x in range (int(from_tile_x), int(to_tile_x)+1): for y in range (int(to_tile_y), int(from_tile_y)+1): onscreen_tiles.add((self.zoom,x,y)) self.tiles.onscreen = onscreen_tiles for z,x,y in onscreen_tiles: tile = self.tiles[(self.zoom,x,y)] #print dx+(x-from_tile_x)*self.tiles.tilewidth-self.width #print dy+(y-from_tile_y)*self.tiles.tileheight-self.height #cr.set_source_surface(tile, int(self.dx-dx+(x-int(from_tile_x))*self.tiles.tilewidth-self.width), int(self.dy-dy-(int(from_tile_y)-y)*self.tiles.tileheight+self.height)) cr.set_source_surface(tile, int(self.dx-dx+(x-int(from_tile_x))*self.tiles.tilewidth), int(self.dy-dy-(int(from_tile_y)-y)*self.tiles.tileheight+self.height)) cr.paint()
def update_surface_by_center(self, lonlat, zoom, style): self.zoom = zoom xy = projections.from4326(lonlat, self.proj) xy1 = projections.to4326((xy[0]-40075016*0.5**self.zoom/256*self.w, xy[1]-40075016*0.5**self.zoom/256*self.h), self.proj) xy2 = projections.to4326((xy[0]+40075016*0.5**self.zoom/256*self.w, xy[1]+40075016*0.5**self.zoom/256*self.h), self.proj) bbox = (xy1[0],xy1[1],xy2[0],xy2[1]) debug (bbox) return self.update_surface(bbox, zoom, style)
def update_surface_by_center(self, lonlat, zoom, style): self.zoom = zoom xy = projections.from4326(lonlat, self.proj) xy1 = projections.to4326( (xy[0] - 40075016 * 0.5**self.zoom / 256 * self.w, xy[1] - 40075016 * 0.5**self.zoom / 256 * self.h), self.proj) xy2 = projections.to4326( (xy[0] + 40075016 * 0.5**self.zoom / 256 * self.w, xy[1] + 40075016 * 0.5**self.zoom / 256 * self.h), self.proj) bbox = (xy1[0], xy1[1], xy2[0], xy2[1]) debug(bbox) return self.update_surface(bbox, zoom, style)
def expose_ev(self, widget, event): if (widget.allocation.width != self.width or widget.allocation.height != self.height): # debug("Rrresize!") self.width = widget.allocation.width self.height = widget.allocation.height cr = widget.window.cairo_create() if self.old_center_coord != self.center_coord or self.old_zoom != self.zoom: # print "Recentered!" xy = projections.from4326(self.center_coord, "EPSG:3857") xy1 = projections.to4326( (xy[0] - 40075016. * (0.5**(self.zoom)) / self.tiles.tilewidth * self.width, xy[1] - 40075016. * (0.5**(self.zoom)) / self.tiles.tileheight * self.height), "EPSG:3857") xy2 = projections.to4326( (xy[0] + 40075016. * (0.5**(self.zoom)) / self.tiles.tilewidth * self.width, xy[1] + 40075016. * (0.5**(self.zoom)) / self.tiles.tileheight * self.height), "EPSG:3857") self.bbox = (xy1[0], xy1[1], xy2[0], xy2[1]) self.tilebox = projections.tile_by_bbox(self.bbox, self.zoom, "EPSG:3857") self.old_center_coord = self.center_coord self.old_zoom = self.zoom from_tile_x, from_tile_y, to_tile_x, to_tile_y = self.tilebox dx = 1. * (from_tile_x - int(from_tile_x)) * self.tiles.tilewidth dy = 1. * (from_tile_y - int(from_tile_y)) * self.tiles.tileheight print dx, dy # print self.dx, self.dy onscreen_tiles = set() for x in range(int(from_tile_x), int(to_tile_x) + 1): for y in range(int(to_tile_y), int(from_tile_y) + 1): onscreen_tiles.add((self.zoom, x, y)) self.tiles.onscreen = onscreen_tiles for z, x, y in onscreen_tiles: tile = self.tiles[(self.zoom, x, y)] # print dx+(x-from_tile_x)*self.tiles.tilewidth-self.width # print dy+(y-from_tile_y)*self.tiles.tileheight-self.height # cr.set_source_surface(tile, int(self.dx-dx+(x-int(from_tile_x))*self.tiles.tilewidth-self.width), int(self.dy-dy-(int(from_tile_y)-y)*self.tiles.tileheight+self.height)) cr.set_source_surface( tile, int(self.dx - dx + (x - int(from_tile_x)) * self.tiles.tilewidth), int(self.dy - dy - (int(from_tile_y) - y) * self.tiles.tileheight + self.height)) cr.paint()
def release_ev(self, widget, event): if event.button == 1: #debug("Stop drag") self.drag = False self.timer.stop() #debug("dd: %s,%s "%(self.dx, self.dy)) x = event.x y = event.y lo1, la1, lo2, la2 = projections.from4326(self.bbox, "EPSG:3857") print lo1, la1, lo2, la2 #self.center_coord = projections.to4326((0.5*(self.width+self.dx)/self.width*(lo1-lo2)+lo2, la1+(0.5*(self.height+self.dy)/self.height*(la2-la1))),"EPSG:3857") self.center_coord = projections.to4326((0.5*(self.width+2*self.dx)/self.width*(lo1-lo2)+lo2, la1+(0.5*(self.height+2*self.dy)/self.height*(la2-la1))),"EPSG:3857") #self.rastertile.screen2lonlat(self.rastertile.w/2 - self.dx, self.rastertile.h/2 - self.dy); self.dx = 0 self.dy = 0 self.redraw()
def release_ev(self, widget, event): if event.button == 1: # debug("Stop drag") self.drag = False self.timer.stop() # debug("dd: %s,%s "%(self.dx, self.dy)) x = event.x y = event.y lo1, la1, lo2, la2 = projections.from4326(self.bbox, "EPSG:3857") print lo1, la1, lo2, la2 # self.center_coord = projections.to4326((0.5*(self.width+self.dx)/self.width*(lo1-lo2)+lo2, la1+(0.5*(self.height+self.dy)/self.height*(la2-la1))),"EPSG:3857") self.center_coord = projections.to4326( (0.5 * (self.width + 2 * self.dx) / self.width * (lo1 - lo2) + lo2, la1 + (0.5 * (self.height + 2 * self.dy) / self.height * (la2 - la1))), "EPSG:3857") # self.rastertile.screen2lonlat(self.rastertile.w/2 - self.dx, self.rastertile.h/2 - self.dy); self.dx = 0 self.dy = 0 self.redraw()
def get_vectors(bbox, zoom, style, vec="polygon"): bbox_p = projections.from4326(bbox, "EPSG:3857") geomcolumn = "way" database = "dbname=gis user=gis" pxtolerance = 1.8 intscalefactor = 10000 ignore_columns = set(["way_area", "osm_id", geomcolumn, "tags", "z_order"]) table = {"polygon": "planet_osm_polygon", "line": "planet_osm_line", "point": "planet_osm_point", "coastline": "coastlines"} a = psycopg2.connect(database) b = a.cursor() if vec != "coastline": b.execute("SELECT * FROM %s LIMIT 1;" % table[vec]) names = [q[0] for q in b.description] for i in ignore_columns: if i in names: names.remove(i) names = ",".join(['"' + i + '"' for i in names]) taghint = "*" types = {"line": "line", "polygon": "area", "point": "node"} adp = "" if "get_sql_hints" in dir(style): sql_hint = style.get_sql_hints(types[vec], zoom) adp = [] for tp in sql_hint: add = [] for j in tp[0]: if j not in names: break else: add.append(tp[1]) if add: add = " OR ".join(add) add = "(" + add + ")" adp.append(add) adp = " OR ".join(adp) if adp: adp = adp.replace("<", "<") adp = adp.replace(">", ">") if vec == "polygon": query = """select ST_AsGeoJSON(ST_TransScale(ST_ForceRHR(ST_Intersection(way,SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913))),%s,%s,%s,%s),0) as %s, ST_AsGeoJSON(ST_TransScale(ST_ForceRHR(ST_PointOnSurface(way)),%s,%s,%s,%s),0) as reprpoint, %s from (select (ST_Dump(ST_Multi(ST_SimplifyPreserveTopology(ST_Buffer(way,-%s),%s)))).geom as %s, %s from (select ST_Union(way) as %s, %s from (select ST_Buffer(way, %s) as %s, %s from %s where (%s) and way && SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913) and way_area > %s ) p group by %s ) p where ST_Area(way) > %s order by ST_Area(way) ) p """ % (bbox_p[0], bbox_p[1], bbox_p[2], bbox_p[3], -bbox_p[0], -bbox_p[1], intscalefactor / (bbox_p[2] - bbox_p[0]), intscalefactor / (bbox_p[3] - bbox_p[1]), geomcolumn, -bbox_p[0], -bbox_p[1], intscalefactor / (bbox_p[2] - bbox_p[0]), intscalefactor / (bbox_p[3] - bbox_p[1]), names, pixel_size_at_zoom(zoom, pxtolerance), pixel_size_at_zoom(zoom, pxtolerance), geomcolumn, names, geomcolumn, names, pixel_size_at_zoom(zoom, pxtolerance), geomcolumn, names, table[vec], adp, bbox_p[0], bbox_p[1], bbox_p[2], bbox_p[3], (pixel_size_at_zoom(zoom, pxtolerance) ** 2) / pxtolerance, names, pixel_size_at_zoom(zoom, pxtolerance) ** 2 ) elif vec == "line": query = """select ST_AsGeoJSON(ST_TransScale(ST_Intersection(way,SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913)),%s,%s,%s,%s),0) as %s, %s from (select (ST_Dump(ST_Multi(ST_SimplifyPreserveTopology(ST_LineMerge(way),%s)))).geom as %s, %s from (select ST_Union(way) as %s, %s from %s where (%s) and way && SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913) group by %s ) p ) p """ % (bbox_p[0], bbox_p[1], bbox_p[2], bbox_p[3], -bbox_p[0], -bbox_p[1], intscalefactor / (bbox_p[2] - bbox_p[0]), intscalefactor / (bbox_p[3] - bbox_p[1]), geomcolumn, names, pixel_size_at_zoom(zoom, pxtolerance), geomcolumn, names, geomcolumn, names, table[vec], adp, bbox_p[0], bbox_p[1], bbox_p[2], bbox_p[3], names, ) elif vec == "point": query = """select ST_AsGeoJSON(ST_TransScale(way,%s,%s,%s,%s),0) as %s, %s from %s where (%s) and way && SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913) limit 10000 """ % ( -bbox_p[0], -bbox_p[1], intscalefactor / (bbox_p[2] - bbox_p[0]), intscalefactor / (bbox_p[3] - bbox_p[1]), geomcolumn, names, table[vec], adp, bbox_p[0], bbox_p[1], bbox_p[2], bbox_p[3], ) elif vec == "coastline": query = """select ST_AsGeoJSON(ST_TransScale(ST_ForceRHR(ST_Intersection(way,SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913))),%s,%s,%s,%s),0) as %s, 'coastline' as "natural" from (select (ST_Dump(ST_Multi(ST_SimplifyPreserveTopology(ST_Buffer(way,-%s),%s)))).geom as %s from (select ST_Union(way) as %s from (select ST_Buffer(SetSRID(the_geom,900913), %s) as %s from %s where SetSRID(the_geom,900913) && SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913) ) p ) p where ST_Area(way) > %s ) p """ % (bbox_p[0], bbox_p[1], bbox_p[2], bbox_p[3], -bbox_p[0], -bbox_p[1], intscalefactor / (bbox_p[2] - bbox_p[0]), intscalefactor / (bbox_p[3] - bbox_p[1]), geomcolumn, pixel_size_at_zoom(zoom, pxtolerance), pixel_size_at_zoom(zoom, pxtolerance), geomcolumn, geomcolumn, pixel_size_at_zoom(zoom, pxtolerance), geomcolumn, table[vec], bbox_p[0], bbox_p[1], bbox_p[2], bbox_p[3], pixel_size_at_zoom(zoom, pxtolerance) ** 2 ) # print query a = psycopg2.connect(database) b = a.cursor() b.execute(query) names = [q[0] for q in b.description] ROWS_FETCHED = 0 polygons = [] for row in b.fetchall(): ROWS_FETCHED += 1 geom = dict(map(None, names, row)) for t in geom.keys(): if not geom[t]: del geom[t] geojson = json.loads(geom[geomcolumn]) del geom[geomcolumn] if geojson["type"] == "GeometryCollection": continue if "reprpoint" in geom: geojson["reprpoint"] = json.loads(geom["reprpoint"])["coordinates"] del geom["reprpoint"] prop = {} for k, v in geom.iteritems(): prop[k] = v try: if int(v) == float(v): prop[k] = int(v) else: prop[k] = float(v) if str(prop[k]) != v: # leading zeros etc.. should be saved prop[k] = v except: pass geojson["properties"] = prop polygons.append(geojson) return {"bbox": bbox, "granularity": intscalefactor, "features": polygons}
def get_vectors(bbox, zoom, style, vec="polygon"): bbox_p = projections.from4326(bbox, "EPSG:3857") geomcolumn = "way" database = "dbname=gis user=gis" pxtolerance = 1.8 intscalefactor = 10000 ignore_columns = set(["way_area", "osm_id", geomcolumn, "tags", "z_order"]) table = { "polygon": "planet_osm_polygon", "line": "planet_osm_line", "point": "planet_osm_point", "coastline": "coastlines" } a = psycopg2.connect(database) b = a.cursor() if vec != "coastline": b.execute("SELECT * FROM %s LIMIT 1;" % table[vec]) names = [q[0] for q in b.description] for i in ignore_columns: if i in names: names.remove(i) names = ",".join(['"' + i + '"' for i in names]) taghint = "*" types = {"line": "line", "polygon": "area", "point": "node"} adp = "" if "get_sql_hints" in dir(style): sql_hint = style.get_sql_hints(types[vec], zoom) adp = [] for tp in sql_hint: add = [] for j in tp[0]: if j not in names: break else: add.append(tp[1]) if add: add = " OR ".join(add) add = "(" + add + ")" adp.append(add) adp = " OR ".join(adp) if adp: adp = adp.replace("<", "<") adp = adp.replace(">", ">") if vec == "polygon": query = """select ST_AsGeoJSON(ST_TransScale(ST_ForceRHR(ST_Intersection(way,SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913))),%s,%s,%s,%s),0) as %s, ST_AsGeoJSON(ST_TransScale(ST_ForceRHR(ST_PointOnSurface(way)),%s,%s,%s,%s),0) as reprpoint, %s from (select (ST_Dump(ST_Multi(ST_SimplifyPreserveTopology(ST_Buffer(way,-%s),%s)))).geom as %s, %s from (select ST_Union(way) as %s, %s from (select ST_Buffer(way, %s) as %s, %s from %s where (%s) and way && SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913) and way_area > %s ) p group by %s ) p where ST_Area(way) > %s order by ST_Area(way) ) p """ % (bbox_p[0], bbox_p[1], bbox_p[2], bbox_p[3], -bbox_p[0], -bbox_p[1], intscalefactor / (bbox_p[2] - bbox_p[0]), intscalefactor / (bbox_p[3] - bbox_p[1]), geomcolumn, -bbox_p[0], -bbox_p[1], intscalefactor / (bbox_p[2] - bbox_p[0]), intscalefactor / (bbox_p[3] - bbox_p[1]), names, pixel_size_at_zoom( zoom, pxtolerance), pixel_size_at_zoom( zoom, pxtolerance), geomcolumn, names, geomcolumn, names, pixel_size_at_zoom(zoom, pxtolerance), geomcolumn, names, table[vec], adp, bbox_p[0], bbox_p[1], bbox_p[2], bbox_p[3], (pixel_size_at_zoom(zoom, pxtolerance)**2) / pxtolerance, names, pixel_size_at_zoom(zoom, pxtolerance)**2) elif vec == "line": query = """select ST_AsGeoJSON(ST_TransScale(ST_Intersection(way,SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913)),%s,%s,%s,%s),0) as %s, %s from (select (ST_Dump(ST_Multi(ST_SimplifyPreserveTopology(ST_LineMerge(way),%s)))).geom as %s, %s from (select ST_Union(way) as %s, %s from %s where (%s) and way && SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913) group by %s ) p ) p """ % ( bbox_p[0], bbox_p[1], bbox_p[2], bbox_p[3], -bbox_p[0], -bbox_p[1], intscalefactor / (bbox_p[2] - bbox_p[0]), intscalefactor / (bbox_p[3] - bbox_p[1]), geomcolumn, names, pixel_size_at_zoom(zoom, pxtolerance), geomcolumn, names, geomcolumn, names, table[vec], adp, bbox_p[0], bbox_p[1], bbox_p[2], bbox_p[3], names, ) elif vec == "point": query = """select ST_AsGeoJSON(ST_TransScale(way,%s,%s,%s,%s),0) as %s, %s from %s where (%s) and way && SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913) limit 10000 """ % ( -bbox_p[0], -bbox_p[1], intscalefactor / (bbox_p[2] - bbox_p[0]), intscalefactor / (bbox_p[3] - bbox_p[1]), geomcolumn, names, table[vec], adp, bbox_p[0], bbox_p[1], bbox_p[2], bbox_p[3], ) elif vec == "coastline": query = """select ST_AsGeoJSON(ST_TransScale(ST_ForceRHR(ST_Intersection(way,SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913))),%s,%s,%s,%s),0) as %s, 'coastline' as "natural" from (select (ST_Dump(ST_Multi(ST_SimplifyPreserveTopology(ST_Buffer(way,-%s),%s)))).geom as %s from (select ST_Union(way) as %s from (select ST_Buffer(SetSRID(the_geom,900913), %s) as %s from %s where SetSRID(the_geom,900913) && SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913) ) p ) p where ST_Area(way) > %s ) p """ % (bbox_p[0], bbox_p[1], bbox_p[2], bbox_p[3], -bbox_p[0], -bbox_p[1], intscalefactor / (bbox_p[2] - bbox_p[0]), intscalefactor / (bbox_p[3] - bbox_p[1]), geomcolumn, pixel_size_at_zoom(zoom, pxtolerance), pixel_size_at_zoom(zoom, pxtolerance), geomcolumn, geomcolumn, pixel_size_at_zoom(zoom, pxtolerance), geomcolumn, table[vec], bbox_p[0], bbox_p[1], bbox_p[2], bbox_p[3], pixel_size_at_zoom(zoom, pxtolerance)**2) #print query a = psycopg2.connect(database) b = a.cursor() b.execute(query) names = [q[0] for q in b.description] ROWS_FETCHED = 0 polygons = [] for row in b.fetchall(): ROWS_FETCHED += 1 geom = dict(map(None, names, row)) for t in geom.keys(): if not geom[t]: del geom[t] geojson = json.loads(geom[geomcolumn]) del geom[geomcolumn] if geojson["type"] == "GeometryCollection": continue if "reprpoint" in geom: geojson["reprpoint"] = json.loads(geom["reprpoint"])["coordinates"] del geom["reprpoint"] prop = {} for k, v in geom.iteritems(): prop[k] = v try: if int(v) == float(v): prop[k] = int(v) else: prop[k] = float(v) if str(prop[k]) != v: # leading zeros etc.. should be saved prop[k] = v except: pass geojson["properties"] = prop polygons.append(geojson) return {"bbox": bbox, "granularity": intscalefactor, "features": polygons}
def build_geometries(diff, srid): srid_txt = "EPSG:{}".format(srid) geom = {'line': [], 'point': [], 'polygon': []} for osm_id, node in diff['add']['node'].iteritems(): x, y = projections.from4326(node["geom"], srid_txt) geom["point"].append({ "geom": "SRID={};POINT({} {})".format(srid, x, y), "osm_id": osm_id, "tags": node["tags"] }) for osm_id, way in diff['add']['way'].iteritems(): line = projections.from4326(way["geom"], srid_txt) polygonize = check_tags_if_polygon(way["tags"]) if polygonize: result, dangles, cuts, invalids = polygonize_full([line]) #print result, dangles, cuts, invalids for poly in result: geom["polygon"].append({ "geom": "SRID={};{}".format(srid, poly.wkt), "osm_id": osm_id, "way_area": poly.area, "tags": way["tags"] }) for line in dangles: geom["line"].append({ "geom": "SRID={};{}".format(srid, line.wkt), "osm_id": osm_id, "tags": way["tags"] }) for line in cuts: geom["line"].append({ "geom": "SRID={};{}".format(srid, line.wkt), "osm_id": osm_id, "tags": way["tags"] }) for line in invalids: geom["line"].append({ "geom": "SRID={};{}".format(srid, line.wkt), "osm_id": osm_id, "tags": way["tags"] }) else: ll = ",".join([a[0] + a[1] for a in line]) geom["line"].append({ "geom": "SRID={};LINESTRING({})".format(srid, ll), "osm_id": osm_id, "tags": way["tags"] }) for osm_id, relation in diff['add']['relation'].iteritems(): lines = [ projections.from4326(way, srid_txt) for way in relation["geom"] ] polygonize = check_tags_if_polygon(relation["tags"]) if polygonize: merged = linemerge(lines) polys = [] lines = [] for line in merged: if line.is_ring: polys.append(line) else: lines.append(line) if polys: # TODO: repair geometry poly = Polygon(polys[0], polys[1:]) geom["polygon"].append({ "geom": "SRID={};{}".format(srid, poly.wkt), "osm_id": -osm_id, "way_area": 0, "tags": relation["tags"] }) for line in lines: geom["line"].append({ "geom": "SRID={};{}".format(srid, line.wkt), "osm_id": -osm_id, "tags": relation["tags"] }) #result, dangles, cuts, invalids = polygonize_full(lines) ##print result, dangles, cuts, invalids #result = list(result) #sd = result[0] #for poly in result[1:]: #sd = sd.symmetric_difference(poly) #geom["polygon"].append({"geom": "SRID={};{}".format(srid, sd.wkt), "osm_id": -osm_id, "way_area":sd.area, "tags": relation["tags"]}) #for line in dangles: #geom["line"].append({"geom": "SRID={};{}".format(srid, line.wkt), "osm_id": -osm_id, "tags": relation["tags"]}) #for line in cuts: #geom["line"].append({"geom": "SRID={};{}".format(srid, line.wkt), "osm_id": -osm_id, "tags": relation["tags"]}) else: ll = ",".join([a[0] + a[1] for a in line]) geom["line"].append({ "geom": "SRID={};LINESTRING({})".format(srid, ll), "osm_id": osm_id, "tags": relation["tags"] }) return geom
def get_vectors(self, bbox, zoom, sql_hint=None, tags_hint=None): """ Fetches vectors for given bbox. sql_hint is a list of sets of (key, sql_for_key) """ a = psycopg2.connect(self.database) b = a.cursor() bbox = tuple(projections.from4326(bbox, self.proj)) ### FIXME: hardcoded EPSG:3857 in database tables = ("planet_osm_line", "planet_osm_polygon") # FIXME: points resp = {} for table in tables: add = "" taghint = "*" if sql_hint: adp = [] for tp in sql_hint: add = [] b.execute("SELECT * FROM %s LIMIT 1;" % table) names = [q[0] for q in b.description] for j in tp[0]: if j not in names: break else: add.append(tp[1]) if add: add = " OR ".join(add) add = "(" + add + ")" adp.append(add) if tags_hint: taghint = ", ".join([ '"' + j + '"' for j in tags_hint if j in names ]) + ", way, osm_id" adp = " OR ".join(adp) req = "SELECT %s FROM %s WHERE (%s) and way && SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913);" % ( taghint, table, adp, bbox[0], bbox[1], bbox[2], bbox[3]) print req b.execute(req) names = [q[0] for q in b.description] for row in b.fetchall(): row_dict = dict(map(None, names, row)) for k, v in row_dict.items(): if not v: del row_dict[k] geom = shapely.wkb.loads(row_dict["way"].decode('hex')) ### FIXME: a dirty hack to basically support polygons, needs lots of rewrite try: geom = list(geom.coords) except NotImplementedError: "trying polygons" try: geom = geom.boundary geom = list(geom.coords) row_dict[":area"] = "yes" except NotImplementedError: "multipolygon" continue ### FIXME # geom = projections.to4326(geom, self.proj) del row_dict["way"] oid = row_dict["osm_id"] del row_dict["osm_id"] w = Way(row_dict, geom) # print row_dict resp[oid] = w a.close() del a return resp
def update_surface(self, bbox, zoom, style, callback=lambda x=None: None): rendertimer = Timer("Rendering image") if "image" not in style.cache: style.cache["image"] = ImageLoader() timer = Timer("Getting data") self.zoom = zoom self.bbox = bbox self.bbox_p = projections.from4326(bbox, self.proj) print self.bbox_p scale = abs(self.w / (self.bbox_p[0] - self.bbox_p[2]) / math.cos(math.pi * (self.bbox[1] + self.bbox[3]) / 2 / 180)) zscale = 0.5 * scale cr = cairo.Context(self.surface) # getting and setting canvas properties bgs = style.get_style("canvas", {}, self.zoom, scale, zscale) if not bgs: bgs = [{}] bgs = bgs[0] cr.rectangle(0, 0, self.w, self.h) # canvas color and opcity color = bgs.get("fill-color", (0.7, 0.7, 0.7)) cr.set_source_rgba(color[0], color[1], color[2], bgs.get("fill-opacity", 1)) cr.fill() callback() # canvas antialiasing antialias = bgs.get("antialias", "full") if antialias == "none": "no antialiasing enabled" cr.set_antialias(1) #cr.font_options_set_antialias(1) elif antialias == "text": "only text antialiased" cr.set_antialias(1) #cr.font_options_set_antialias(2) else: "full antialias" cr.set_antialias(2) #cr.font_options_set_antialias(2) datatimer = Timer("Asking backend") if "get_sql_hints" in dir(style): hints = style.get_sql_hints('way', self.zoom) else: hints = None if "get_interesting_tags" in dir(style): itags = style.get_interesting_tags(zoom=self.zoom) else: itags = None # enlarge bbox by 20% to each side. results in more vectors, but makes less artifaces. span_x, span_y = bbox[2] - bbox[0], bbox[3] - bbox[1] bbox_expand = [ bbox[0] - 0.2 * span_x, bbox[1] - 0.2 * span_y, bbox[2] + 0.2 * span_x, bbox[3] + 0.2 * span_y ] vectors = self.data.get_vectors(bbox_expand, self.zoom, hints, itags).values() datatimer.stop() datatimer = Timer("Applying styles") ww = [] for way in vectors: type = "line" if way.coords[0] == way.coords[-1]: type == "area" st = style.get_style("area", way.tags, self.zoom, scale, zscale) if st: for fpt in st: #debug(fpt) ww.append([way.copy(), fpt]) datatimer.stop() debug("%s objects on screen (%s in dataset)" % (len(ww), len(vectors))) er = Timer("Projecing data") if self.data.proj != self.proj: for w in ww: w[0].cs = [ self.lonlat2screen(coord) for coord in projections.transform( w[0].coords, self.data.proj, self.proj) ] else: for w in ww: w[0].cs = [self.lonlat2screen(coord) for coord in w[0].coords] for w in ww: if "offset" in w[1]: offset = float(w[1]["offset"]) w[0] = w[0].copy() w[0].cs = offset_line(w[0].cs, offset) if "raise" in w[1] and not "extrude" in w[1]: w[0] = w[0].copy() offset = float(w[1]["raise"]) w[0].cs_real = w[0].cs w[0].cs = [(x, y - offset) for x, y in w[0].cs] if "extrude" in w[1]: if w[1]["extrude"] < 2: del w[1]["extrude"] if "extrude" in w[1] and "fill-color" not in w[1] and "width" in w[ 1]: w[1]["fill-color"] = w[1].get("color", (0, 0, 0)) w[1]["fill-opacity"] = w[1].get("opacity", 1) w[0] = w[0].copy() #print w[0].cs w[0].cs = offset_line(w[0].cs, w[1]["width"] / 2) #print w[0].cs aa = offset_line(w[0].cs, -w[1]["width"]) del w[1]["width"] aa.reverse() w[0].cs.extend(aa) er.stop() ww.sort(key=lambda x: x[1]["layer"]) layers = list(set([int(x[1]["layer"] / 100.) for x in ww])) layers.sort() objs_by_layers = {} for layer in layers: objs_by_layers[layer] = [] for obj in ww: objs_by_layers[int(obj[1]["layer"] / 100.)].append(obj) del ww timer.stop() timer = Timer("Rasterizing image") linecaps = {"butt": 0, "round": 1, "square": 2} linejoin = {"miter": 0, "round": 1, "bevel": 2} text_rendered_at = set([(-100, -100)]) for layer in layers: data = objs_by_layers[layer] #data.sort(lambda x,y:cmp(max([x1[1] for x1 in x[0].cs]), max([x1[1] for x1 in y[0].cs]))) # - fill polygons for obj in data: if ("fill-color" in obj[1] or "fill-image" in obj[1] ) and not "extrude" in obj[1]: ## TODO: fill-image color = obj[1].get("fill-color", (0, 0, 0)) cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("fill-opacity", 1)) if "fill-image" in obj[1]: image = style.cache["image"][obj[1]["fill-image"]] if image: pattern = cairo.SurfacePattern(image) pattern.set_extend(cairo.EXTEND_REPEAT) cr.set_source(pattern) poly(cr, obj[0].cs) # - draw casings on layer for obj in data: ### Extras: casing-linecap, casing-linejoin if "casing-width" in obj[1] or "casing-color" in obj[ 1] and "extrude" not in obj[1]: cr.set_dash(obj[1].get("casing-dashes", obj[1].get("dashes", []))) cr.set_line_join( linejoin.get( obj[1].get("casing-linejoin", obj[1].get("linejoin", "round")), 1)) color = obj[1].get("casing-color", (0, 0, 0)) cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("casing-opacity", 1)) ## TODO: good combining of transparent lines and casing ## Probable solution: render casing, render way as mask and put casing with mask chopped out onto image cr.set_line_width(obj[1].get("width", 0) + obj[1].get("casing-width", 1)) cr.set_line_cap( linecaps.get( obj[1].get("casing-linecap", obj[1].get("linecap", "butt")), 0)) line(cr, obj[0].cs) # - draw line centers for obj in data: if ("width" in obj[1] or "color" in obj[1] or "image" in obj[1]) and "extrude" not in obj[1]: cr.set_dash(obj[1].get("dashes", [])) cr.set_line_join( linejoin.get(obj[1].get("linejoin", "round"), 1)) color = obj[1].get("color", (0, 0, 0)) cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("opacity", 1)) ## TODO: better overlapping of transparent lines. ## Probable solution: render them (while they're of the same opacity and layer) on a temporary canvas that's merged into main later cr.set_line_width(obj[1].get("width", 1)) cr.set_line_cap( linecaps.get(obj[1].get("linecap", "butt"), 0)) if "image" in obj[1]: image = style.cache["image"][obj[1]["image"]] if image: pattern = cairo.SurfacePattern(image) pattern.set_extend(cairo.EXTEND_REPEAT) cr.set_source(pattern) line(cr, obj[0].cs) callback() # - extruding polygons #data.sort(lambda x,y:cmp(max([x1[1] for x1 in x[0].cs]), max([x1[1] for x1 in y[0].cs]))) # Pass 1. Creating list of extruded polygons extlist = [] # fromat: (coords, ("h"/"v", y,z), real_obj) for obj in data: if "extrude" in obj[1]: def face_to_poly(face, hgt): """ Converts a line into height-up extruded poly """ return [ face[0], face[1], (face[1][0], face[1][1] - hgt), (face[0][0], face[0][1] - hgt), face[0] ] hgt = obj[1]["extrude"] raised = float(obj[1].get("raise", 0)) excoords = [(a[0], a[1] - hgt - raised) for a in obj[0].cs] faces = [] coord = obj[0].cs[-1] #p_coord = (coord[0],coord[1]-raised) p_coord = False for coord in obj[0].cs: c = (coord[0], coord[1] - raised) if p_coord: extlist.append((face_to_poly([c, p_coord], hgt), ("v", min(coord[1], p_coord[1]), hgt), obj)) p_coord = c extlist.append( (excoords, ("h", min(coord[1], p_coord[1]), hgt), obj)) #faces.sort(lambda x,y:cmp(max([x1[1] for x1 in x]), max([x1[1] for x1 in y]))) # Pass 2. Sorting def compare_things(a, b): """ Custom comparator for extlist sorting. Sorts back-to-front, bottom-to-top, | > \ > _, horizontal-to-vertical. """ t1, t2 = a[1], b[1] # if t1[1] > t2[1]: # back-to-front return 1 if t1[1] < t2[1]: return -1 if t1[2] > t2[2]: # bottom-to-top return 1 if t1[2] < t2[2]: return -1 if t1[0] < t2[0]: # h-to-v return 1 if t1[0] > t2[0]: return -1 return cmp( math.sin( math.atan2(a[0][0][0] - a[0][1][0], a[0][0][0] - a[0][1][0])), math.sin( math.atan2(b[0][0][0] - b[0][1][0], b[0][0][0] - b[0][1][0]))) print t1 print t2 extlist.sort(compare_things) # Pass 3. Rendering using painter's algorythm cr.set_dash([]) for ply, prop, obj in extlist: if prop[0] == "v": color = obj[1].get("extrude-face-color", obj[1].get("color", (0, 0, 0))) cr.set_source_rgba( color[0], color[1], color[2], obj[1].get("extrude-face-opacity", obj[1].get("opacity", 1))) poly(cr, ply) color = obj[1].get("extrude-edge-color", obj[1].get("color", (0, 0, 0))) cr.set_source_rgba( color[0], color[1], color[2], obj[1].get("extrude-edge-opacity", obj[1].get("opacity", 1))) cr.set_line_width(.5) line(cr, ply) if prop[0] == "h": if "fill-color" in obj[1]: color = obj[1]["fill-color"] cr.set_source_rgba( color[0], color[1], color[2], obj[1].get("fill-opacity", obj[1].get("opacity", 1))) poly(cr, ply) color = obj[1].get("extrude-edge-color", obj[1].get("color", (0, 0, 0))) cr.set_source_rgba( color[0], color[1], color[2], obj[1].get("extrude-edge-opacity", obj[1].get("opacity", 1))) cr.set_line_width(1) line(cr, ply) #cr.set_line_width (obj[1].get("width", 1)) #color = obj[1].get("color", (0,0,0) ) #cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("extrude-edge-opacity", obj[1].get("opacity", 1))) #line(cr,excoords) #if "fill-color" in obj[1]: #color = obj[1]["fill-color"] #cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("fill-opacity", 1)) #poly(cr,excoords) for obj in data: if "icon-image" in obj[1]: image = style.cache["image"][obj[1]["icon-image"]] if image: dy = image.get_height() / 2 dx = image.get_width() / 2 where = self.lonlat2screen( projections.transform(obj[0].center, self.data.proj, self.proj)) cr.set_source_surface(image, where[0] - dx, where[1] - dy) cr.paint() callback() # - render text labels texttimer = Timer("Text rendering") cr.set_line_join( 1 ) # setting linejoin to "round" to get less artifacts on halo render for obj in data: if "text" in obj[1]: text = obj[1]["text"] #cr.set_line_width (obj[1].get("width", 1)) #cr.set_font_size(float(obj[1].get("font-size", 9))) ft_desc = pango.FontDescription() ft_desc.set_family(obj[1].get('font-family', 'sans')) ft_desc.set_size(pango.SCALE * int(obj[1].get('font-size', 9))) fontstyle = obj[1].get('font-style', 'normal') if fontstyle == 'italic': fontstyle = pango.STYLE_ITALIC else: fontstyle = pango.STYLE_NORMAL ft_desc.set_style(fontstyle) fontweight = obj[1].get('font-weight', 400) try: fontweight = int(fontweight) except ValueError: if fontweight == 'bold': fontweight = 700 else: fontweight = 400 ft_desc.set_weight(fontweight) if obj[1].get('text-transform', None) == 'uppercase': text = text.upper() p_ctx = pangocairo.CairoContext(cr) p_layout = p_ctx.create_layout() p_layout.set_font_description(ft_desc) p_layout.set_text(text) p_attrs = pango.AttrList() decoration = obj[1].get('text-decoration', 'none') if decoration == 'underline': p_attrs.insert( pango.AttrUnderline(pango.UNDERLINE_SINGLE, end_index=-1)) decoration = obj[1].get('font-variant', 'none') if decoration == 'small-caps': p_attrs.insert( pango.AttrVariant(pango.VARIANT_SMALL_CAPS, start_index=0, end_index=-1)) p_layout.set_attributes(p_attrs) if obj[1].get("text-position", "center") == "center": where = self.lonlat2screen( projections.transform(obj[0].center, self.data.proj, self.proj)) for t in text_rendered_at: if ((t[0] - where[0])**2 + (t[1] - where[1])**2) < 15 * 15: break else: text_rendered_at.add(where) #debug ("drawing text: %s at %s"%(text, where)) if "text-halo-color" in obj[ 1] or "text-halo-radius" in obj[1]: cr.new_path() cr.move_to(where[0], where[1]) cr.set_line_width(obj[1].get( "text-halo-radius", 1)) color = obj[1].get("text-halo-color", (1., 1., 1.)) cr.set_source_rgb(color[0], color[1], color[2]) cr.text_path(text) cr.stroke() cr.new_path() cr.move_to(where[0], where[1]) cr.set_line_width(obj[1].get( "text-halo-radius", 1)) color = obj[1].get("text-color", (0., 0., 0.)) cr.set_source_rgb(color[0], color[1], color[2]) cr.text_path(text) cr.fill() else: ### render text along line c = obj[0].cs text = unicode(text, "utf-8") # - calculate line length length = reduce( lambda x, y: (x[0] + ((y[0] - x[1])**2 + (y[1] - x[2])**2)**0.5, y[0], y[1]), c, (0, c[0][0], c[0][1]))[0] #print length, text, cr.text_extents(text) if length > cr.text_extents(text)[2]: # - function to get (x, y, normale) from (c, length_along_c) def get_xy_from_len(c, length_along_c): x0, y0 = c[0] for x, y in c: seg_len = ((x - x0)**2 + (y - y0)**2)**0.5 if length_along_c < seg_len: normed = length_along_c / seg_len return (x - x0) * normed + x0, ( y - y0) * normed + y0, math.atan2( y - y0, x - x0) else: length_along_c -= seg_len x0, y0 = x, y else: return None da = 0 os = 1 z = length / 2 - cr.text_extents(text)[2] / 2 # print get_xy_from_len(c,z) if c[0][0] < c[1][0] and get_xy_from_len( c, z)[2] < math.pi / 2 and get_xy_from_len( c, z)[2] > -math.pi / 2: da = 0 os = 1 z = length / 2 - cr.text_extents(text)[2] / 2 else: da = math.pi os = -1 z = length / 2 + cr.text_extents(text)[2] / 2 z1 = z if "text-halo-color" in obj[ 1] or "text-halo-radius" in obj[1]: cr.set_line_width( obj[1].get("text-halo-radius", 1.5) * 2) color = obj[1].get("text-halo-color", (1., 1., 1.)) cr.set_source_rgb(color[0], color[1], color[2]) xy = get_xy_from_len(c, z) cr.save() #cr.move_to(xy[0],xy[1]) p_ctx.translate(xy[0], xy[1]) cr.rotate(xy[2] + da) #p_ctx.translate(x,y) #p_ctx.show_layout(p_layout) p_ctx.layout_path(p_layout) cr.restore() cr.stroke() #for letter in text: #cr.new_path() #xy = get_xy_from_len(c,z) ##print letter, cr.text_extents(letter) #cr.move_to(xy[0],xy[1]) #cr.save() #cr.rotate(xy[2]+da) #cr.text_path(letter) #cr.restore() #cr.stroke() #z += os*cr.text_extents(letter)[4] color = obj[1].get("text-color", (0., 0., 0.)) cr.set_source_rgb(color[0], color[1], color[2]) z = z1 xy = get_xy_from_len(c, z) cr.save() #cr.move_to(xy[0],xy[1]) p_ctx.translate(xy[0], xy[1]) cr.rotate(xy[2] + da) #p_ctx.translate(x,y) p_ctx.show_layout(p_layout) cr.restore() #for letter in text: #cr.new_path() #xy = get_xy_from_len(c,z) ##print letter, cr.text_extents(letter) #cr.move_to(xy[0],xy[1]) #cr.save() #cr.rotate(xy[2]+da) #cr.text_path(letter) #cr.restore() #cr.fill() #z += os*cr.text_extents(letter)[4] texttimer.stop() del data del layers timer.stop() rendertimer.stop() debug(self.bbox) callback(True)
def __del__(self): del self.surface def screen2lonlat(self, x, y): lo1, la1, lo2, la2 = self.bbox_p debug("%s %s - %s %s" % (x, y, self.w, self.h)) debug(self.bbox_p) return projections.to4326((1. * x / self.w * (lo2 - lo1) + lo1, la2 + (1. * y / (self.h) * (la1 - la2))), self.proj) # return (x - self.w/2)/(math.cos(self.center_coord[1]*math.pi/180)*self.zoom) + self.center_coord[0], -(y - self.h/2)/self.zoom + self.center_coord[1] def lonlat2screen(self, (lon, lat), epsg4326=False): if epsg4326: lon, lat = projections.from4326((lon, lat), self.proj) lo1, la1, lo2, la2 = self.bbox_p return ((lon - lo1) * (self.w - 1) / abs(lo2 - lo1), ((la2 - lat) * (self.h - 1) / (la2 - la1))) # return (lon - self.center_coord[0])*self.lcc*self.zoom + self.w/2, -(lat - self.center_coord[1])*self.zoom + self.h/2 def update_surface_by_center(self, lonlat, zoom, style): self.zoom = zoom xy = projections.from4326(lonlat, self.proj) xy1 = projections.to4326( (xy[0] - 40075016 * 0.5**self.zoom / 256 * self.w, xy[1] - 40075016 * 0.5**self.zoom / 256 * self.h), self.proj) xy2 = projections.to4326( (xy[0] + 40075016 * 0.5**self.zoom / 256 * self.w, xy[1] + 40075016 * 0.5**self.zoom / 256 * self.h), self.proj) bbox = (xy1[0], xy1[1], xy2[0], xy2[1])
def update_surface(self, bbox, zoom, style, callback = lambda x=None: None): rendertimer = Timer("Rendering image") if "image" not in style.cache: style.cache["image"] = ImageLoader() timer = Timer("Getting data") self.zoom = zoom self.bbox = bbox self.bbox_p = projections.from4326(bbox,self.proj) print self.bbox_p scale = abs(self.w/(self.bbox_p[0] - self.bbox_p[2])/math.cos(math.pi*(self.bbox[1]+self.bbox[3])/2/180)) zscale = 0.5*scale cr = cairo.Context(self.surface) # getting and setting canvas properties bgs = style.get_style("canvas", {}, self.zoom, scale, zscale) if not bgs: bgs = [{}] bgs = bgs[0] cr.rectangle(0, 0, self.w, self.h) # canvas color and opcity color = bgs.get("fill-color",(0.7, 0.7, 0.7)) cr.set_source_rgba(color[0], color[1], color[2], bgs.get("fill-opacity", 1)) cr.fill() callback() # canvas antialiasing antialias = bgs.get("antialias", "full") if antialias == "none": "no antialiasing enabled" cr.set_antialias(1) #cr.font_options_set_antialias(1) elif antialias == "text": "only text antialiased" cr.set_antialias(1) #cr.font_options_set_antialias(2) else: "full antialias" cr.set_antialias(2) #cr.font_options_set_antialias(2) datatimer = Timer("Asking backend") if "get_sql_hints" in dir(style): hints = style.get_sql_hints('way', self.zoom) else: hints = None if "get_interesting_tags" in dir(style): itags = style.get_interesting_tags(zoom=self.zoom) else: itags = None # enlarge bbox by 20% to each side. results in more vectors, but makes less artifaces. span_x, span_y = bbox[2]-bbox[0], bbox[3]-bbox[1] bbox_expand = [bbox[0]-0.2*span_x,bbox[1]-0.2*span_y,bbox[2]+0.2*span_x,bbox[3]+0.2*span_y] vectors = self.data.get_vectors(bbox_expand,self.zoom,hints,itags).values() datatimer.stop() datatimer = Timer("Applying styles") ww = [] for way in vectors: type = "line" if way.coords[0] == way.coords[-1]: type == "area" st = style.get_style("area", way.tags, self.zoom, scale, zscale) if st: for fpt in st: #debug(fpt) ww.append([way.copy(), fpt]) datatimer.stop() debug( "%s objects on screen (%s in dataset)"%(len(ww),len(vectors)) ) er = Timer("Projecing data") if self.data.proj != self.proj: for w in ww: w[0].cs = [self.lonlat2screen(coord) for coord in projections.transform(w[0].coords, self.data.proj, self.proj)] else: for w in ww: w[0].cs = [self.lonlat2screen(coord) for coord in w[0].coords] for w in ww: if "offset" in w[1]: offset = float(w[1]["offset"]) w[0] = w[0].copy() w[0].cs = offset_line(w[0].cs, offset) if "raise" in w[1] and not "extrude" in w[1]: w[0] = w[0].copy() offset = float(w[1]["raise"]) w[0].cs_real = w[0].cs w[0].cs = [(x,y-offset) for x,y in w[0].cs] if "extrude" in w[1]: if w[1]["extrude"]<2: del w[1]["extrude"] if "extrude" in w[1] and "fill-color" not in w[1] and "width" in w[1]: w[1]["fill-color"] = w[1].get("color", (0,0,0)) w[1]["fill-opacity"] = w[1].get("opacity", 1) w[0] = w[0].copy() #print w[0].cs w[0].cs = offset_line(w[0].cs, w[1]["width"]/2) #print w[0].cs aa = offset_line(w[0].cs, -w[1]["width"]) del w[1]["width"] aa.reverse() w[0].cs.extend(aa) er.stop() ww.sort(key=lambda x: x[1]["layer"]) layers = list(set([int(x[1]["layer"]/100.) for x in ww])) layers.sort() objs_by_layers = {} for layer in layers: objs_by_layers[layer] = [] for obj in ww: objs_by_layers[int(obj[1]["layer"]/100.)].append(obj) del ww timer.stop() timer = Timer("Rasterizing image") linecaps = {"butt":0, "round":1, "square":2} linejoin = {"miter":0, "round":1, "bevel":2} text_rendered_at = set([(-100,-100)]) for layer in layers: data = objs_by_layers[layer] #data.sort(lambda x,y:cmp(max([x1[1] for x1 in x[0].cs]), max([x1[1] for x1 in y[0].cs]))) # - fill polygons for obj in data: if ("fill-color" in obj[1] or "fill-image" in obj[1]) and not "extrude" in obj[1]: ## TODO: fill-image color = obj[1].get("fill-color", (0,0,0)) cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("fill-opacity", 1)) if "fill-image" in obj[1]: image = style.cache["image"][obj[1]["fill-image"]] if image: pattern = cairo.SurfacePattern(image) pattern.set_extend(cairo.EXTEND_REPEAT) cr.set_source(pattern) poly(cr, obj[0].cs) # - draw casings on layer for obj in data: ### Extras: casing-linecap, casing-linejoin if "casing-width" in obj[1] or "casing-color" in obj[1] and "extrude" not in obj[1]: cr.set_dash(obj[1].get("casing-dashes",obj[1].get("dashes", []))) cr.set_line_join(linejoin.get(obj[1].get("casing-linejoin",obj[1].get("linejoin", "round")),1)) color = obj[1].get("casing-color", (0,0,0)) cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("casing-opacity", 1)) ## TODO: good combining of transparent lines and casing ## Probable solution: render casing, render way as mask and put casing with mask chopped out onto image cr.set_line_width (obj[1].get("width",0)+obj[1].get("casing-width", 1 )) cr.set_line_cap(linecaps.get(obj[1].get("casing-linecap", obj[1].get("linecap", "butt")),0)) line(cr, obj[0].cs) # - draw line centers for obj in data: if ("width" in obj[1] or "color" in obj[1] or "image" in obj[1]) and "extrude" not in obj[1]: cr.set_dash(obj[1].get("dashes", [])) cr.set_line_join(linejoin.get(obj[1].get("linejoin", "round"),1)) color = obj[1].get("color", (0,0,0)) cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("opacity", 1)) ## TODO: better overlapping of transparent lines. ## Probable solution: render them (while they're of the same opacity and layer) on a temporary canvas that's merged into main later cr.set_line_width (obj[1].get("width", 1)) cr.set_line_cap(linecaps.get(obj[1].get("linecap", "butt"),0)) if "image" in obj[1]: image = style.cache["image"][obj[1]["image"]] if image: pattern = cairo.SurfacePattern(image) pattern.set_extend(cairo.EXTEND_REPEAT) cr.set_source(pattern) line(cr, obj[0].cs) callback() # - extruding polygons #data.sort(lambda x,y:cmp(max([x1[1] for x1 in x[0].cs]), max([x1[1] for x1 in y[0].cs]))) # Pass 1. Creating list of extruded polygons extlist = [] # fromat: (coords, ("h"/"v", y,z), real_obj) for obj in data: if "extrude" in obj[1]: def face_to_poly(face, hgt): """ Converts a line into height-up extruded poly """ return [face[0], face[1], (face[1][0], face[1][1]-hgt), (face[0][0], face[0][1]-hgt), face[0]] hgt = obj[1]["extrude"] raised = float(obj[1].get("raise",0)) excoords = [(a[0],a[1]-hgt-raised) for a in obj[0].cs] faces = [] coord = obj[0].cs[-1] #p_coord = (coord[0],coord[1]-raised) p_coord = False for coord in obj[0].cs: c = (coord[0],coord[1]-raised) if p_coord: extlist.append( (face_to_poly([c, p_coord],hgt), ("v", min(coord[1],p_coord[1]), hgt), obj )) p_coord = c extlist.append( (excoords, ("h", min(coord[1],p_coord[1]), hgt), obj )) #faces.sort(lambda x,y:cmp(max([x1[1] for x1 in x]), max([x1[1] for x1 in y]))) # Pass 2. Sorting def compare_things(a,b): """ Custom comparator for extlist sorting. Sorts back-to-front, bottom-to-top, | > \ > _, horizontal-to-vertical. """ t1,t2 = a[1],b[1] # if t1[1] > t2[1]: # back-to-front return 1 if t1[1] < t2[1]: return -1 if t1[2] > t2[2]: # bottom-to-top return 1 if t1[2] < t2[2]: return -1 if t1[0] < t2[0]: # h-to-v return 1 if t1[0] > t2[0]: return -1 return cmp(math.sin(math.atan2(a[0][0][0]-a[0][1][0],a[0][0][0]-a[0][1][0])),math.sin(math.atan2(b[0][0][0]-b[0][1][0],b[0][0][0]-b[0][1][0]))) print t1 print t2 extlist.sort(compare_things) # Pass 3. Rendering using painter's algorythm cr.set_dash([]) for ply, prop, obj in extlist: if prop[0] == "v": color = obj[1].get("extrude-face-color", obj[1].get("color", (0,0,0) )) cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("extrude-face-opacity", obj[1].get("opacity", 1))) poly(cr, ply) color = obj[1].get("extrude-edge-color", obj[1].get("color", (0,0,0) )) cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("extrude-edge-opacity", obj[1].get("opacity", 1))) cr.set_line_width (.5) line(cr, ply) if prop[0] == "h": if "fill-color" in obj[1]: color = obj[1]["fill-color"] cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("fill-opacity", obj[1].get("opacity", 1))) poly(cr,ply) color = obj[1].get("extrude-edge-color", obj[1].get("color", (0,0,0) )) cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("extrude-edge-opacity", obj[1].get("opacity", 1))) cr.set_line_width (1) line(cr, ply) #cr.set_line_width (obj[1].get("width", 1)) #color = obj[1].get("color", (0,0,0) ) #cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("extrude-edge-opacity", obj[1].get("opacity", 1))) #line(cr,excoords) #if "fill-color" in obj[1]: #color = obj[1]["fill-color"] #cr.set_source_rgba(color[0], color[1], color[2], obj[1].get("fill-opacity", 1)) #poly(cr,excoords) for obj in data: if "icon-image" in obj[1]: image = style.cache["image"][obj[1]["icon-image"]] if image: dy = image.get_height()/2 dx = image.get_width()/2 where = self.lonlat2screen(projections.transform(obj[0].center,self.data.proj,self.proj)) cr.set_source_surface(image, where[0]-dx, where[1]-dy) cr.paint() callback() # - render text labels texttimer = Timer("Text rendering") cr.set_line_join(1) # setting linejoin to "round" to get less artifacts on halo render for obj in data: if "text" in obj[1]: text = obj[1]["text"] #cr.set_line_width (obj[1].get("width", 1)) #cr.set_font_size(float(obj[1].get("font-size", 9))) ft_desc = pango.FontDescription() ft_desc.set_family(obj[1].get('font-family', 'sans')) ft_desc.set_size(pango.SCALE*int(obj[1].get('font-size',9))) fontstyle = obj[1].get('font-style', 'normal') if fontstyle == 'italic': fontstyle = pango.STYLE_ITALIC else: fontstyle = pango.STYLE_NORMAL ft_desc.set_style(fontstyle) fontweight = obj[1].get('font-weight', 400) try: fontweight = int(fontweight) except ValueError: if fontweight == 'bold': fontweight = 700 else: fontweight = 400 ft_desc.set_weight(fontweight) if obj[1].get('text-transform', None) == 'uppercase': text = text.upper() p_ctx = pangocairo.CairoContext(cr) p_layout = p_ctx.create_layout() p_layout.set_font_description(ft_desc) p_layout.set_text(text) p_attrs = pango.AttrList() decoration = obj[1].get('text-decoration', 'none') if decoration == 'underline': p_attrs.insert(pango.AttrUnderline(pango.UNDERLINE_SINGLE,end_index=-1)) decoration = obj[1].get('font-variant', 'none') if decoration == 'small-caps': p_attrs.insert(pango.AttrVariant(pango.VARIANT_SMALL_CAPS, start_index=0, end_index=-1)) p_layout.set_attributes(p_attrs) if obj[1].get("text-position", "center") == "center": where = self.lonlat2screen(projections.transform(obj[0].center,self.data.proj,self.proj)) for t in text_rendered_at: if ((t[0]-where[0])**2+(t[1]-where[1])**2) < 15*15: break else: text_rendered_at.add(where) #debug ("drawing text: %s at %s"%(text, where)) if "text-halo-color" in obj[1] or "text-halo-radius" in obj[1]: cr.new_path() cr.move_to(where[0], where[1]) cr.set_line_width (obj[1].get("text-halo-radius", 1)) color = obj[1].get("text-halo-color", (1.,1.,1.)) cr.set_source_rgb(color[0], color[1], color[2]) cr.text_path(text) cr.stroke() cr.new_path() cr.move_to(where[0], where[1]) cr.set_line_width (obj[1].get("text-halo-radius", 1)) color = obj[1].get("text-color", (0.,0.,0.)) cr.set_source_rgb(color[0], color[1], color[2]) cr.text_path(text) cr.fill() else: ### render text along line c = obj[0].cs text = unicode(text,"utf-8") # - calculate line length length = reduce(lambda x,y: (x[0]+((y[0]-x[1])**2 + (y[1]-x[2])**2 )**0.5, y[0], y[1]), c, (0,c[0][0],c[0][1]))[0] #print length, text, cr.text_extents(text) if length > cr.text_extents(text)[2]: # - function to get (x, y, normale) from (c, length_along_c) def get_xy_from_len(c,length_along_c): x0, y0 = c[0] for x,y in c: seg_len = ((x-x0)**2+(y-y0)**2)**0.5 if length_along_c < seg_len: normed = length_along_c /seg_len return (x-x0)*normed+x0, (y-y0)*normed+y0, math.atan2(y-y0,x-x0) else: length_along_c -= seg_len x0,y0 = x,y else: return None da = 0 os = 1 z = length/2-cr.text_extents(text)[2]/2 # print get_xy_from_len(c,z) if c[0][0] < c[1][0] and get_xy_from_len(c,z)[2]<math.pi/2 and get_xy_from_len(c,z)[2] > -math.pi/2: da = 0 os = 1 z = length/2-cr.text_extents(text)[2]/2 else: da = math.pi os = -1 z = length/2+cr.text_extents(text)[2]/2 z1=z if "text-halo-color" in obj[1] or "text-halo-radius" in obj[1]: cr.set_line_width (obj[1].get("text-halo-radius", 1.5)*2) color = obj[1].get("text-halo-color", (1.,1.,1.)) cr.set_source_rgb(color[0], color[1], color[2]) xy = get_xy_from_len(c,z) cr.save() #cr.move_to(xy[0],xy[1]) p_ctx.translate(xy[0],xy[1]) cr.rotate(xy[2]+da) #p_ctx.translate(x,y) #p_ctx.show_layout(p_layout) p_ctx.layout_path(p_layout) cr.restore() cr.stroke() #for letter in text: #cr.new_path() #xy = get_xy_from_len(c,z) ##print letter, cr.text_extents(letter) #cr.move_to(xy[0],xy[1]) #cr.save() #cr.rotate(xy[2]+da) #cr.text_path(letter) #cr.restore() #cr.stroke() #z += os*cr.text_extents(letter)[4] color = obj[1].get("text-color", (0.,0.,0.)) cr.set_source_rgb(color[0], color[1], color[2]) z = z1 xy = get_xy_from_len(c,z) cr.save() #cr.move_to(xy[0],xy[1]) p_ctx.translate(xy[0],xy[1]) cr.rotate(xy[2]+da) #p_ctx.translate(x,y) p_ctx.show_layout(p_layout) cr.restore() #for letter in text: #cr.new_path() #xy = get_xy_from_len(c,z) ##print letter, cr.text_extents(letter) #cr.move_to(xy[0],xy[1]) #cr.save() #cr.rotate(xy[2]+da) #cr.text_path(letter) #cr.restore() #cr.fill() #z += os*cr.text_extents(letter)[4] texttimer.stop() del data del layers timer.stop() rendertimer.stop() debug(self.bbox) callback(True)
self.bbox_p = (0.,0.,0.,0.) self.zoomlevel = zoomlevel self.zoom = None self.data = data_backend self.proj = raster_proj def __del__(self): del self.surface def screen2lonlat(self, x, y): lo1, la1, lo2, la2 = self.bbox_p debug ("%s %s - %s %s"%(x,y,self.w, self.h)) debug(self.bbox_p) return projections.to4326( (1.*x/self.w*(lo2-lo1)+lo1, la2+(1.*y/(self.h)*(la1-la2))),self.proj) # return (x - self.w/2)/(math.cos(self.center_coord[1]*math.pi/180)*self.zoom) + self.center_coord[0], -(y - self.h/2)/self.zoom + self.center_coord[1] def lonlat2screen(self, (lon, lat), epsg4326=False): if epsg4326: lon, lat = projections.from4326((lon,lat),self.proj) lo1, la1, lo2, la2 = self.bbox_p return ((lon-lo1)*(self.w-1)/abs(lo2-lo1), ((la2-lat)*(self.h-1)/(la2-la1))) # return (lon - self.center_coord[0])*self.lcc*self.zoom + self.w/2, -(lat - self.center_coord[1])*self.zoom + self.h/2 def update_surface_by_center(self, lonlat, zoom, style): self.zoom = zoom xy = projections.from4326(lonlat, self.proj) xy1 = projections.to4326((xy[0]-40075016*0.5**self.zoom/256*self.w, xy[1]-40075016*0.5**self.zoom/256*self.h), self.proj) xy2 = projections.to4326((xy[0]+40075016*0.5**self.zoom/256*self.w, xy[1]+40075016*0.5**self.zoom/256*self.h), self.proj) bbox = (xy1[0],xy1[1],xy2[0],xy2[1]) debug (bbox) return self.update_surface(bbox, zoom, style) def update_surface(self, bbox, zoom, style, callback = lambda x=None: None): rendertimer = Timer("Rendering image")
def get_vectors (self, bbox, zoom, sql_hint = None, tags_hint = None): """ Fetches vectors for given bbox. sql_hint is a list of sets of (key, sql_for_key) """ a = psycopg2.connect(self.database) b = a.cursor() bbox = tuple(projections.from4326(bbox,self.proj)) ### FIXME: hardcoded EPSG:3857 in database tables = ("planet_osm_line","planet_osm_polygon") # FIXME: points resp = {} for table in tables: add = "" taghint = "*" if sql_hint: adp = [] for tp in sql_hint: add = [] b.execute("SELECT * FROM %s LIMIT 1;"%table) names = [q[0] for q in b.description] for j in tp[0]: if j not in names: break else: add.append(tp[1]) if add: add = " OR ".join(add) add = "("+add+")" adp.append(add) if tags_hint: taghint = ", ".join(['"'+j+'"' for j in tags_hint if j in names])+ ", way, osm_id" adp = " OR ".join(adp) req = "SELECT %s FROM %s WHERE (%s) and way && SetSRID('BOX3D(%s %s,%s %s)'::box3d,900913);"%(taghint,table,adp,bbox[0],bbox[1],bbox[2],bbox[3]) print req b.execute(req) names = [q[0] for q in b.description] for row in b.fetchall(): row_dict = dict(map(None,names,row)) for k,v in row_dict.items(): if not v: del row_dict[k] geom = shapely.wkb.loads(row_dict["way"].decode('hex')) ### FIXME: a dirty hack to basically support polygons, needs lots of rewrite try: geom = list(geom.coords) except NotImplementedError: "trying polygons" try: geom = geom.boundary geom = list(geom.coords) row_dict[":area"] = "yes" except NotImplementedError: "multipolygon" continue ### FIXME #geom = projections.to4326(geom, self.proj) del row_dict["way"] oid = row_dict["osm_id"] del row_dict["osm_id"] w = Way(row_dict, geom) #print row_dict resp[oid] = w a.close() del a return resp