def _transform(self, src_img, src_bbox, dst_size, dst_bbox, image_opts): """ Do a 'real' transformation with a transformed mesh (see above). """ src_bbox = self.src_srs.align_bbox(src_bbox) dst_bbox = self.dst_srs.align_bbox(dst_bbox) src_size = src_img.size src_quad = (0, 0, src_size[0], src_size[1]) dst_quad = (0, 0, dst_size[0], dst_size[1]) to_src_px = make_lin_transf(src_bbox, src_quad) to_dst_w = make_lin_transf(dst_quad, dst_bbox) meshes = [] def dst_quad_to_src(quad): src_quad = [] for dst_px in [(quad[0], quad[1]), (quad[0], quad[3]), (quad[2], quad[3]), (quad[2], quad[1])]: dst_w = to_dst_w((dst_px[0] + 0.5, dst_px[1] + 0.5)) src_w = self.dst_srs.transform_to(self.src_srs, dst_w) src_px = to_src_px(src_w) src_quad.extend(src_px) return quad, src_quad mesh_div = self.mesh_div while mesh_div > 1 and (dst_size[0] / mesh_div < 10 or dst_size[1] / mesh_div < 10): mesh_div -= 1 for quad in griddify(dst_quad, mesh_div): meshes.append(dst_quad_to_src(quad)) result = src_img.as_image().transform(dst_size, Image.MESH, meshes, image_filter[image_opts.resampling]) return ImageSource(result, size=dst_size, image_opts=image_opts)
def _transform(self, src_img, src_bbox, dst_size, dst_bbox, image_opts): """ Do a 'real' transformation with a transformed mesh (see above). """ src_bbox = self.src_srs.align_bbox(src_bbox) dst_bbox = self.dst_srs.align_bbox(dst_bbox) src_size = src_img.size src_quad = (0, 0, src_size[0], src_size[1]) dst_quad = (0, 0, dst_size[0], dst_size[1]) to_src_px = make_lin_transf(src_bbox, src_quad) to_dst_w = make_lin_transf(dst_quad, dst_bbox) meshes = [] def dst_quad_to_src(quad): src_quad = [] for dst_px in [(quad[0], quad[1]), (quad[0], quad[3]), (quad[2], quad[3]), (quad[2], quad[1])]: dst_w = to_dst_w((dst_px[0] + 0.5, dst_px[1] + 0.5)) src_w = self.dst_srs.transform_to(self.src_srs, dst_w) src_px = to_src_px(src_w) src_quad.extend(src_px) return quad, src_quad mesh_div = self.mesh_div while mesh_div > 1 and (dst_size[0] / mesh_div < 10 or dst_size[1] / mesh_div < 10): mesh_div -= 1 for quad in griddify(dst_quad, mesh_div): meshes.append(dst_quad_to_src(quad)) result = src_img.as_image().transform( dst_size, Image.MESH, meshes, image_filter[image_opts.resampling]) return ImageSource(result, size=dst_size, image_opts=image_opts)
def _get_transformed_query(self, query): """ Handle FI requests for unsupported SRS. """ req_srs = query.srs req_bbox = query.bbox req_coord = make_lin_transf((0, 0, query.size[0], query.size[1]), req_bbox)(query.pos) info_srs = self._best_supported_srs(req_srs) info_bbox = req_srs.transform_bbox_to(info_srs, req_bbox) # calculate new info_size to keep square pixels after transform_bbox_to info_aratio = (info_bbox[3] - info_bbox[1])/(info_bbox[2] - info_bbox[0]) info_size = query.size[0], int(info_aratio*query.size[0]) info_coord = req_srs.transform_to(info_srs, req_coord) info_pos = make_lin_transf((info_bbox), (0, 0, info_size[0], info_size[1]))(info_coord) info_pos = int(round(info_pos[0])), int(round(info_pos[1])) info_query = InfoQuery( bbox=info_bbox, size=info_size, srs=info_srs, pos=info_pos, info_format=query.info_format, feature_count=query.feature_count, ) return info_query
def _get_transformed_query(self, query): """ Handle FI requests for unsupported SRS. """ req_srs = query.srs req_bbox = query.bbox req_coord = make_lin_transf((0, 0, query.size[0], query.size[1]), req_bbox)(query.pos) info_srs = self.supported_srs.best_srs(req_srs) info_bbox = req_srs.transform_bbox_to(info_srs, req_bbox) # calculate new info_size to keep square pixels after transform_bbox_to info_aratio = (info_bbox[3] - info_bbox[1]) / (info_bbox[2] - info_bbox[0]) info_size = query.size[0], int(info_aratio * query.size[0]) info_coord = req_srs.transform_to(info_srs, req_coord) info_pos = make_lin_transf( (info_bbox), (0, 0, info_size[0], info_size[1]))(info_coord) info_pos = int(round(info_pos[0])), int(round(info_pos[1])) info_query = InfoQuery( bbox=info_bbox, size=info_size, srs=info_srs, pos=info_pos, info_format=query.info_format, feature_count=query.feature_count, ) return info_query
def image_mask_from_geom(size, bbox, polygons): mask = Image.new('L', size, 255) if len(polygons) == 0: return mask transf = make_lin_transf(bbox, (0, 0) + size) # use negative ~.1 pixel buffer buffer = -0.1 * min((bbox[2] - bbox[0]) / size[0], (bbox[3] - bbox[1]) / size[1]) draw = ImageDraw.Draw(mask) def draw_polygon(p): draw.polygon([transf(coord) for coord in p.exterior.coords], fill=0) for ring in p.interiors: draw.polygon([transf(coord) for coord in ring.coords], fill=255) for p in polygons: # little bit smaller polygon does not include touched pixels outside coverage buffered = p.buffer(buffer, resolution=1, join_style=2) if buffered.is_empty: # can be empty after negative buffer continue if buffered.type == 'MultiPolygon': # negative buffer can turn polygon into multipolygon for p in buffered: draw_polygon(p) else: draw_polygon(buffered) return mask
def _transform_simple(self, src_img, src_bbox, dst_size, dst_bbox, image_opts): """ Do a simple crop/extent transformation. """ src_quad = (0, 0, src_img.size[0], src_img.size[1]) to_src_px = make_lin_transf(src_bbox, src_quad) minx, miny = to_src_px((dst_bbox[0], dst_bbox[3])) maxx, maxy = to_src_px((dst_bbox[2], dst_bbox[1])) src_res = ((src_bbox[0]-src_bbox[2])/src_img.size[0], (src_bbox[1]-src_bbox[3])/src_img.size[1]) dst_res = ((dst_bbox[0]-dst_bbox[2])/dst_size[0], (dst_bbox[1]-dst_bbox[3])/dst_size[1]) tenth_px_res = (abs(dst_res[0]/(dst_size[0]*10)), abs(dst_res[1]/(dst_size[1]*10))) if (abs(src_res[0]-dst_res[0]) < tenth_px_res[0] and abs(src_res[1]-dst_res[1]) < tenth_px_res[1]): # rounding might result in subpixel inaccuracy # this exact resolutioni match should only happen in clients with # fixed resolutions like OpenLayers minx = int(round(minx)) miny = int(round(miny)) result = src_img.as_image().crop((minx, miny, minx+dst_size[0], miny+dst_size[1])) else: img = img_for_resampling(src_img.as_image(), image_opts.resampling) result = img.transform(dst_size, Image.EXTENT, (minx, miny, maxx, maxy), image_filter[image_opts.resampling]) return ImageSource(result, size=dst_size, image_opts=image_opts)
def _get_transformed_query(self, query): """ Handle FI requests for unsupported SRS. """ req_srs = query.srs req_bbox = query.bbox info_srs = self._best_supported_srs(req_srs) info_bbox = req_srs.transform_bbox_to(info_srs, req_bbox) req_coord = make_lin_transf((0, query.size[1], query.size[0], 0), req_bbox)(query.pos) info_coord = req_srs.transform_to(info_srs, req_coord) info_pos = make_lin_transf((info_bbox), (0, query.size[1], query.size[0], 0))(info_coord) info_query = InfoQuery(info_bbox, query.size, info_srs, info_pos, query.info_format) return info_query
def _transform_simple(self, src_img, src_bbox, dst_size, dst_bbox, image_opts): """ Do a simple crop/extent transformation. """ src_quad = (0, 0, src_img.size[0], src_img.size[1]) to_src_px = make_lin_transf(src_bbox, src_quad) minx, miny = to_src_px((dst_bbox[0], dst_bbox[3])) maxx, maxy = to_src_px((dst_bbox[2], dst_bbox[1])) src_res = ((src_bbox[0] - src_bbox[2]) / src_img.size[0], (src_bbox[1] - src_bbox[3]) / src_img.size[1]) dst_res = ((dst_bbox[0] - dst_bbox[2]) / dst_size[0], (dst_bbox[1] - dst_bbox[3]) / dst_size[1]) tenth_px_res = (abs(dst_res[0] / (dst_size[0] * 10)), abs(dst_res[1] / (dst_size[1] * 10))) if abs(src_res[0] - dst_res[0]) < tenth_px_res[0] and abs(src_res[1] - dst_res[1]) < tenth_px_res[1]: # rounding might result in subpixel inaccuracy # this exact resolutioni match should only happen in clients with # fixed resolutions like OpenLayers minx = int(round(minx)) miny = int(round(miny)) result = src_img.as_image().crop((minx, miny, minx + dst_size[0], miny + dst_size[1])) else: result = src_img.as_image().transform( dst_size, Image.EXTENT, (minx, miny, maxx, maxy), image_filter[image_opts.resampling] ) return ImageSource(result, size=dst_size, image_opts=image_opts)
def _get_transformed_query(self, query): """ Handle FI requests for unsupported SRS. """ req_srs = query.srs req_bbox = query.bbox info_srs = self._best_supported_srs(req_srs) info_bbox = req_srs.transform_bbox_to(info_srs, req_bbox) req_coord = make_lin_transf((0, query.size[1], query.size[0], 0), req_bbox)(query.pos) info_coord = req_srs.transform_to(info_srs, req_coord) info_pos = make_lin_transf( (info_bbox), (0, query.size[1], query.size[0], 0))(info_coord) info_query = InfoQuery(info_bbox, query.size, info_srs, info_pos, query.info_format) return info_query
def _transform(self, src_img, src_bbox, dst_size, dst_bbox, image_opts): """ Do a 'real' transformation with a transformed mesh (see above). """ src_bbox = self.src_srs.align_bbox(src_bbox) dst_bbox = self.dst_srs.align_bbox(dst_bbox) src_size = src_img.size src_quad = (0, 0, src_size[0], src_size[1]) dst_quad = (0, 0, dst_size[0], dst_size[1]) to_src_px = make_lin_transf(src_bbox, src_quad) to_dst_w = make_lin_transf(dst_quad, dst_bbox) meshes = [] # more recent versions of Pillow use center coordinates for # transformations, we manually need to add half a pixel otherwise if transform_uses_center(): px_offset = 0.0 else: px_offset = 0.5 def dst_quad_to_src(quad): src_quad = [] for dst_px in [(quad[0], quad[1]), (quad[0], quad[3]), (quad[2], quad[3]), (quad[2], quad[1])]: dst_w = to_dst_w( (dst_px[0] + px_offset, dst_px[1] + px_offset)) src_w = self.dst_srs.transform_to(self.src_srs, dst_w) src_px = to_src_px(src_w) src_quad.extend(src_px) return quad, src_quad mesh_div = self.mesh_div while mesh_div > 1 and (dst_size[0] / mesh_div < 10 or dst_size[1] / mesh_div < 10): mesh_div -= 1 for quad in griddify(dst_quad, mesh_div): meshes.append(dst_quad_to_src(quad)) img = img_for_resampling(src_img.as_image(), image_opts.resampling) result = img.transform(dst_size, Image.MESH, meshes, image_filter[image_opts.resampling]) return ImageSource(result, size=dst_size, image_opts=image_opts)
def image_mask_from_geom(img, bbox, polygons): transf = make_lin_transf(bbox, (0, 0) + img.size) mask = Image.new("L", img.size, 255) draw = ImageDraw.Draw(mask) for p in polygons: draw.polygon([transf(coord) for coord in p.exterior.coords], fill=0) for ring in p.interiors: draw.polygon([transf(coord) for coord in ring.coords], fill=255) return mask
def image_mask_from_geom(img, bbox, polygons): transf = make_lin_transf(bbox, (0, 0) + img.size) mask = Image.new('L', img.size, 255) draw = ImageDraw.Draw(mask) for p in polygons: draw.polygon([transf(coord) for coord in p.exterior.coords], fill=0) for ring in p.interiors: draw.polygon([transf(coord) for coord in ring.coords], fill=255) return mask
def _transform(self, src_img, src_bbox, dst_size, dst_bbox, image_opts): """ Do a 'real' transformation with a transformed mesh (see above). """ src_bbox = self.src_srs.align_bbox(src_bbox) dst_bbox = self.dst_srs.align_bbox(dst_bbox) src_size = src_img.size src_quad = (0, 0, src_size[0], src_size[1]) dst_quad = (0, 0, dst_size[0], dst_size[1]) to_src_px = make_lin_transf(src_bbox, src_quad) to_dst_w = make_lin_transf(dst_quad, dst_bbox) meshes = [] # more recent versions of Pillow use center coordinates for # transformations, we manually need to add half a pixel otherwise if transform_uses_center(): px_offset = 0.0 else: px_offset = 0.5 def dst_quad_to_src(quad): src_quad = [] for dst_px in [(quad[0], quad[1]), (quad[0], quad[3]), (quad[2], quad[3]), (quad[2], quad[1])]: dst_w = to_dst_w((dst_px[0]+px_offset, dst_px[1]+px_offset)) src_w = self.dst_srs.transform_to(self.src_srs, dst_w) src_px = to_src_px(src_w) src_quad.extend(src_px) return quad, src_quad mesh_div = self.mesh_div while mesh_div > 1 and (dst_size[0] / mesh_div < 10 or dst_size[1] / mesh_div < 10): mesh_div -= 1 for quad in griddify(dst_quad, mesh_div): meshes.append(dst_quad_to_src(quad)) img = img_for_resampling(src_img.as_image(), image_opts.resampling) result = img.transform(dst_size, Image.MESH, meshes, image_filter[image_opts.resampling]) return ImageSource(result, size=dst_size, image_opts=image_opts)
def _get_transformed_query(self, query): """ Handle FI requests for unsupported SRS. """ req_srs = query.srs req_bbox = query.bbox info_srs = self._best_supported_srs(req_srs) info_bbox = req_srs.transform_bbox_to(info_srs, req_bbox) req_coord = make_lin_transf((0, query.size[1], query.size[0], 0), req_bbox)(query.pos) info_coord = req_srs.transform_to(info_srs, req_coord) info_pos = make_lin_transf((info_bbox), (0, query.size[1], query.size[0], 0))(info_coord) info_pos = int(round(info_pos[0])), int(round(info_pos[1])) info_query = InfoQuery( bbox=info_bbox, size=query.size, srs=info_srs, pos=info_pos, info_format=query.info_format, feature_count=query.feature_count, ) return info_query
def bbox_position_in_image(bbox, size, src_bbox): """ Calculate the position of ``bbox`` in an image of ``size`` and ``src_bbox``. Returns the sub-image size and the offset in pixel from top-left corner and the sub-bbox. >>> bbox_position_in_image((-180, -90, 180, 90), (600, 300), (-180, -90, 180, 90)) ((600, 300), (0, 0), (-180, -90, 180, 90)) >>> bbox_position_in_image((-200, -100, 200, 100), (600, 300), (-180, -90, 180, 90)) ((540, 270), (30, 15), (-180, -90, 180, 90)) >>> bbox_position_in_image((-200, -50, 200, 100), (600, 300), (-180, -90, 180, 90)) ((540, 280), (30, 20), (-180, -50, 180, 90)) >>> bbox_position_in_image((586400,196400,752800,362800), (256, 256), (586400,196400,752800,350000)) ((256, 237), (0, 19), (586400, 196400, 752800, 350000)) """ coord_to_px = make_lin_transf(bbox, (0, 0) + size) offsets = [0, size[1], size[0], 0] sub_bbox = list(bbox) if src_bbox[0] > bbox[0]: sub_bbox[0] = src_bbox[0] x, y = coord_to_px((src_bbox[0], 0)) offsets[0] = int(x) if src_bbox[1] > bbox[1]: sub_bbox[1] = src_bbox[1] x, y = coord_to_px((0, src_bbox[1])) offsets[1] = int(y) if src_bbox[2] < bbox[2]: sub_bbox[2] = src_bbox[2] x, y = coord_to_px((src_bbox[2], 0)) offsets[2] = int(x) if src_bbox[3] < bbox[3]: sub_bbox[3] = src_bbox[3] x, y = coord_to_px((0, src_bbox[3])) offsets[3] = int(y) size = abs(offsets[2] - offsets[0]), abs(offsets[1] - offsets[3]) return size, (offsets[0], offsets[3]), tuple(sub_bbox)
def bbox_position_in_image(bbox, size, src_bbox): """ Calculate the position of ``bbox`` in an image of ``size`` and ``src_bbox``. Returns the sub-image size and the offset in pixel from top-left corner and the sub-bbox. >>> bbox_position_in_image((-180, -90, 180, 90), (600, 300), (-180, -90, 180, 90)) ((600, 300), (0, 0), (-180, -90, 180, 90)) >>> bbox_position_in_image((-200, -100, 200, 100), (600, 300), (-180, -90, 180, 90)) ((540, 270), (30, 15), (-180, -90, 180, 90)) >>> bbox_position_in_image((-200, -50, 200, 100), (600, 300), (-180, -90, 180, 90)) ((540, 280), (30, 20), (-180, -50, 180, 90)) """ coord_to_px = make_lin_transf(bbox, (0, 0) + size) offsets = [0.0, float(size[1]), float(size[0]), 0.0] sub_bbox = list(bbox) if src_bbox[0] > bbox[0]: sub_bbox[0] = src_bbox[0] x, y = coord_to_px((src_bbox[0], 0)) offsets[0] = x if src_bbox[1] > bbox[1]: sub_bbox[1] = src_bbox[1] x, y = coord_to_px((0, src_bbox[1])) offsets[1] = y if src_bbox[2] < bbox[2]: sub_bbox[2] = src_bbox[2] x, y = coord_to_px((src_bbox[2], 0)) offsets[2] = x if src_bbox[3] < bbox[3]: sub_bbox[3] = src_bbox[3] x, y = coord_to_px((0, src_bbox[3])) offsets[3] = y size = int(offsets[2] - offsets[0]), int(offsets[1] - offsets[3]) return size, (int(offsets[0]), int(offsets[3])), tuple(sub_bbox)
def _get_pos(self): size = self.size vals = self['geometry'].split(',') x, y = float(vals[0]), float(vals[1]) return make_lin_transf(self.bbox, (0, 0, size[0], size[1]))((x, y))
def pos_coords(self): """x, y query coordinates (in request SRS)""" width, height = self.size bbox = self.bbox return make_lin_transf((0, 0, width, height), bbox)(self.pos)
def _set_pos(self, value): size = self.size req_coord = make_lin_transf((0, 0, size[0], size[1]), self.bbox)(value) self['geometry'] = '%f,%f' % req_coord
def _get_pos(self): size = self.size vals = self["geometry"].split(",") x, y = float(vals[0]), float(vals[1]) return make_lin_transf(self.bbox, (0, 0, size[0], size[1]))((x, y))
def _set_pos(self, value): size = self.size req_coord = make_lin_transf((0, 0, size[0], size[1]), self.bbox)(value) self["geometry"] = "%f,%f" % req_coord
def coord(self): return make_lin_transf((0, self.size[1], self.size[0], 0), self.bbox)(self.pos)
def coord(self): return make_lin_transf((0, 0, self.size[0], self.size[1]), self.bbox)(self.pos)
def transform_meshes(src_size, src_bbox, src_srs, dst_size, dst_bbox, dst_srs, max_px_err=1): """ transform_meshes creates a list of QUAD transformation parameters for PIL's MESH image transformation. Each QUAD is a rectangle in the destination image, like ``(0, 0, 100, 100)`` and a list of four pixel coordinates in the source image that match the destination rectangle. The four points form a quadliteral (i.e. not a rectangle). PIL's image transform uses affine transformation to fill each rectangle in the destination image with data from the source quadliteral. The number of QUADs is calculated dynamically to keep the deviation in the image transformation below one pixel. Image transformations for large map scales can be transformed with 1-4 QUADs most of the time. For low scales, transform_meshes can generate a few hundred QUADs. It generates a maximum of one QUAD per 50 pixel. """ src_bbox = src_srs.align_bbox(src_bbox) dst_bbox = dst_srs.align_bbox(dst_bbox) src_rect = (0, 0, src_size[0], src_size[1]) dst_rect = (0, 0, dst_size[0], dst_size[1]) to_src_px = make_lin_transf(src_bbox, src_rect) to_src_w = make_lin_transf(src_rect, src_bbox) to_dst_w = make_lin_transf(dst_rect, dst_bbox) meshes = [] # more recent versions of Pillow use center coordinates for # transformations, we manually need to add half a pixel otherwise if transform_uses_center(): px_offset = 0.0 else: px_offset = 0.5 def dst_quad_to_src(quad): src_quad = [] for dst_px in [(quad[0], quad[1]), (quad[0], quad[3]), (quad[2], quad[3]), (quad[2], quad[1])]: dst_w = to_dst_w( (dst_px[0] + px_offset, dst_px[1] + px_offset)) src_w = dst_srs.transform_to(src_srs, dst_w) src_px = to_src_px(src_w) src_quad.extend(src_px) return quad, src_quad res = (dst_bbox[2] - dst_bbox[0]) / dst_size[0] max_err = max_px_err * res def is_good(quad, src_quad): w = quad[2] - quad[0] h = quad[3] - quad[1] if w < 50 or h < 50: return True xc = quad[0] + w / 2.0 - 0.5 yc = quad[1] + h / 2.0 - 0.5 # coordinate for the center of the quad dst_w = to_dst_w((xc, yc)) # actual coordinate for the center of the quad src_px = center_quad_transform(quad, src_quad) real_dst_w = src_srs.transform_to(dst_srs, to_src_w(src_px)) err = max(abs(dst_w[0] - real_dst_w[0]), abs(dst_w[1] - real_dst_w[1])) return err < max_err # recursively add meshes. divide each quad into four sub quad till # accuracy is good enough. def add_meshes(quads): for quad in quads: quad, src_quad = dst_quad_to_src(quad) if is_good(quad, src_quad): meshes.append((quad, src_quad)) else: add_meshes(divide_quad(quad)) add_meshes([(0, 0, dst_size[0], dst_size[1])]) return meshes