def kml_chunk(self, n, s, e, w): """ Get the kml of a lat/lon bounded part of the study region, with geometry simplified in proportion to the visible % of the region """ bounds = Polygon(LinearRing([Point(w, n), Point(e, n), Point(e, s), Point(w, s), Point(w, n)])) bounds.set_srid(4326) center_lat = bounds.centroid.y # in 4326 because it is used only for setting up the subregion calls center_lon = bounds.centroid.x # in 4326 because it is used only for setting up the subregion calls bounds.transform(settings.GEOMETRY_DB_SRID) # all longitudinal width calcs should be done in GEOMETRY_DB_SRID - 4326 can fail across the date line zoom_width = (Point(bounds.extent[0], bounds.centroid.y)).distance(Point(bounds.extent[2], bounds.centroid.y)) full_shape_width = (Point(self.geometry.extent[0], self.geometry.centroid.y)).distance(Point(self.geometry.extent[2], self.geometry.centroid.y)) # The following simplify values can be tuned to your preference # minimum geometry simplify value (highest detail) = 50 (arbitrary, based on observation) # maximum geometry simplify value = 200 (arbitrary, based on observation) # value set by pecentage of study region width requested in this chunk min_simplify_val = 50.0 max_simplify_val = 200.0 simplify_factor = max(min_simplify_val, min(max_simplify_val, max_simplify_val * zoom_width / full_shape_width)) transform_geom = self.geometry.simplify(simplify_factor, preserve_topology=True) transform_geom = transform_geom.intersection(bounds) transform_geom.transform(4326) # Debugging info #print zoom_width #print full_shape_width #print simplify_factor #print transform_geom.num_coords # End debugging info # only add sub-regions if this is not our highest detail level bLastLodLevel = simplify_factor < max_simplify_val # change this last value to build varying levels of LOD max_lod_pixels = 500 min_lod_pixels = 250 # make sure the most detailed lod stays active no matter how close user zooms if bLastLodLevel: max_lod_pixels = -1 retval = '<Region><LatLonAltBox><north>%f</north><south>%f</south><east>%f</east><west>%f</west></LatLonAltBox><Lod><minLodPixels>%f</minLodPixels><maxLodPixels>%f</maxLodPixels><minFadeExtent>0</minFadeExtent><maxFadeExtent>0</maxFadeExtent></Lod></Region>' % (n, s, e, w, min_lod_pixels, max_lod_pixels) + '<Placemark> <name>Study Region Boundaries</name><Style> <LineStyle> <color>ff00ffff</color> <width>2</width> </LineStyle> <PolyStyle> <color>8000ffff</color> </PolyStyle></Style>%s</Placemark>' % (transform_geom.kml,) # conditionally add sub-regions if not bLastLodLevel: subregions = '<Folder><name>Study Region LODs</name>' + '<Folder><name>SE</name>' + self.kml_chunk(center_lat, s, e, center_lon) + '</Folder>' subregions = subregions + '<Folder><name>NE</name>' + self.kml_chunk(n, center_lat, e, center_lon) + '</Folder>' subregions = subregions + '<Folder><name>SW</name>' + self.kml_chunk(center_lat, s, center_lon, w) + '</Folder>' subregions = subregions + '<Folder><name>NW</name>' + self.kml_chunk(n, center_lat, center_lon, w) + '</Folder>' retval = retval + subregions + '</Folder>' return retval
def setUp(self): # Make sure we're using the GB postcode functions here models.countries = countries utils.countries = countries self.postcode = models.Postcode.objects.create( postcode='SW1A1AA', location=Point(-0.141588, 51.501009) ) self.generation = models.Generation.objects.create( active=True, description="Test generation", ) self.type = models.Type.objects.create( code="TEST_TYPE", description="A test area", ) self.area = models.Area.objects.create( name="Area", type=self.type, generation_low=self.generation, generation_high=self.generation, ) polygon = Polygon(((-5, 50), (-5, 55), (1, 55), (1, 50), (-5, 50)), srid=4326) polygon.transform(settings.MAPIT_AREA_SRID) self.shape = models.Geometry.objects.create( area=self.area, polygon=polygon, )
def project_to_WGS84(self, ov_bbox, ov_crs, center=None): try: srid = int(ov_crs.split(':')[1]) srid = 3857 if srid == 900913 else srid poly = Polygon(((ov_bbox[0], ov_bbox[1]), (ov_bbox[0], ov_bbox[3]), (ov_bbox[2], ov_bbox[3]), (ov_bbox[2], ov_bbox[1]), (ov_bbox[0], ov_bbox[1])), srid=srid) if srid != 4326: gcoord = SpatialReference(4326) ycoord = SpatialReference(srid) trans = CoordTransform(ycoord, gcoord) poly.transform(trans) try: if not center: center = { "x": get_valid_number(poly.centroid.coords[0]), "y": get_valid_number(poly.centroid.coords[1]), "crs": "EPSG:4326" } zoom = GoogleZoom().get_zoom(poly) + 1 except Exception: center = (0, 0) zoom = 0 tb = traceback.format_exc() logger.debug(tb) except Exception: tb = traceback.format_exc() logger.debug(tb) return (center, zoom)
def _get_shape(self, value): points = json.loads(value) # close ring and create Polygon polygon = Polygon(points+[points[0]]) polygon.srid = SRID_WSG84 polygon.transform(SRID_RD) return polygon
def setUp(self): self.generation = Generation.objects.create( active=True, description="Test generation", ) self.area_type = Type.objects.create( code="BIG", description="A large test area", ) self.name_type = NameType.objects.create( code='O', description='Ordnance Survey name type' ) self.area = Area.objects.create( name="Big Area", type=self.area_type, generation_low=self.generation, generation_high=self.generation, ) polygon = Polygon(((-5, 50), (-5, 55), (1, 55), (1, 50), (-5, 50)), srid=4326) polygon.transform(settings.MAPIT_AREA_SRID) self.geometry = self.area.polygons.create(polygon=polygon)
def setUp(self): self.generation = Generation.objects.create( active=True, description="Test generation", ) self.area_type = Type.objects.create( code="BIG", description="A large test area", ) self.name_type = NameType.objects.create( code='O', description='Ordnance Survey name type') self.area = Area.objects.create( name="Big Area", type=self.area_type, generation_low=self.generation, generation_high=self.generation, ) polygon = Polygon(((-5, 50), (-5, 55), (1, 55), (1, 50), (-5, 50)), srid=4326) polygon.transform(settings.MAPIT_AREA_SRID) self.geometry = self.area.polygons.create(polygon=polygon)
def find(self, bbox=None, text=None, adm1=None, adm2=None, is_primary=True, threshold=0.5, srid=4326): qset = self.get_query_set().filter(is_primary=is_primary) if bbox: (minx, miny, maxx, maxy) = bbox bbox = Polygon(((minx, miny), (minx, maxy), (maxx, maxy), (maxx, miny), (minx, miny)), srid=srid) if srid != 4326: bbox.transform(4326) # convert to lon/lat qset = qset.filter(geometry__bboverlaps=bbox) if text: self.set_threshold(threshold) # use the pg_trgm index qset = qset.extra( select={"similarity": "similarity(preferred_name, %s)"}, select_params=[text], where=["preferred_name %% %s"], params=[text], order_by=["-similarity"]) if adm1: qset = qset.filter(admin1__exact=adm1) if adm2: qset = qset.filter(admin2__exact=adm2) return qset
def setUp(self): # Make sure we're using the GB postcode functions here models.countries = countries utils.countries = countries self.postcode = models.Postcode.objects.create(postcode='SW1A1AA', location=Point( -0.141588, 51.501009)) self.generation = models.Generation.objects.create( active=True, description="Test generation", ) self.type = models.Type.objects.create( code="TEST_TYPE", description="A test area", ) self.area = models.Area.objects.create( name="Area", type=self.type, generation_low=self.generation, generation_high=self.generation, ) polygon = Polygon(((-5, 50), (-5, 55), (1, 55), (1, 50), (-5, 50)), srid=4326) polygon.transform(settings.MAPIT_AREA_SRID) self.shape = models.Geometry.objects.create( area=self.area, polygon=polygon, )
def add_anonymous_boundary(request): request_dict = json.loads(request.body) srid = request_dict.get('srid', 4326) polygon = Polygon(request_dict.get('polygon', []), srid=srid) if srid != 3857: polygon.transform(3857) b = Boundary.anonymous(polygon) b.save() return {'id': b.id}
def add_anonymous_boundary(request): request_dict = json.loads(request.body) srid = request_dict.get('srid', 4326) polygon = Polygon(request_dict.get('polygon', []), srid=srid) if srid != 3857: polygon.transform(3857) b = Boundary.anonymous(polygon) b.save() return {'id': b.id}
def _shape(points): if points[0] != points[-1]: points.append(points[0]) try: poly = Polygon([Point(*p, srid=OSGB) for p in points], srid=OSGB) poly.transform(WGS) return poly except Exception, e: print points print e raise
def get_zoom(ov_bbox, ov_crs): srid = int(ov_crs.split(':')[1]) srid = 3857 if srid == 900913 else srid poly = Polygon(((ov_bbox[0], ov_bbox[1]), (ov_bbox[0], ov_bbox[3]), (ov_bbox[2], ov_bbox[3]), (ov_bbox[2], ov_bbox[1]), (ov_bbox[0], ov_bbox[1])), srid=srid) gcoord = SpatialReference(4326) ycoord = SpatialReference(srid) trans = CoordTransform(ycoord, gcoord) poly.transform(trans) zoom = GoogleZoom().get_zoom(poly) return zoom
def import_paths(filename): reader = shapefile.Reader(filename) records = reader.iterRecords() shapes = reader.iterShapes() cells = {} for record, shape in zip(records, shapes): id, x, y = record point = Point(x, y, srid=IN_SRID) point.transform(OUT_SRID) poly = Polygon(map(tuple, shape.points), srid=IN_SRID) poly.transform(OUT_SRID) cells[id] = Cell(id, point, poly) return cells
def setUp(self): self.generation = Generation.objects.create( active=True, description="Test generation", ) self.big_type = Type.objects.create( code="BIG", description="A large test area", ) self.small_type = Type.objects.create( code="SML", description="A small test area", ) self.big_area = Area.objects.create( name="Big Area", type=self.big_type, generation_low=self.generation, generation_high=self.generation, ) polygon = Polygon(((-5, 50), (-5, 55), (1, 55), (1, 50), (-5, 50)), srid=4326) polygon.transform(settings.MAPIT_AREA_SRID) self.big_shape = Geometry.objects.create( area=self.big_area, polygon=polygon) self.small_area_1 = Area.objects.create( name="Small Area 1", type=self.small_type, generation_low=self.generation, generation_high=self.generation, ) self.small_area_2 = Area.objects.create( name="Small Area 2", type=self.small_type, generation_low=self.generation, generation_high=self.generation, ) polygon = Polygon(((-4, 51), (-4, 52), (-3, 52), (-3, 51), (-4, 51)), srid=4326) polygon.transform(settings.MAPIT_AREA_SRID) self.small_shape_1 = Geometry.objects.create( area=self.small_area_1, polygon=polygon) polygon = Polygon(((-3, 51), (-3, 52), (-2, 52), (-2, 51), (-3, 51)), srid=4326) polygon.transform(settings.MAPIT_AREA_SRID) self.small_shape_2 = Geometry.objects.create( area=self.small_area_2, polygon=polygon) polygon = Polygon(((-2, 53), (-2, 54), (-1, 54), (-1, 53), (-2, 53)), srid=4326) polygon.transform(settings.MAPIT_AREA_SRID) self.small_shape_3 = Geometry.objects.create( area=self.small_area_2, polygon=polygon) self.postcode = Postcode.objects.create( postcode='P', location=Point(-3.5, 51.5))
def find(self, bbox=None, text=None, adm1=None, adm2=None, is_primary=True, threshold=0.5, srid=4326): qset = self.get_query_set().filter(is_primary=is_primary) if bbox: (minx, miny, maxx, maxy) = bbox bbox = Polygon(((minx,miny),(minx,maxy),(maxx,maxy),(maxx,miny),(minx,miny)),srid=srid) if srid != 4326: bbox.transform(4326) # convert to lon/lat qset = qset.filter(geometry__bboverlaps=bbox) if text: self.set_threshold(threshold) # use the pg_trgm index qset = qset.extra(select={"similarity":"similarity(preferred_name, %s)"}, select_params=[text], where=["preferred_name %% %s"], params=[text], order_by=["-similarity"]) if adm1: qset = qset.filter(admin1__exact=adm1) if adm2: qset = qset.filter(admin2__exact=adm2) return qset
def test_should_find_correct_parent(self): generation = models.Generation.objects.create(active=False, description="Test generation") parent_type = models.Type.objects.create(code="UTA", description="A parent test area") child_type = models.Type.objects.create(code="UTW", description="A child test area") parent_area_1 = models.Area.objects.create( name="Parent Area 1", type=parent_type, generation_low=generation, generation_high=generation) parent_area_2 = models.Area.objects.create( name="Parent Area 2", type=parent_type, generation_low=generation, generation_high=generation) child_area_1 = models.Area.objects.create( name="Child Area 1", type=child_type, generation_low=generation, generation_high=generation) child_area_2 = models.Area.objects.create( name="Child Area 2", type=child_type, generation_low=generation, generation_high=generation) polygon = Polygon(((-5, 50), (-5, 55), (1, 55), (1, 50), (-5, 50)), srid=4326) polygon.transform(settings.MAPIT_AREA_SRID) models.Geometry.objects.create(area=parent_area_1, polygon=polygon) polygon = Polygon(((1, 50), (1, 55), (5, 55), (5, 50), (1, 50)), srid=4326) polygon.transform(settings.MAPIT_AREA_SRID) models.Geometry.objects.create(area=parent_area_2, polygon=polygon) polygon = Polygon(((-4, 51), (-4, 52), (-3, 52), (-3, 51), (-4, 51)), srid=4326) polygon.transform(settings.MAPIT_AREA_SRID) models.Geometry.objects.create(area=child_area_1, polygon=polygon) polygon = Polygon(((2, 53), (2, 54), (3, 54), (3, 53), (2, 53)), srid=4326) polygon.transform(settings.MAPIT_AREA_SRID) models.Geometry.objects.create(area=child_area_2, polygon=polygon) stdout = StringIO() call_command( 'mapit_UK_find_parents', commit=True, stderr=StringIO(), stdout=stdout, ) child_area_1 = models.Area.objects.get(pk=child_area_1.id) child_area_2 = models.Area.objects.get(pk=child_area_2.id) expected = 'Parent for Child Area 1 [%d] (UTW) was None, is now Parent Area 1 [%d] (UTA)\n' % ( child_area_1.id, parent_area_1.id) expected += 'Parent for Child Area 2 [%d] (UTW) was None, is now Parent Area 2 [%d] (UTA)\n' % ( child_area_2.id, parent_area_2.id) self.assertEqual(stdout.getvalue(), expected) self.assertEqual(child_area_1.parent_area, parent_area_1) self.assertEqual(child_area_2.parent_area, parent_area_2)
def setUp(self): self.generation = Generation.objects.create( active=True, description="Test generation", ) self.big_type = Type.objects.create( code="BIG", description="A large test area", ) self.small_type = Type.objects.create( code="SML", description="A small test area", ) self.big_area = Area.objects.create( name="Big Area", type=self.big_type, generation_low=self.generation, generation_high=self.generation, ) polygon = Polygon(((-5, 50), (-5, 55), (1, 55), (1, 50), (-5, 50)), srid=4326) polygon.transform(settings.MAPIT_AREA_SRID) self.big_shape = Geometry.objects.create(area=self.big_area, polygon=polygon) self.small_area_1 = Area.objects.create( name="Small Area 1", type=self.small_type, generation_low=self.generation, generation_high=self.generation, ) self.small_area_2 = Area.objects.create( name="Small Area 2", type=self.small_type, generation_low=self.generation, generation_high=self.generation, ) polygon = Polygon(((-4, 51), (-4, 52), (-3, 52), (-3, 51), (-4, 51)), srid=4326) polygon.transform(settings.MAPIT_AREA_SRID) self.small_shape_1 = Geometry.objects.create(area=self.small_area_1, polygon=polygon) polygon = Polygon(((-3, 51), (-3, 52), (-2, 52), (-2, 51), (-3, 51)), srid=4326) polygon.transform(settings.MAPIT_AREA_SRID) self.small_shape_2 = Geometry.objects.create(area=self.small_area_2, polygon=polygon) self.postcode = Postcode.objects.create(postcode='P', location=Point(-3.5, 51.5))
def map_tile(request, layer_slug, boundary_slug, tile_zoom, tile_x, tile_y, format): if not has_imaging_library: raise Http404("Cairo is not available.") layer = get_object_or_404(MapLayer, slug=layer_slug) # Load basic parameters. try: size = int(request.GET.get('size', '256' if format not in ('json', 'jsonp') else '64')) if size not in (64, 128, 256, 512, 1024): raise ValueError() srs = int(request.GET.get('srs', '3857')) except ValueError: raise Http404("Invalid parameter.") db_srs, out_srs = get_srs(srs) # Get the bounding box for the tile, in the SRS of the output. try: tile_x = int(tile_x) tile_y = int(tile_y) tile_zoom = int(tile_zoom) except ValueError: raise Http404("Invalid parameter.") # Guess the world size. We need to know the size of the world in # order to locate the bounding box of any viewport at zoom levels # greater than zero. if "radius" not in request.GET: p = Point( (-90.0, 0.0), srid=db_srs.srid ) p.transform(out_srs) world_left = p[0]*2 world_top = -world_left world_size = -p[0] * 4.0 else: p = Point((0,0), srid=out_srs.srid ) p.transform(db_srs) p1 = Point([p[0] + 1.0, p[1] + 1.0], srid=db_srs.srid) p.transform(out_srs) p1.transform(out_srs) world_size = math.sqrt(abs(p1[0]-p[0])*abs(p1[1]-p[1])) * float(request.GET.get('radius', '50')) world_left = p[0] - world_size/2.0 world_top = p[1] + world_size/2.0 tile_world_size = world_size / math.pow(2.0, tile_zoom) p1 = Point( (world_left + tile_world_size*tile_x, world_top - tile_world_size*tile_y) ) p2 = Point( (world_left + tile_world_size*(tile_x+1), world_top - tile_world_size*(tile_y+1)) ) bbox = Polygon( ((p1[0], p1[1]),(p2[0], p1[1]),(p2[0], p2[1]),(p1[0], p2[1]),(p1[0], p1[1])), srid=out_srs.srid ) # A function to convert world coordinates in the output SRS into # pixel coordinates. blon1, blat1, blon2, blat2 = bbox.extent bx = float(size)/(blon2-blon1) by = float(size)/(blat2-blat1) def viewport(coord): # Convert the world coordinates to image coordinates according to the bounding box # (in output SRS). return float(coord[0] - blon1)*bx, (size-1) - float(coord[1] - blat1)*by # Convert the bounding box to the database SRS. db_bbox = bbox.transform(db_srs, clone=True) # What is the width of a pixel in the database SRS? If it is smaller than # SIMPLE_SHAPE_TOLERANCE, load the simplified geometry from the database. shape_field = 'shape' pixel_width = (db_bbox.extent[2]-db_bbox.extent[0]) / size / 2 if pixel_width > boundaries_settings.SIMPLE_SHAPE_TOLERANCE: shape_field = 'simple_shape' # Query for any boundaries that intersect the bounding box. boundaries = Boundary.objects.filter(set=layer.boundaryset, shape__intersects=db_bbox)\ .values("id", "slug", "name", "label_point", shape_field) if boundary_slug: boundaries = boundaries.filter(slug=boundary_slug) boundary_id_map = dict( (b["id"], b) for b in boundaries ) if len(boundaries) == 0: if format == "svg": raise Http404("No boundaries here.") elif format in ("png", "gif"): # Send a 1x1 transparent image. Google is OK getting 404s for map tile images # but OpenLayers isn't. Maybe cache the image? im = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1, 1) ctx = cairo.Context(im) buf = StringIO() im.write_to_png(buf) v = buf.getvalue() if format == "gif": v = convert_png_to_gif(v) r = HttpResponse(v, content_type='image/' + format) r["Content-Length"] = len(v) return r elif format == "json": # Send an empty "UTF-8 Grid"-like response. return HttpResponse('{"error":"nothing-here"}', content_type="application/json") elif format == "jsonp": # Send an empty "UTF-8 Grid"-like response. return HttpResponse(request.GET.get("callback", "callback") + '({"error":"nothing-here"})', content_type="text/javascript") # Query for layer style information and then set it on the boundary objects. styles = layer.boundaries.filter(boundary__in=boundary_id_map.keys()) for style in styles: boundary_id_map[style.boundary_id]["style"] = style # Create the image buffer. if format in ('png', 'gif'): im = cairo.ImageSurface(cairo.FORMAT_ARGB32, size, size) elif format == 'svg': buf = StringIO() im = cairo.SVGSurface(buf, size, size) elif format in ('json', 'jsonp'): # This is going to be a "UTF-8 Grid"-like response, but we generate that # info by first creating an actual image, with colors coded by index to # represent which boundary covers which pixels. im = cairo.ImageSurface(cairo.FORMAT_RGB24, size, size) # Color helpers. def get_rgba_component(c): return c if isinstance(c, float) else c/255.0 def get_rgba_tuple(clr, alpha=.25): # Colors are specified as tuples/lists with 3 (RGB) or 4 (RGBA) # components. Components that are float values must be in the # range 0-1, while all other values are in the range 0-255. # Because .gif does not support partial transparency, alpha values # are forced to 1. return (get_rgba_component(clr[0]), get_rgba_component(clr[1]), get_rgba_component(clr[2]), get_rgba_component(clr[3]) if len(clr) == 4 and format != 'gif' else (alpha if format != 'gif' else 1.0)) # Create the drawing surface. ctx = cairo.Context(im) ctx.select_font_face(maps_settings.MAP_LABEL_FONT, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) if format in ('json', 'jsonp'): # For the UTF-8 Grid response, turn off anti-aliasing since the color we draw to each pixel # is a code for what is there. ctx.set_antialias(cairo.ANTIALIAS_NONE) def max_extent(shape): a, b, c, d = shape.extent return max(c-a, d-b) # Transform the boundaries to output coordinates. draw_shapes = [] for bdry in boundaries: if not "style" in bdry: continue # Boundary had no corresponding MapLayerBoundary shape = bdry[shape_field] # Simplify to the detail that could be visible in the output. Although # simplification may be a little expensive, drawing a more complex # polygon is even worse. try: shape = shape.simplify(pixel_width, preserve_topology=True) except: # GEOSException pass # try drawing original # Make sure the results are all MultiPolygons for consistency. if shape.__class__.__name__ == 'Polygon': shape = MultiPolygon((shape,), srid=db_srs.srid) else: # Be sure to override SRS (for Google, see above). This code may # never execute? shape = MultiPolygon(list(shape), srid=db_srs.srid) # Is this shape too small to be visible? ext_dim = max_extent(shape) if ext_dim < pixel_width: continue # Convert the shape to the output SRS. shape.transform(out_srs) draw_shapes.append( (len(draw_shapes), bdry, shape, ext_dim) ) # Draw shading, for each linear ring of each polygon in the multipolygon. for i, bdry, shape, ext_dim in draw_shapes: if not bdry["style"].color and format not in ('json', 'jsonp'): continue for polygon in shape: for ring in polygon: # should just be one since no shape should have holes? color = bdry["style"].color if format in ('json', 'jsonp'): # We're returning a "UTF-8 Grid" indicating which feature is at # each pixel location on the grid. In order to compute the grid, # we draw to an image surface with a distinct color for each feature. # Then we convert the pixel data into the UTF-8 Grid format. ctx.set_source_rgb(*[ (((i+1)/(256**exp)) % 256)/255.0 for exp in xrange(3) ]) elif isinstance(color, (tuple, list)): # Specify a 3/4-tuple (or list) for a solid color. ctx.set_source_rgba(*get_rgba_tuple(color)) elif isinstance(color, dict): # Specify a dict of the form { "color1": (R,G,B), "color2": (R,G,B) } to # create a solid fill of color1 plus smaller stripes of color2. if color.get("color", None) != None: ctx.set_source_rgba(*get_rgba_tuple(color["color"])) elif color.get("color1", None) != None and color.get("color2", None) != None: pat = cairo.LinearGradient(0.0, 0.0, size, size) for x in xrange(0,size, 32): # divisor of the size so gradient ends at the end pat.add_color_stop_rgba(*([float(x)/size] + list(get_rgba_tuple(color["color1"], alpha=.3)))) pat.add_color_stop_rgba(*([float(x+28)/size] + list(get_rgba_tuple(color["color1"], alpha=.3)))) pat.add_color_stop_rgba(*([float(x+28)/size] + list(get_rgba_tuple(color["color2"], alpha=.4)))) pat.add_color_stop_rgba(*([float(x+32)/size] + list(get_rgba_tuple(color["color2"], alpha=.4)))) ctx.set_source(pat) else: continue # skip fill else: continue # Unknown color data structure. ctx.new_path() for pt in ring.coords: ctx.line_to(*viewport(pt)) ctx.fill() # Draw outlines, for each linear ring of each polygon in the multipolygon. for i, bdry, shape, ext_dim in draw_shapes: if format in ('json', 'jsonp'): continue if ext_dim < pixel_width * 3: continue # skip outlines if too small color = bdry["style"].color for polygon in shape: for ring in polygon: # should just be one since no shape should have holes? ctx.new_path() for pt in ring.coords: ctx.line_to(*viewport(pt)) if not isinstance(color, dict) or not "border" in color or not "width" in color["border"]: if ext_dim < pixel_width * 60: ctx.set_line_width(1) else: ctx.set_line_width(2.5) else: ctx.set_line_width(color["border"]["width"]) if not isinstance(color, dict) or not "border" in color or not "color" in color["border"]: ctx.set_source_rgba(.3,.3,.3, .75) # grey, semi-transparent else: ctx.set_source_rgba(*get_rgba_tuple(color["border"]["color"], alpha=.75)) ctx.stroke_preserve() # Draw labels. for i, bdry, shape, ext_dim in draw_shapes: if format in ('json', 'jsonp'): continue if ext_dim < pixel_width * 20: continue color = bdry["style"].color if isinstance(color, dict) and "label" in color and color["label"] == None: continue # Get the location of the label stored in the database, or fall back to # GDAL routine point_on_surface to get a point quickly. if bdry["style"].label_point: # Override the SRS on the point (for Google, see above). Then transform # it to world coordinates. pt = Point(tuple(bdry["style"].label_point), srid=db_srs.srid) pt.transform(out_srs) elif bdry["label_point"]: # Same transformation as above. pt = Point(tuple(bdry["label_point"]), srid=db_srs.srid) pt.transform(out_srs) else: # No label_point is specified so try to find one by using the # point_on_surface to find a point that is in the shape and # in the viewport's bounding box. try: pt = bbox.intersection(shape).point_on_surface except: # Don't know why this would fail. Bad geometry of some sort. # But we really don't want to leave anything unlabeled so # try the center of the bounding box. pt = bbox.centroid if not shape.contains(pt): continue # Transform to world coordinates and ensure it is within the bounding box. if not bbox.contains(pt): # If it's not in the bounding box and the shape occupies most of this # bounding box, try moving the point to somewhere in the current tile. try: inters = bbox.intersection(shape) if inters.area < bbox.area/3: continue pt = inters.point_on_surface except: continue pt = viewport(pt) txt = bdry["name"] if isinstance(bdry["style"].metadata, dict): txt = bdry["style"].metadata.get("label", txt) if ext_dim > size * pixel_width: ctx.set_font_size(18) else: ctx.set_font_size(12) x_off, y_off, tw, th = ctx.text_extents(txt)[:4] # Is it within the rough bounds of the shape and definitely the bounds of this tile? if tw < ext_dim/pixel_width/5 and th < ext_dim/pixel_width/5 \ and pt[0]-x_off-tw/2-4 > 0 and pt[1]-th-4 > 0 and pt[0]-x_off+tw/2+7 < size and pt[1]+6 < size: # Draw the background rectangle behind the text. ctx.set_source_rgba(0,0,0,.55) # black, some transparency ctx.new_path() ctx.line_to(pt[0]-x_off-tw/2-4,pt[1]-th-4) ctx.rel_line_to(tw+9, 0) ctx.rel_line_to(0, +th+8) ctx.rel_line_to(-tw-9, 0) ctx.fill() # Now a drop shadow (also is partially behind the first rectangle). ctx.set_source_rgba(0,0,0,.3) # black, some transparency ctx.new_path() ctx.line_to(pt[0]-x_off-tw/2-4,pt[1]-th-4) ctx.rel_line_to(tw+11, 0) ctx.rel_line_to(0, +th+10) ctx.rel_line_to(-tw-11, 0) ctx.fill() # Draw the text. ctx.set_source_rgba(1,1,1,1) # white ctx.move_to(pt[0]-x_off-tw/2,pt[1]) ctx.show_text(txt) if format in ("png", "gif"): # Convert the image buffer to raw bytes. buf = StringIO() im.write_to_png(buf) v = buf.getvalue() if format == "gif": v = convert_png_to_gif(v) # Form the response. r = HttpResponse(v, content_type='image/' + format) r["Content-Length"] = len(v) elif format == "svg": im.finish() v = buf.getvalue() r = HttpResponse(v, content_type='image/svg+xml') r["Content-Length"] = len(v) elif format in ('json', 'jsonp'): # Get the bytes, which are RGBA sequences. buf1 = list(im.get_data()) # Convert the 4-byte sequences back into integers that refer back to # the boundary list. Count the number of pixels for each shape. shapeidx = [] shapecount = { } for i in xrange(0, size*size): b = ord(buf1[i*4+2])*(256**0) + ord(buf1[i*4+1])*(256**1) + ord(buf1[i*4+0])*(256**2) shapeidx.append(b) if b > 0: shapecount[b] = shapecount.get(b, 0) + 1 # Assign low unicode code points to the most frequently occuring pixel values, # except always map zero to character 32. shapecode1 = { } shapecode2 = { } for k, count in sorted(shapecount.items(), key = lambda kv : kv[1]): b = len(shapecode1) + 32 + 1 if b >= 34: b += 1 if b >= 92: b += 1 shapecode1[k] = b shapecode2[b] = draw_shapes[k-1] buf = '' if format == 'jsonp': buf += request.GET.get("callback", "callback") + "(\n" buf += '{"grid":[' for row in xrange(size): if row > 0: buf += ",\n " buf += json.dumps(u"".join(unichr(shapecode1[k] if k != 0 else 32) for k in shapeidx[row*size:(row+1)*size])) buf += "],\n" buf += ' "keys":' + json.dumps([""] + [shapecode2[k][1]["slug"] for k in sorted(shapecode2)], separators=(',', ':')) + ",\n" buf += ' "data":' + json.dumps(dict( (shapecode2[k][1]["slug"], { "name": shapecode2[k][1]["name"], }) for k in sorted(shapecode2)), separators=(',', ':')) buf += "}" if format == 'jsonp': buf += ")" if format == "json": r = HttpResponse(buf, content_type='application/json') else: r = HttpResponse(buf, content_type='text/javascript') return r
def get_or_create_pngserie_with_defaultlegend_from_old_results(scenario, pt): log.debug('!!!get_or_create_pngserie_with_defaultlegend_from_old_results') result = Result.objects.filter( scenario=scenario, resulttype__resulttype_presentationtype__presentationtype=pt) if result.count() > 0: #check if layer is already there result = result[0] log.info('result found') pl, pl_new = PresentationLayer.objects.get_or_create( presentationtype=pt, scenario_presentationlayer__scenario=scenario, defaults={"value": result.value}) if pl_new: Scenario_PresentationLayer.objects.create( scenario=scenario, presentationlayer=pl) log.info("pl_new id: " + str(pl.id)) pl.value = result.value pl.save() try: log.info(result.resultpngloc) dest_dir = Setting.objects.get(key='DESTINATION_DIR').value presentation_dir = Setting.objects.get( key='PRESENTATION_DIR').value if result.resultpngloc is not None: log.debug('read grid information from pgw en png file!') resultpngloc = result.resultpngloc.replace('\\', '/') png_name = os.path.join(dest_dir, resultpngloc) if result.resulttype.overlaytype == 'ANIMATEDMAPOVERLAY': numberstring = '%4i' % result.firstnr numberstring = numberstring.replace(" ", "0") png_name = png_name.replace('####', numberstring) pgw_name = png_name.replace(".png", ".pgw") pgwfile = open(pgw_name, 'r') pgwfields = pgwfile.readlines() pgwfile.close() gridsize, a, b, c, west, north = [float(s) for s in pgwfields] picture = Image.open(png_name) width, height = picture.size east = west + width * gridsize south = north - height * gridsize old_file = resultpngloc.split('/') output_dir_name = os.path.join( 'flooding', 'scenario', str(scenario.id), old_file[-2]) output_file_name = os.path.join(output_dir_name, old_file[-1]) s_dir = os.path.join(dest_dir, os.path.dirname(resultpngloc)) #destination dir d_dir = os.path.join(presentation_dir, output_dir_name) if os.path.isdir(d_dir): rmtree(d_dir) # or move(presentation_dir + # output_dir_name, presentation_dir # + output_dir_name + '_old') log.debug('source dir is ' + str(s_dir)) log.debug('destination dir is ' + str(d_dir)) copytree(s_dir, d_dir) if result.resulttype.overlaytype == 'ANIMATEDMAPOVERLAY': stype = PresentationSource.SOURCE_TYPE_SERIE_PNG_FILES else: stype = PresentationSource.SOURCE_TYPE_PNG_FILE source, new = PresentationSource.objects.get_or_create( file_location=output_file_name, type=stype) source.t_source = datetime.datetime.now() source.save() grid, new = PresentationGrid.objects.get_or_create( presentationlayer=pl, defaults={ 'rownr': height, 'colnr': width, 'gridsize': gridsize}) grid.bbox_orignal_srid = 28992 grid.png_default_legend = source #left lower ll = Point(south, west, srid=grid.bbox_orignal_srid) #right upper ru = Point(north, east, srid=grid.bbox_orignal_srid) poly = Polygon([ll, ru, ru, ll]) poly.transform(4326) grid.extent = MultiPolygon(poly) grid.save() if result.resulttype.overlaytype == 'ANIMATEDMAPOVERLAY': if not result.startnr: result.startnr = result.firstnr animation, new = Animation.objects.get_or_create( presentationlayer=pl, defaults={ 'firstnr': result.firstnr, 'lastnr': result.lastnr, 'startnr': result.startnr, 'delta_timestep': (1 / 24)}) animation.firstnr = result.firstnr animation.lastnr = result.lastnr animation.startnr = result.startnr log.debug( "save animation with numbers %i tot %i" % (result.firstnr, result.lastnr)) animation.save() except IOError as e: log.error('error creating source') log.error(','.join(map(str, e.args))) if pl_new: pl.delete()
def get_or_create_pngserie_with_defaultlegend_from_old_results(scenario, pt): log.debug('!!!get_or_create_pngserie_with_defaultlegend_from_old_results') result = Result.objects.filter( scenario=scenario, resulttype__resulttype_presentationtype__presentationtype=pt) if result.count() > 0: #check if layer is already there result = result[0] log.info('result found') pl, pl_new = PresentationLayer.objects.get_or_create( presentationtype=pt, scenario_presentationlayer__scenario=scenario, defaults={"value": result.value}) if pl_new: Scenario_PresentationLayer.objects.create(scenario=scenario, presentationlayer=pl) log.info("pl_new id: " + str(pl.id)) pl.value = result.value pl.save() try: log.info(result.resultpngloc) dest_dir = Setting.objects.get(key='DESTINATION_DIR').value # Make sure every directory separator is consistent #dest_dir = dest_dir.replace('\\', os.sep).replace('/', os.sep) presentation_dir = Setting.objects.get( key='PRESENTATION_DIR').value #presentation_dir = presentation_dir.replace('\\', os.sep).replace('/', os.sep) if result.resultpngloc is not None: log.debug('read grid information from pgw en png file!') resultpngloc = result.resultpngloc.replace('\\', '/') png_name = os.path.join(dest_dir, resultpngloc) if result.resulttype.overlaytype == 'ANIMATEDMAPOVERLAY': numberstring = '%4i' % result.firstnr numberstring = numberstring.replace(" ", "0") png_name = png_name.replace('####', numberstring) pgw_name = png_name.replace(".png", ".pgw") pgwfile = open(pgw_name, 'r') pgwfields = pgwfile.readlines() pgwfile.close() gridsize, a, b, c, west, north = [float(s) for s in pgwfields] picture = Image.open(png_name) width, height = picture.size east = west + width * gridsize south = north - height * gridsize old_file = resultpngloc.split('/') output_dir_name = os.path.join('flooding', 'scenario', str(scenario.id), old_file[-2]) output_file_name = os.path.join(output_dir_name, old_file[-1]) s_dir = os.path.join(dest_dir, os.path.dirname(resultpngloc)).replace( '\\', os.sep) #destination dir d_dir = os.path.join(presentation_dir, output_dir_name).replace('\\', os.sep) if os.path.isdir(d_dir): rmtree(d_dir) # or move(presentation_dir + # output_dir_name, presentation_dir # + output_dir_name + '_old') log.debug('source dir is {0}'.format(s_dir.encode('utf-8'))) log.debug('destination dir is {0}'.format( d_dir.encode('utf-8'))) copytree(s_dir, d_dir) if result.resulttype.overlaytype == 'ANIMATEDMAPOVERLAY': stype = PresentationSource.SOURCE_TYPE_SERIE_PNG_FILES else: stype = PresentationSource.SOURCE_TYPE_PNG_FILE source, new = PresentationSource.objects.get_or_create( file_location=output_file_name, type=stype) source.t_source = datetime.datetime.now() source.save() grid, new = PresentationGrid.objects.get_or_create( presentationlayer=pl, defaults={ 'rownr': height, 'colnr': width, 'gridsize': gridsize }) grid.bbox_orignal_srid = 28992 grid.png_default_legend = source #left lower ll = Point(south, west, srid=grid.bbox_orignal_srid) #right upper ru = Point(north, east, srid=grid.bbox_orignal_srid) poly = Polygon([ll, ru, ru, ll]) poly.transform(4326) grid.extent = MultiPolygon(poly) grid.save() if result.resulttype.overlaytype == 'ANIMATEDMAPOVERLAY': if not result.startnr: result.startnr = result.firstnr animation, new = Animation.objects.get_or_create( presentationlayer=pl, defaults={ 'firstnr': result.firstnr, 'lastnr': result.lastnr, 'startnr': result.startnr, 'delta_timestep': (1 / 24) }) animation.firstnr = result.firstnr animation.lastnr = result.lastnr animation.startnr = result.startnr log.debug("save animation with numbers %i tot %i" % (result.firstnr, result.lastnr)) animation.save() except IOError as e: log.error('error creating source') log.error(','.join(map(str, e.args))) if pl_new: pl.delete()
def output_kml(point_index_and_triangle_indices): point_index, triangle_indices = point_index_and_triangle_indices centre_x = x[point_index] centre_y = y[point_index] position_tuple = centre_x, centre_y if len(triangle_indices) < 3: # Skip any point with fewer than 3 triangle_indices return if position_tuple in position_to_postcodes: postcodes = sorted(position_to_postcodes[position_tuple]) file_basename = postcodes[0] outcode = file_basename.split()[0] else: postcodes = [] file_basename = 'point-{0:09d}'.format(point_index) outcode = 'points-at-infinity' mkdir_p(join(postcodes_output_directory, outcode)) leafname = file_basename + ".kml" if len(postcodes) > 1: json_leafname = file_basename + ".json" with open(join(postcodes_output_directory, outcode, json_leafname), "w") as fp: json.dump(postcodes, fp) kml_filename = join(postcodes_output_directory, outcode, leafname) if not os.path.exists(kml_filename): circumcentres = [ccs[i] for i in triangle_indices] def compare_points(a, b): ax = a[0] - centre_x ay = a[1] - centre_y bx = b[0] - centre_x by = b[1] - centre_y angle_a = math.atan2(ay, ax) angle_b = math.atan2(by, bx) result = angle_b - angle_a if result > 0: return 1 elif result < 0: return -1 return 0 sccs = np.array(sorted(circumcentres, cmp=compare_points)) xs = [cc[0] for cc in sccs] ys = [cc[1] for cc in sccs] border = [] for i in range(0, len(sccs) + 1): index_to_use = i if i == len(sccs): index_to_use = 0 cc = (float(xs[index_to_use]), float(ys[index_to_use])) border.append(cc) polygon = Polygon(border, srid=27700) wgs_84_polygon = polygon.transform(4326, clone=True) # If the polygon isn't valid after transformation, try to # fix it. (There is one such case.) if not wgs_84_polygon.valid: tqdm.write("Warning: had to fix polygon {0}".format(kml_filename)) wgs_84_polygon = fix_invalid_geos_geometry(wgs_84_polygon) requires_clipping = polygon_requires_clipping(wgs_84_polygon) if requires_clipping: try: if wgs_84_polygon.intersects(uk_multipolygon): clipped_polygon = wgs_84_polygon.intersection(uk_multipolygon) else: clipped_polygon = wgs_84_polygon except Exception, e: tqdm.write("Got exception when generating:", kml_filename) tqdm.write("The exception was:", e) tqdm.write("The polygon's KML was:", wgs_84_polygon.kml) clipped_polygon = wgs_84_polygon else: clipped_polygon = wgs_84_polygon output_boundary_kml(kml_filename, requires_clipping, postcodes, clipped_polygon)
def kml_chunk(self, n, s, e, w): """ Get the kml of a lat/lon bounded part of the study region, with geometry simplified in proportion to the visible % of the region """ bounds = Polygon( LinearRing([ Point(w, n), Point(e, n), Point(e, s), Point(w, s), Point(w, n) ])) bounds.set_srid(4326) center_lat = bounds.centroid.y # in 4326 because it is used only for setting up the subregion calls center_lon = bounds.centroid.x # in 4326 because it is used only for setting up the subregion calls bounds.transform(settings.GEOMETRY_DB_SRID) # all longitudinal width calcs should be done in GEOMETRY_DB_SRID - 4326 can fail across the date line zoom_width = (Point(bounds.extent[0], bounds.centroid.y)).distance( Point(bounds.extent[2], bounds.centroid.y)) full_shape_width = (Point(self.geometry.extent[0], self.geometry.centroid.y)).distance( Point(self.geometry.extent[2], self.geometry.centroid.y)) # The following simplify values can be tuned to your preference # minimum geometry simplify value (highest detail) = 50 (arbitrary, based on observation) # maximum geometry simplify value = 200 (arbitrary, based on observation) # value set by pecentage of study region width requested in this chunk min_simplify_val = 50.0 max_simplify_val = 200.0 simplify_factor = max( min_simplify_val, min(max_simplify_val, max_simplify_val * zoom_width / full_shape_width)) transform_geom = self.geometry.simplify(simplify_factor, preserve_topology=True) transform_geom = transform_geom.intersection(bounds) transform_geom.transform(4326) # Debugging info #print zoom_width #print full_shape_width #print simplify_factor #print transform_geom.num_coords # End debugging info # only add sub-regions if this is not our highest detail level bLastLodLevel = simplify_factor < max_simplify_val # change this last value to build varying levels of LOD max_lod_pixels = 500 min_lod_pixels = 250 # make sure the most detailed lod stays active no matter how close user zooms if bLastLodLevel: max_lod_pixels = -1 retval = '<Region><LatLonAltBox><north>%f</north><south>%f</south><east>%f</east><west>%f</west></LatLonAltBox><Lod><minLodPixels>%f</minLodPixels><maxLodPixels>%f</maxLodPixels><minFadeExtent>0</minFadeExtent><maxFadeExtent>0</maxFadeExtent></Lod></Region>' % ( n, s, e, w, min_lod_pixels, max_lod_pixels ) + '<Placemark> <name>Study Region Boundaries</name><Style> <LineStyle> <color>ff00ffff</color> <width>2</width> </LineStyle> <PolyStyle> <color>8000ffff</color> </PolyStyle></Style>%s</Placemark>' % ( transform_geom.kml, ) # conditionally add sub-regions if not bLastLodLevel: subregions = '<Folder><name>Study Region LODs</name>' + '<Folder><name>SE</name>' + self.kml_chunk( center_lat, s, e, center_lon) + '</Folder>' subregions = subregions + '<Folder><name>NE</name>' + self.kml_chunk( n, center_lat, e, center_lon) + '</Folder>' subregions = subregions + '<Folder><name>SW</name>' + self.kml_chunk( center_lat, s, center_lon, w) + '</Folder>' subregions = subregions + '<Folder><name>NW</name>' + self.kml_chunk( n, center_lat, center_lon, w) + '</Folder>' retval = retval + subregions + '</Folder>' return retval