Ejemplo n.º 1
0
def tileid_from_bbox(self, bbox: geo.LatLonBBox, tile_scale: int = 1) -> List[TileID]:
    """
    Gets the tile_ids given a bounding box.
    Args:
        bbox (tuple): (left, upper, right, bottom) mercantile compatible bounding box.
        tile_scale (int, optional): Essentially how much zoom to add. +ve numbers zoom in, -ve zoom out. 0 treated as 1: no zoom change. Defaults to 1.
    Returns:
        List[TileID]: TileIDs covering the box.
    """
    tile_ids = []
    mt = mercantile.bounding_tile(*bbox)
    tm = TileID(z=mt.z, x=mt.x, y=mt.y)
    if tile_scale < 0:
        start_z = tm.z
        end_z = tm.z + tile_scale
        for _ in range(start_z, end_z, -1):
            tm = tm.parent()
            if tm.z == 0:
                break
        tile_ids = [tm]
    elif tile_scale in (0, 1):
        tile_ids = [tm]
    else:
        tile_ids = [tm]
        start_z = tm.z
        end_z = tm.z + tile_scale
        for _ in range(start_z, min(end_z, self.max_zoom)):
            new_ids = []
            for t in tile_ids:
                new_ids += t.children()
            tile_ids = new_ids
    return [TileID(z=m.z, x=m.x, y=m.y) for m in tile_ids]
Ejemplo n.º 2
0
    def load_files(self, *files: List[Path], verbose: bool = False):
        files = [Path(file) for file in files]
        if verbose:
            from tqdm import tqdm
            files = tqdm(files, desc="RoadEngine Load")
        for file in files:
            if not file.exists():
                raise FileNotFoundError(f"Could not load {file.absolute()}!")
            gdf = gpd.read_file(file.absolute())
            self.cache = self.cache.append(gdf, ignore_index=True, sort=False)

        tileset = set()
        geometry = gdf['geometry']
        if verbose:
            geometry = tqdm(geometry, desc="RoadEngine Quadtree")
        for geom in geometry:
            if type(geom) != LineString:
                continue
            root = bounding_tile(*geom.bounds)
            tileset |= {root}
        self.cached_tiles |= set(simplify(*tileset))
        if verbose:
            logger.info("RoadEngine is building an R-Tree...")
        self.cache.sindex
        if verbose:
            logger.info("RoadEngine R-Tree done!")
    def get_sources(self, bounds, resolution):
        bounds, bounds_crs = bounds
        zoom = get_zoom(max(resolution))

        self._log.info("Resolution: %s; equivalent zoom: %d", resolution, zoom)

        left, bottom, right, top = warp.transform_bounds(
            bounds_crs, WGS84_CRS, *bounds)

        # account for rounding errors when converting between tiles and coords
        left += 0.000001
        bottom += 0.000001
        right -= 0.000001
        top -= 0.000001

        if (self._bounds[0] <= left <= self._bounds[2]
                or self._bounds[0] <= right <= self._bounds[2]) and (
                    self._bounds[1] <= bottom <= self._bounds[3]
                    or self._bounds[1] <= top <= self._bounds[3]) and (
                        self._minzoom <= zoom <= self._maxzoom):
            tile = mercantile.bounding_tile(left, bottom, right, top)

            with requests.get(
                    self.endpoint.format(x=tile.x, y=tile.y, z=tile.z)) as rsp:
                if not rsp:
                    self._log.warn("%s failed: %s", rsp.url, rsp.text)
                    return

                for source in rsp.json():
                    yield Source(**source)
Ejemplo n.º 4
0
def zoom_level_from_geometry(geometry, splits=4):
    """Generate optimum zoom level for geometry.

    Notes
    -----
    The obvious solution would be

    >>> mercantile.bounding_tile(*geometry.get_shape(WGS84_CRS).bounds).z

    However, if the geometry is split between two or four tiles,
    the resulting zoom level might be too big.

    """
    # This import is here to avoid cyclic references
    from telluric.vectors import generate_tile_coordinates

    # We split the geometry and compute the zoom level for each chunk
    levels = []
    for chunk in generate_tile_coordinates(geometry, (splits, splits)):
        levels.append(
            mercantile.bounding_tile(*chunk.get_shape(WGS84_CRS).bounds).z)

    # We now return the median value using the median_low function, which
    # always picks the result from the list
    return median_low(levels)
Ejemplo n.º 5
0
def tileserver_optimized_raster(src, dest):
    """ This method converts a raster to a tileserver optimized raster.
        The method will reproject the raster to align to the xyz system, in resolution and projection
        It will also create overviews
        And finally it will arragne the raster in a cog way.
        You could take the dest file upload it to a web server that supports ranges and user GeoRaster.get_tile
        on it,
        You are geranteed that you will get as minimal data as possible
    """
    src_raster = tl.GeoRaster2.open(src)
    bounding_box = src_raster.footprint().get_shape(
        tl.constants.WGS84_CRS).bounds
    tile = mercantile.bounding_tile(*bounding_box)
    dest_resolution = mercator_upper_zoom_level(src_raster)
    bounds = tl.GeoVector.from_xyz(tile.x, tile.y, tile.z).get_bounds(
        tl.constants.WEB_MERCATOR_CRS)
    create_options = {
        "tiled": "YES",
        "blocksize": 256,
        "compress": "DEFLATE",
        "photometric": "MINISBLACK"
    }
    with TemporaryDirectory() as temp_dir:
        temp_file = os.path.join(temp_dir, 'temp.tif')

        warp(src,
             temp_file,
             dst_crs=tl.constants.WEB_MERCATOR_CRS,
             resolution=dest_resolution,
             dst_bounds=bounds,
             create_options=create_options)

        with rasterio.Env(GDAL_TIFF_INTERNAL_MASK=True,
                          GDAL_TIFF_OVR_BLOCKSIZE=256):
            resampling = rasterio.enums.Resampling.gauss
            with rasterio.open(temp_file, 'r+') as tmp_raster:
                factors = _calc_overviews_factors(tmp_raster)
                tmp_raster.build_overviews(factors, resampling=resampling)
                tmp_raster.update_tags(ns='rio_overview',
                                       resampling=resampling.name)
                telluric_tags = _get_telluric_tags(src)
                if telluric_tags:
                    tmp_raster.update_tags(**telluric_tags)

            rasterio_sh.copy(temp_file,
                             dest,
                             COPY_SRC_OVERVIEWS=True,
                             tiled=True,
                             compress='DEFLATE',
                             photometric='MINISBLACK')
Ejemplo n.º 6
0
 def get_bounding_tile(self, lon_lat_h, C_n_v):
     """
     Calculates the corner points of the image in WGS-84, and returns the
     WMS tile z/x/y that completely bounds the image
     :param lon_lat_h: [3,] Position of the camera: Lon, Lat (deg), \
         height (m)
     :param C_n_v: [3x3] np.ndarray that translates a vector in the vehicle
         frame into the local-level NED frame
     :return: mercantile.Tile that completely bounds the projected image
     """
     cwgs = self.project_corners(lon_lat_h, C_n_v)
     b_tile = mercantile.bounding_tile(
         cwgs.min(0)[0],
         cwgs.min(0)[1],
         cwgs.max(0)[0],
         cwgs.max(0)[1])
     return b_tile
Ejemplo n.º 7
0
def build_tile_dict(geom):
    # use bounds to find the smallest tile that completely contains our input aoi
    # not useful for AOIs that cross lat or lon 0 (returns tile [0, 0, 0])
    # but helpful for many AOIs
    # https://github.com/mapbox/mercantile/blob/master/docs/cli.rst#bounding-tile
    bbox = geom.bounds
    bounding_tile = mercantile.bounding_tile(*bbox)

    # divide tiles into within and intersecting lists
    within_list, intersect_list = process_tile([bounding_tile], geom)

    # initialize tile: proportion covered dict, starting with within
    # tiles, all of which have a coverage proportion of 1
    tile_dict = dict([(x, 1) for x in within_list])

    for t in intersect_list:
        # do intersection of intersecting tile and original AOI geom
        intersect_area = get_intersect_area(geom, t)

        # divide intersect area by area of all z12 tiles in webmerc
        tile_dict[t] = intersect_area / 9572.547449763457

    return tile_dict
Ejemplo n.º 8
0
def missing_quadkeys(mosaic: Dict,
                     shp_path: str,
                     bounds: List[float] = None,
                     simplify: bool = True) -> Dict:
    """Find quadkeys over land missing from mosaic

    Args:
        - mosaic: mosaic definition
        - shp_path: path to Natural Earth shapefile of land boundaries
        - bounds: force given bounds
        - simplify: reduce size of the tileset as much as possible by merging leaves into parents

    Returns:
        - GeoJSON FeatureCollection of missing tiles
    """
    bounds = bounds or mosaic['bounds']
    top_tile = mercantile.bounding_tile(*bounds)
    gdf = gpd.read_file(shp_path)
    quadkey_zoom = mosaic.get('quadkey_zoom', mosaic['minzoom'])

    # Remove null island
    # Keep the landmasses that are visible at given zoom
    gdf = gdf[gdf['max_zoom'] <= quadkey_zoom]

    land_tiles = find_child_land_tiles(top_tile, gdf, quadkey_zoom)
    quadkeys = {mercantile.quadkey(tile) for tile in land_tiles}

    mosaic_quadkeys = set(mosaic['tiles'].keys())
    not_in_mosaic = quadkeys.difference(mosaic_quadkeys)
    not_in_mosaic = [mercantile.quadkey_to_tile(qk) for qk in not_in_mosaic]

    if simplify:
        not_in_mosaic = mercantile.simplify(not_in_mosaic)

    features = [mercantile.feature(tile) for tile in not_in_mosaic]

    return {'type': 'FeatureCollection', 'features': features}
Ejemplo n.º 9
0
def test_bounding_tile_pt():
    """A point is a valid input"""
    assert mercantile.bounding_tile(-91.5, 1.0).z == 28
Ejemplo n.º 10
0
def test_bounding_tile():
    assert mercantile.bounding_tile(-92.5, 0.5, -90.5, 1.5) == (31, 63, 7)
    assert mercantile.bounding_tile(-90.5, 0.5, -89.5, 0.5) == (0, 0, 1)
    assert mercantile.bounding_tile(-92, 0, -88, 2) == (0, 0, 0)
Ejemplo n.º 11
0
def test_overflow_bounding_tile():
    assert mercantile.bounding_tile(
        -179.99999999999997,
        -90.00000000000003,
        180.00000000000014,
        -63.27066048950458) == (0, 0, 0)
Ejemplo n.º 12
0
def tiles(ctx, zoom, input, bounding_tile, with_bounds, seq, x_json_seq):
    """Lists Web Mercator tiles at ZOOM level intersecting
    GeoJSON [west, south, east, north] bounding boxen, features, or
    collections read from stdin. Output is a JSON
    [x, y, z [, west, south, east, north -- optional]] array.

    Example:

    $ echo "[-105.05, 39.95, -105, 40]" | mercantile tiles 12

    Output:

    [852, 1550, 12]
    [852, 1551, 12]
    [853, 1550, 12]
    [853, 1551, 12]

    """
    verbosity = ctx.obj['verbosity']
    logger = logging.getLogger('mercantile')
    try:
        src = click.open_file(input).readlines()
    except IOError:
        src = [input]

    src = iter(src)
    first_line = next(src)

    # If input is RS-delimited JSON sequence.
    if first_line.startswith(u'\x1e'):
        def feature_gen():
            buffer = first_line.strip(u'\x1e')
            for line in src:
                if line.startswith(u'\x1e'):
                    if buffer:
                        yield json.loads(buffer)
                    buffer = line.strip(u'\x1e')
                else:
                    buffer += line
            else:
                yield json.loads(buffer)
    else:
        def feature_gen():
            yield json.loads(first_line)
            for line in src:
                yield json.loads(line)

    try:
        source = feature_gen()
        # Detect the input format
        for obj in source:
            if isinstance(obj, list):
                bbox = obj
                if len(bbox) == 2:
                    bbox += bbox
                if len(bbox) != 4:
                    raise ValueError("Invalid input.")
            elif isinstance(obj, dict):
                if 'bbox' in obj:
                    bbox = obj['bbox']
                else:
                    box_xs = []
                    box_ys = []
                    for feat in obj.get('features', [obj]):
                        lngs, lats = zip(*list(coords(feat)))
                        box_xs.extend([min(lngs), max(lngs)])
                        box_ys.extend([min(lats), max(lats)])
                    bbox = min(box_xs), min(box_ys), max(box_xs), max(box_ys)
            west, south, east, north = bbox
            if bounding_tile:
                vals = mercantile.bounding_tile(
                    west, south, east, north, truncate=False)
                output = json.dumps(vals)
                if seq:
                    click.echo(u'\x1e')
                click.echo(output)
            else:
                # shrink the bounds a small amount so that
                # shapes/tiles round trip.
                epsilon = 1.0e-10
                west += epsilon
                south += epsilon
                east -= epsilon
                north -= epsilon
                for tile in mercantile.tiles(
                        west, south, east, north, [zoom], truncate=False):
                    vals = (tile.x, tile.y, zoom)
                    if with_bounds:
                        vals += mercantile.bounds(tile.x, tile.y, zoom)
                    output = json.dumps(vals)
                    if seq:
                        click.echo(u'\x1e')
                    click.echo(output)

        sys.exit(0)
    except Exception:
        logger.exception("Failed. Exception caught")
        sys.exit(1)
Ejemplo n.º 13
0
def bounding_tile(ctx, input, seq):
    """Print the Web Mercator tile at ZOOM level bounding
    GeoJSON [west, south, east, north] bounding boxes, features, or
    collections read from stdin.

    Input may be a compact newline-delimited sequences of JSON or
    a pretty-printed ASCII RS-delimited sequence of JSON (like
    https://tools.ietf.org/html/rfc8142 and
    https://tools.ietf.org/html/rfc7159).

    Example:

    $ echo "[-105.05, 39.95, -105, 40]" | mercantile bounding-tile

    Output:

    [426, 775, 11]
    """
    src = iter(normalize_input(input))
    first_line = next(src)

    # If input is RS-delimited JSON sequence.
    if first_line.startswith(u'\x1e'):
        def feature_gen():
            buffer = first_line.strip(u'\x1e')
            for line in src:
                if line.startswith(u'\x1e'):
                    if buffer:
                        yield json.loads(buffer)
                    buffer = line.strip(u'\x1e')
                else:
                    buffer += line
            else:
                yield json.loads(buffer)
    else:
        def feature_gen():
            yield json.loads(first_line)
            for line in src:
                yield json.loads(line)

    source = feature_gen()
    # Detect the input format
    for obj in source:
        if isinstance(obj, list):
            bbox = obj
            if len(bbox) == 2:
                bbox += bbox
            if len(bbox) != 4:
                raise click.BadParameter(
                    "{0}".format(bbox), param=input, param_hint='input')
        elif isinstance(obj, dict):
            if 'bbox' in obj:
                bbox = obj['bbox']
            else:
                box_xs = []
                box_ys = []
                for feat in obj.get('features', [obj]):
                    lngs, lats = zip(*list(coords(feat)))
                    box_xs.extend([min(lngs), max(lngs)])
                    box_ys.extend([min(lats), max(lats)])
                bbox = min(box_xs), min(box_ys), max(box_xs), max(box_ys)
        west, south, east, north = bbox
        vals = mercantile.bounding_tile(
            west, south, east, north, truncate=False)
        output = json.dumps(vals)
        if seq:
            click.echo(u'\x1e')
        click.echo(output)
Ejemplo n.º 14
0
def bounding_tile(ctx, input, seq):
    """Print the Web Mercator tile at ZOOM level bounding
    GeoJSON [west, south, east, north] bounding boxes, features, or
    collections read from stdin.

    Input may be a compact newline-delimited sequences of JSON or
    a pretty-printed ASCII RS-delimited sequence of JSON (like
    https://tools.ietf.org/html/rfc8142 and
    https://tools.ietf.org/html/rfc7159).

    Example:

    \b
    echo "[-105.05, 39.95, -105, 40]" | mercantile bounding-tile
    [426, 775, 11]

    """
    src = iter(normalize_input(input))
    first_line = next(src)

    # If input is RS-delimited JSON sequence.
    if first_line.startswith(RS):

        def feature_gen():
            buffer = first_line.strip(RS)
            for line in src:
                if line.startswith(RS):
                    if buffer:
                        yield json.loads(buffer)
                    buffer = line.strip(RS)
                else:
                    buffer += line
            else:
                yield json.loads(buffer)

    else:

        def feature_gen():
            yield json.loads(first_line)
            for line in src:
                yield json.loads(line)

    for obj in feature_gen():

        if isinstance(obj, list):
            bbox = obj
            if len(bbox) == 2:
                bbox += bbox
            elif len(bbox) != 4:
                raise click.BadParameter("{0}".format(bbox),
                                         param=input,
                                         param_hint="input")

        elif isinstance(obj, dict):
            if "bbox" in obj:
                bbox = obj["bbox"]
            else:
                bbox = mercantile.geojson_bounds(obj)

        west, south, east, north = bbox
        vals = mercantile.bounding_tile(west,
                                        south,
                                        east,
                                        north,
                                        truncate=False)
        output = json.dumps(vals)

        if seq:
            click.echo(RS)

        click.echo(output)
Ejemplo n.º 15
0
def test_bounding_tile(bounds, tile):
    assert mercantile.bounding_tile(*bounds) == mercantile.Tile(*tile)
Ejemplo n.º 16
0
def thumbnail_url(rec):
    bounds = shape(rec['geometry']).bounds
    tile = mercantile.bounding_tile(*bounds)
    return tms_template_url(rec).format(z=tile.z, x=tile.x, y=tile.y)
Ejemplo n.º 17
0
def tiles(ctx, zoom, input, bounding_tile, with_bounds, seq, x_json_seq):
    """Lists Web Mercator tiles at ZOOM level intersecting
    GeoJSON [west, south, east, north] bounding boxen, features, or
    collections read from stdin. Output is a JSON
    [x, y, z [, west, south, east, north -- optional]] array.

    Example:

    $ echo "[-105.05, 39.95, -105, 40]" | mercantile tiles 12

    Output:

    [852, 1550, 12]
    [852, 1551, 12]
    [853, 1550, 12]
    [853, 1551, 12]

    """
    src = iter(normalize_input(input))
    first_line = next(src)

    # If input is RS-delimited JSON sequence.
    if first_line.startswith(u'\x1e'):

        def feature_gen():
            buffer = first_line.strip(u'\x1e')
            for line in src:
                if line.startswith(u'\x1e'):
                    if buffer:
                        yield json.loads(buffer)
                    buffer = line.strip(u'\x1e')
                else:
                    buffer += line
            else:
                yield json.loads(buffer)
    else:

        def feature_gen():
            yield json.loads(first_line)
            for line in src:
                yield json.loads(line)

    source = feature_gen()
    # Detect the input format
    for obj in source:
        if isinstance(obj, list):
            bbox = obj
            if len(bbox) == 2:
                bbox += bbox
            if len(bbox) != 4:
                raise click.BadParameter("{0}".format(bbox),
                                         param=input,
                                         param_hint='input')
        elif isinstance(obj, dict):
            if 'bbox' in obj:
                bbox = obj['bbox']
            else:
                box_xs = []
                box_ys = []
                for feat in obj.get('features', [obj]):
                    lngs, lats = zip(*list(coords(feat)))
                    box_xs.extend([min(lngs), max(lngs)])
                    box_ys.extend([min(lats), max(lats)])
                bbox = min(box_xs), min(box_ys), max(box_xs), max(box_ys)
        west, south, east, north = bbox
        if bounding_tile:
            vals = mercantile.bounding_tile(west,
                                            south,
                                            east,
                                            north,
                                            truncate=False)
            output = json.dumps(vals)
            if seq:
                click.echo(u'\x1e')
            click.echo(output)
        else:
            epsilon = 1.0e-10

            if east != west and north != south:
                # 2D bbox
                # shrink the bounds a small amount so that
                # shapes/tiles round trip.
                west += epsilon
                south += epsilon
                east -= epsilon
                north -= epsilon

            for tile in mercantile.tiles(west,
                                         south,
                                         east,
                                         north, [zoom],
                                         truncate=False):
                vals = (tile.x, tile.y, zoom)
                if with_bounds:
                    vals += mercantile.bounds(tile.x, tile.y, zoom)
                output = json.dumps(vals)
                if seq:
                    click.echo(u'\x1e')
                click.echo(output)
Ejemplo n.º 18
0
def test_bounding_tile_roundtrip(t):
    """bounding_tile(bounds(tile)) gives the tile"""
    val = mercantile.bounding_tile(*mercantile.bounds(t))
    assert val.x == t.x
    assert val.y == t.y
    assert val.z == t.z
Ejemplo n.º 19
0
def tiles(ctx, zoom, input, bounding_tile, with_bounds, seq, x_json_seq):
    """Lists Web Mercator tiles at ZOOM level intersecting
    GeoJSON [west, south, east, north] bounding boxen, features, or
    collections read from stdin. Output is a JSON
    [x, y, z [, west, south, east, north -- optional]] array.

    Example:

    $ echo "[-105.05, 39.95, -105, 40]" | mercantile tiles 12

    Output:

    [852, 1550, 12]
    [852, 1551, 12]
    [853, 1550, 12]
    [853, 1551, 12]

    """
    src = iter(normalize_input(input))
    first_line = next(src)

    # If input is RS-delimited JSON sequence.
    if first_line.startswith(u'\x1e'):
        def feature_gen():
            buffer = first_line.strip(u'\x1e')
            for line in src:
                if line.startswith(u'\x1e'):
                    if buffer:
                        yield json.loads(buffer)
                    buffer = line.strip(u'\x1e')
                else:
                    buffer += line
            else:
                yield json.loads(buffer)
    else:
        def feature_gen():
            yield json.loads(first_line)
            for line in src:
                yield json.loads(line)

    source = feature_gen()
    # Detect the input format
    for obj in source:
        if isinstance(obj, list):
            bbox = obj
            if len(bbox) == 2:
                bbox += bbox
            if len(bbox) != 4:
                raise click.BadParameter(
                    "{0}".format(bbox), param=input, param_hint='input')
        elif isinstance(obj, dict):
            if 'bbox' in obj:
                bbox = obj['bbox']
            else:
                box_xs = []
                box_ys = []
                for feat in obj.get('features', [obj]):
                    lngs, lats = zip(*list(coords(feat)))
                    box_xs.extend([min(lngs), max(lngs)])
                    box_ys.extend([min(lats), max(lats)])
                bbox = min(box_xs), min(box_ys), max(box_xs), max(box_ys)
        west, south, east, north = bbox
        if bounding_tile:
            vals = mercantile.bounding_tile(
                west, south, east, north, truncate=False)
            output = json.dumps(vals)
            if seq:
                click.echo(u'\x1e')
            click.echo(output)
        else:
            epsilon = 1.0e-10

            if east != west and north != south:
                # 2D bbox
                # shrink the bounds a small amount so that
                # shapes/tiles round trip.
                west += epsilon
                south += epsilon
                east -= epsilon
                north -= epsilon

            for tile in mercantile.tiles(
                    west, south, east, north, [zoom], truncate=False):
                vals = (tile.x, tile.y, zoom)
                if with_bounds:
                    vals += mercantile.bounds(tile.x, tile.y, zoom)
                output = json.dumps(vals)
                if seq:
                    click.echo(u'\x1e')
                click.echo(output)
Ejemplo n.º 20
0
def test_bounding_tile_truncate():
    """Input is truncated"""
    assert mercantile.bounding_tile(-181.0, 1.0,
                                    truncate=True) == mercantile.bounding_tile(
                                        -180.0, 1.0)
Ejemplo n.º 21
0
def test_bounding_tile_pt():
    """A point is a valid input"""
    assert mercantile.bounding_tile(-91.5, 1.0).z == 28
Ejemplo n.º 22
0
def test_overflow_bounding_tile():
    assert mercantile.bounding_tile(-179.99999999999997, -90.00000000000003,
                                    180.00000000000014,
                                    -63.27066048950458) == (0, 0, 0)
Ejemplo n.º 23
0
def test_bounding_tile_truncate():
    """Input is truncated"""
    assert mercantile.bounding_tile(-181.0, 1.0, truncate=True) \
        == mercantile.bounding_tile(-180.0, 1.0)
Ejemplo n.º 24
0
 def test_geo_bounding_tile(self):
     gr = self.raster_for_test()
     gv = gr.footprint().reproject({'init': 'epsg:4326'})
     bounding_tile = mercantile.bounding_tile(*gv.get_shape(gv.crs).bounds)
     self.assertEqual(bounding_tile, (2319, 1578, 12))
Ejemplo n.º 25
0
def provide_legend(idx, field_name):
    # Extract out special extent parameter that is independent from hash
    extent = None
    params = request.args.get("params")
    if params and params != "{params}":
        params = json.loads(params)
        extent = params.get("extent")
    
    zoom = params.get("zoom")
    if (zoom is None) and extent:
        zoom = mercantile.bounding_tile(
            max(-180.0, extent["minLon"]),
            max(-90.0, extent["minLat"]),
            max(180.0, extent["maxLon"]),
            max(90.0, extent["maxLat"]),
        ).z - 1
    elif (zoom is None) and not extent:
        return legend_response("[]", "no zoom")

    zoom = int(zoom)

    # Get hash and parameters
    try:
        parameter_hash, params = extract_parameters(request)
    except Exception as e:
        current_app.logger.exception("Error while extracting parameters")
        return legend_response("[]", e)

    cache_dir = Path(current_app.config["CACHE_DIRECTORY"])
    params = merge_generated_parameters(params, idx, parameter_hash)

    # Assign param value to legacy keyword values
    geopoint_field = params["geopoint_field"]
    category_type = params["category_type"]
    category_histogram = params["category_histogram"]
    category_format = params["category_format"]
    cmap = params["cmap"]
    histogram_interval = params.get("generated_params", {}).get(
        "histogram_interval", None
    )
    field_min = params.get("generated_params", {}).get(
        "field_min", None
    )
    field_max = params.get("generated_params", {}).get(
        "field_max", None
    )

    # If not in category mode, just return nothing
    if params["category_field"] is None:
        return legend_response("[]", parameter_hash=parameter_hash, params=params)

    cmap = params["cmap"]
    category_field = params["category_field"]
    geopoint_field = params["geopoint_field"]

    base_s = get_search_base(current_app.config.get("ELASTIC"), params, idx)
    if extent:
        legend_bbox = {
            "top_left": {
                "lat": min(90.0, extent["maxLat"]),
                "lon": max(-180.0, extent["minLon"]),
            },
            "bottom_right": {
                "lat": max(-90.0, extent["minLat"]),
                "lon": min(180.0, extent["maxLon"]),
            },
        }
        current_app.logger.info("legend_bbox: %s", legend_bbox)
        base_s = base_s.filter("geo_bounding_box", **{geopoint_field: legend_bbox})
    legend_s = copy.copy(base_s)
    legend_s = legend_s.params(size=0)

    legend = {}
    if histogram_interval is not None and category_histogram in (True, None):
        # Put in the histogram search
        legend_s.aggs.bucket(
            "categories",
            "histogram",
            field=category_field,
            interval=histogram_interval,
            min_doc_count=1,
        )

        # Perform the execution
        response = legend_s.execute()
        # If no categories then return blank list
        if not hasattr(response.aggregations, "categories"):
            return legend_response("[]", parameter_hash=parameter_hash, params=params)

        # Generate the legend list
        for category in response.aggregations.categories:
            # Bin the data
            raw = float(category.key)
            # Format with pynumeral if provided
            if category_format:
                label = "%s-%s" % (
                    pynumeral.format(raw, category_format),
                    pynumeral.format(raw + histogram_interval, category_format),
                )
            else:
                label = "%s-%s" % (raw, raw + histogram_interval)
            legend[label] = category.doc_count
    elif category_field:
        tiles_iter = mercantile.tiles(
            max(-180.0, extent["minLon"]),
            max(-90.0, extent["minLat"]),
            min(180.0, extent["maxLon"]),
            min(90.0, extent["maxLat"]),
            zoom
        )

        for tile in tiles_iter:
            #Query the database to get the categories for this tile
            _, tile_legend = get_tile_categories(
                base_s,
                tile.x,
                tile.y,
                tile.z,
                geopoint_field,
                category_field,
                int(current_app.config["MAX_LEGEND_ITEMS_PER_TILE"]),
            )
            
            for k, v in tile_legend.items():
                if category_type == "number":
                    try:
                        k = pynumeral.format(to_32bit_float(k), category_format)
                    except ValueError:
                        k = str(k)                        
                else:
                    k = str(k)
                legend[k] = legend.get(k, 0) + v

    color_key_legend = []
    if not legend:
        return legend_response("[]", parameter_hash=parameter_hash, params=params)
    else:
        # Extract other to put it at the end
        other = legend.pop("Other", None)
        for k, count in sorted(legend.items(), key=lambda x: x[1], reverse=True):
            c = create_color_key([k], cmap=cmap, field_min=field_min, field_max=field_max, histogram_interval=histogram_interval).get(
                str(k), "#000000"
            )
            color_key_legend.append({"key": k, "color": c, "count": count})
        # Add Other to the end
        if other:
            k = "Other"
            count = other
            if not params.get("ellipses"):
                c = create_color_key([k], cmap=cmap, field_min=field_min, field_max=field_max, histogram_interval=histogram_interval).get(
                    str(k), "#000000"
                )
            else:
                # In ellipse mode everything gets it's own color
                # so there is never a color for Other
                c = None
            color_key_legend.append({"key": k, "color": c, "count": count})         

        return legend_response(json.dumps(color_key_legend), parameter_hash=parameter_hash, params=params)
Ejemplo n.º 26
0
 def test_geo_bounding_tile(self):
     gr = self.read_only_virtual_geo_raster()
     gv = gr.footprint().reproject(CRS({'init': 'epsg:4326'}))
     bounding_tile = mercantile.bounding_tile(*gv.get_shape(gv.crs).bounds)
     self.assertEqual(bounding_tile, (37108, 25248, 16))
Ejemplo n.º 27
0
def tiles(ctx, zoom, input, bounding_tile, with_bounds, seq, x_json_seq):
    """Lists Web Mercator tiles at ZOOM level intersecting
    GeoJSON [west, south, east, north] bounding boxen, features, or
    collections read from stdin. Output is a JSON
    [x, y, z [, west, south, east, north -- optional]] array.

    Example:

    $ echo "[-105.05, 39.95, -105, 40]" | mercantile tiles 12

    Output:

    [852, 1550, 12]
    [852, 1551, 12]
    [853, 1550, 12]
    [853, 1551, 12]

    """
    verbosity = ctx.obj['verbosity']
    logger = logging.getLogger('mercantile')
    try:
        src = click.open_file(input).readlines()
    except IOError:
        src = [input]

    src = iter(src)
    first_line = next(src)

    # If input is RS-delimited JSON sequence.
    if first_line.startswith(u'\x1e'):
        def feature_gen():
            buffer = first_line.strip(u'\x1e')
            for line in src:
                if line.startswith(u'\x1e'):
                    if buffer:
                        yield json.loads(buffer)
                    buffer = line.strip(u'\x1e')
                else:
                    buffer += line
            else:
                yield json.loads(buffer)
    else:
        def feature_gen():
            yield json.loads(first_line)
            for line in src:
                yield json.loads(line)

    try:
        source = feature_gen()
        # Detect the input format
        for obj in source:
            if isinstance(obj, list):
                bbox = obj
                if len(bbox) == 2:
                    bbox += bbox
                if len(bbox) != 4:
                    raise ValueError("Invalid input.")
            elif isinstance(obj, dict):
                if 'bbox' in obj:
                    bbox = obj['bbox']
                else:
                    box_xs = []
                    box_ys = []
                    for feat in obj.get('features', [obj]):
                        lngs, lats = zip(*list(coords(feat)))
                        box_xs.extend([min(lngs), max(lngs)])
                        box_ys.extend([min(lats), max(lats)])
                    bbox = min(box_xs), min(box_ys), max(box_xs), max(box_ys)
            west, south, east, north = bbox
            if bounding_tile:
                vals = mercantile.bounding_tile(
                        west, south, east, north, truncate=False)
                output = json.dumps(vals)
                if seq:
                    click.echo(u'\x1e')
                click.echo(output)
            else:
                # shrink the bounds a small amount so that
                # shapes/tiles round trip.
                epsilon = 1.0e-10
                west += epsilon
                south += epsilon
                east -= epsilon
                north -= epsilon
                for tile in mercantile.tiles(
                        west, south, east, north, [zoom], truncate=False):
                    vals = (tile.x, tile.y, zoom)
                    if with_bounds:
                        vals += mercantile.bounds(tile.x, tile.y, zoom)
                    output = json.dumps(vals)
                    if seq:
                        click.echo(u'\x1e')
                    click.echo(output)

        sys.exit(0)
    except Exception:
        logger.exception("Failed. Exception caught")
        sys.exit(1)
Ejemplo n.º 28
0
def test_bounding_tile():
    assert mercantile.bounding_tile(-92.5, 0.5, -90.5, 1.5) == (31, 63, 7)
    assert mercantile.bounding_tile(-90.5, 0.5, -89.5, 0.5) == (0, 0, 1)
    assert mercantile.bounding_tile(-92, 0, -88, 2) == (0, 0, 0)