def raster2pyramid( input_file, output_dir, options ): """ Creates a tile pyramid out of an input raster dataset. """ pyramid_type = options["pyramid_type"] scale_method = options["scale_method"] output_format = options["output_format"] resampling = options["resampling"] zoom = options["zoom"] bounds = options["bounds"] overwrite = options["overwrite"] # Prepare process parameters minzoom, maxzoom = _get_zoom(zoom, input_file, pyramid_type) process_file = os.path.join( os.path.dirname(os.path.realpath(__file__)), "tilify.py" ) with rasterio.open(input_file, "r") as input_raster: output_bands = input_raster.count input_dtype = input_raster.dtypes[0] output_dtype = input_raster.dtypes[0] nodataval = input_raster.nodatavals[0] if not nodataval: nodataval = 0 if output_format == "PNG": if output_bands > 3: output_bands = 3 output_dtype = 'uint8' scales_minmax = () if scale_method == "dtype_scale": for index in range(1, output_bands+1): scales_minmax += (DTYPE_RANGES[input_dtype], ) elif scale_method == "minmax_scale": for index in range(1, output_bands+1): band = input_raster.read(index) scales_minmax += ((band.min(), band.max()), ) elif scale_method == "crop": for index in range(1, output_bands+1): scales_minmax += ((0, 255), ) if input_dtype == "uint8": scale_method = None scales_minmax = () for index in range(1, output_bands+1): scales_minmax += ((None, None), ) # Create configuration config = {} config.update( process_file=process_file, output={ "path": output_dir, "format": output_format, "type": pyramid_type, "bands": output_bands, "dtype": output_dtype }, scale_method=scale_method, scales_minmax=scales_minmax, input_files={"raster": input_file}, config_dir=os.getcwd(), process_minzoom=minzoom, process_maxzoom=maxzoom, nodataval=nodataval, resampling=resampling, bounds=bounds, pixelbuffer=5, baselevel={"zoom": maxzoom, "resampling": resampling} ) LOGGER.info("preparing process ...") try: mapchete = Mapchete( MapcheteConfig( config, zoom=zoom, bounds=bounds ) ) except PyCompileError as error: print error return except: raise # Prepare output directory and logging if not os.path.exists(output_dir): os.makedirs(output_dir) logging.config.dictConfig(get_log_config(mapchete)) for zoom in reversed(range(minzoom, maxzoom+1)): # Determine work tiles and run work_tiles = mapchete.get_work_tiles(zoom) func = partial(_worker, mapchete=mapchete, overwrite=overwrite ) pool = Pool() try: pool.map_async(func, work_tiles) pool.close() except KeyboardInterrupt: LOGGER.info( "Caught KeyboardInterrupt, terminating workers" ) pool.terminate() break except: raise finally: pool.close() pool.join()
def main(args=None): """ Creates the Mapchete host and serves both web page with OpenLayers and the WMTS simple REST endpoint. """ if args is None: args = sys.argv[1:] parser = argparse.ArgumentParser() parser.add_argument("mapchete_file", type=str) parser.add_argument("--port", "-p", type=int, default=5000) parser.add_argument("--zoom", "-z", type=int, nargs='*', ) parser.add_argument("--bounds", "-b", type=float, nargs='*') parser.add_argument("--log", action="store_true") parser.add_argument("--overwrite", action="store_true") parser.add_argument("--input_file", type=str) parsed = parser.parse_args(args) elif isinstance(args, argparse.Namespace): parsed = args else: raise RuntimeError("invalid arguments for mapchete serve") try: assert os.path.splitext(parsed.mapchete_file)[1] == ".mapchete" except: raise IOError("must be a valid mapchete file") try: LOGGER.info("preparing process ...") mapchete = Mapchete( MapcheteConfig( parsed.mapchete_file, zoom=parsed.zoom, bounds=parsed.bounds, single_input_file=parsed.input_file ) ) except: raise app = Flask(__name__) logging.config.dictConfig(get_log_config(mapchete)) metatile_cache = {} metatile_lock = threading.Lock() @app.route('/', methods=['GET']) def return_index(): """ Renders and hosts the appropriate OpenLayers instance. """ index_html = pkgutil.get_data('mapchete.static', 'index.html') process_bounds = mapchete.config.process_bounds() if not process_bounds: process_bounds = ( mapchete.tile_pyramid.left, mapchete.tile_pyramid.bottom, mapchete.tile_pyramid.right, mapchete.tile_pyramid.top ) return render_template_string( index_html, srid=mapchete.tile_pyramid.srid, process_bounds=",".join(map(str, process_bounds)), is_mercator=(mapchete.tile_pyramid.srid == 3857) ) tile_base_url = '/wmts_simple/1.0.0/mapchete/default/' if mapchete.tile_pyramid.srid == 3857: tile_base_url += "g/" else: tile_base_url += "WGS84/" @app.route( tile_base_url+'<int:zoom>/<int:row>/<int:col>.png', methods=['GET'] ) def get(zoom, row, col): """ Returns processed, empty or error (in pink color) tile. """ tile = mapchete.tile_pyramid.tilepyramid.tile(zoom, row, col) try: metatile = mapchete.tile( mapchete.tile_pyramid.tile( tile.zoom, int(tile.row/mapchete.config.metatiling), int(tile.col/mapchete.config.metatiling), ) ) with metatile_lock: metatile_event = metatile_cache.get(metatile.id) if not metatile_event: metatile_cache[metatile.id] = threading.Event() if metatile_event: LOGGER.info("%s waiting for metatile %s", tile.id, metatile.id ) metatile_event.wait() try: image = mapchete.get(tile) except: raise else: LOGGER.info("%s getting metatile %s", tile.id, metatile.id ) try: image = mapchete.get(tile, overwrite=parsed.overwrite) except: raise finally: with metatile_lock: metatile_event = metatile_cache.get(metatile.id) del metatile_cache[metatile.id] metatile_event.set() if image: resp = make_response(image) # set no-cache header: resp.cache_control.no_cache = True LOGGER.info((tile.id, "ok", "image sent")) return resp else: raise IOError("no image returned") except Exception as exception: error_msg = (tile.id, "failed", exception) LOGGER.error(error_msg) size = mapchete.tile_pyramid.tilepyramid.tile_size empty_image = Image.new('RGBA', (size, size)) pixels = empty_image.load() for y_idx in xrange(size): for x_idx in xrange(size): pixels[x_idx, y_idx] = (255, 0, 0, 128) out_img = io.BytesIO() empty_image.save(out_img, 'PNG') out_img.seek(0) resp = make_response(send_file(out_img, mimetype='image/png')) resp.cache_control.no_cache = True return resp app.run( threaded=True, debug=True, port=parsed.port, extra_files=[parsed.mapchete_file] )