Exemple #1
0
def _describe_file(filepath):
    """
    Helper function to describe a geospatial data
    First checks if a sidecar mcf file is available, if so uses that
    if not, script will parse the file to retrieve some info from the file

    :param filepath: path to file

    :returns: `dict` of GeoJSON item
    """

    content = {'bbox': None, 'geometry': None, 'properties': {}}

    mcf_file = '{}.yml'.format(os.path.splitext(filepath)[0])

    if os.path.isfile(mcf_file):
        try:
            from pygeometa.core import read_mcf, MCFReadError
            from pygeometa.schemas.stac import STACItemOutputSchema

            md = read_mcf(mcf_file)
            stacjson = STACItemOutputSchema.write(STACItemOutputSchema, md)
            stacdata = loads(stacjson)
            for k, v in stacdata.items():
                content[k] = v
        except ImportError:
            LOGGER.debug('pygeometa not found')
        except MCFReadError as err:
            LOGGER.warning('MCF error: {}'.format(err))
    else:
        LOGGER.debug('No mcf found at: {}'.format(mcf_file))

    if content['geometry'] is None and content['bbox'] is None:
        try:
            import rasterio
            from rasterio.crs import CRS
            from rasterio.warp import transform_bounds
        except ImportError as err:
            LOGGER.warning('rasterio not found')
            LOGGER.warning(err)
            return content

        try:
            import fiona
        except ImportError as err:
            LOGGER.warning('fiona not found')
            LOGGER.warning(err)
            return content

        try:  # raster
            LOGGER.debug('Testing raster data detection')
            d = rasterio.open(filepath)
            content['bbox'] = [
                d.bounds.left, d.bounds.bottom, d.bounds.right, d.bounds.top
            ]
            content['geometry'] = {
                'type':
                'Polygon',
                'coordinates': [[[d.bounds.left, d.bounds.bottom],
                                 [d.bounds.left, d.bounds.top],
                                 [d.bounds.right, d.bounds.top],
                                 [d.bounds.right, d.bounds.bottom],
                                 [d.bounds.left, d.bounds.bottom]]]
            }
            for k, v in d.tags(d.count).items():
                content['properties'][k] = v
                if k in ['GRIB_REF_TIME']:
                    value = int(v.split()[0])
                    datetime_ = datetime.fromtimestamp(value)
                    content['properties']['datetime'] = datetime_.isoformat(
                    ) + 'Z'  # noqa
        except rasterio.errors.RasterioIOError:
            try:
                LOGGER.debug('Testing vector data detection')
                d = fiona.open(filepath)
                scrs = CRS(d.crs)
                if scrs.to_epsg() is not None and scrs.to_epsg() != 4326:
                    tcrs = CRS.from_epsg(4326)
                    bnds = transform_bounds(scrs, tcrs, d.bounds[0],
                                            d.bounds[1], d.bounds[2],
                                            d.bounds[3])
                    content['properties']['projection'] = scrs.to_epsg()
                else:
                    bnds = d.bounds

                if d.schema['geometry'] not in [None, 'None']:
                    content['bbox'] = [bnds[0], bnds[1], bnds[2], bnds[3]]
                    content['geometry'] = {
                        'type':
                        'Polygon',
                        'coordinates':
                        [[[bnds[0], bnds[1]], [bnds[0], bnds[3]],
                          [bnds[2], bnds[3]], [bnds[2], bnds[1]],
                          [bnds[0], bnds[1]]]]
                    }

                for k, v in d.schema['properties'].items():
                    content['properties'][k] = v

                if d.driver == 'ESRI Shapefile':
                    id_ = os.path.splitext(os.path.basename(filepath))[0]
                    content['assets'] = {}
                    for suffix in ['shx', 'dbf', 'prj']:
                        fullpath = '{}.{}'.format(
                            os.path.splitext(filepath)[0], suffix)

                        if os.path.exists(fullpath):
                            filectime = file_modified_iso8601(fullpath)
                            filesize = os.path.getsize(fullpath)

                            content['assets'][suffix] = {
                                'href': './{}.{}'.format(id_, suffix),
                                'created': filectime,
                                'file:size': filesize
                            }

            except fiona.errors.DriverError:
                LOGGER.debug('Could not detect raster or vector data')

    return content
Exemple #2
0
def CRS_to_uri(crs: CRS) -> str:
    """Convert CRS to URI."""
    epsg_code = crs.to_epsg()
    return f"http://www.opengis.net/def/crs/EPSG/0/{epsg_code}"
Exemple #3
0
def _describe_file(filepath):
    """
    Helper function to describe a geospatial data

    :param filepath: path to file

    :returns: `dict` of GeoJSON item
    """

    content = {'bbox': None, 'geometry': None, 'properties': {}}

    try:
        import rasterio
        from rasterio.crs import CRS
        from rasterio.warp import transform_bounds
        LOGGER.warning(
            'rasterio not found. Cannot derive geospatial properties')  # noqa
    except ImportError as err:
        LOGGER.warning(err)
        return content

    try:
        import fiona
    except ImportError as err:
        LOGGER.warning('fiona not found. Cannot derive geospatial properties')
        LOGGER.warning(err)
        return content

    try:  # raster
        LOGGER.debug('Testing raster data detection')
        d = rasterio.open(filepath)
        content['bbox'] = [
            d.bounds.left, d.bounds.bottom, d.bounds.right, d.bounds.top
        ]
        content['geometry'] = {
            'type':
            'Polygon',
            'coordinates': [[[d.bounds.left, d.bounds.bottom],
                             [d.bounds.left, d.bounds.top],
                             [d.bounds.right, d.bounds.top],
                             [d.bounds.right, d.bounds.bottom],
                             [d.bounds.left, d.bounds.bottom]]]
        }
        for k, v in d.tags(1).items():
            content['properties'][k] = v
    except rasterio.errors.RasterioIOError:
        LOGGER.debug('Testing vector data detection')
        d = fiona.open(filepath)
        scrs = CRS(d.crs)
        if scrs.to_epsg() is not None and scrs.to_epsg() != 4326:
            tcrs = CRS.from_epsg(4326)
            bnds = transform_bounds(scrs, tcrs, d.bounds[0], d.bounds[1],
                                    d.bounds[2], d.bounds[3])
            content['properties']['projection'] = scrs.to_epsg()
        else:
            bnds = d.bounds

        if d.schema['geometry'] not in [None, 'None']:
            content['bbox'] = [bnds[0], bnds[1], bnds[2], bnds[3]]
            content['geometry'] = {
                'type':
                'Polygon',
                'coordinates': [[[bnds[0], bnds[1]], [bnds[0], bnds[3]],
                                 [bnds[2], bnds[3]], [bnds[2], bnds[1]],
                                 [bnds[0], bnds[1]]]]
            }

        for k, v in d.schema['properties'].items():
            content['properties'][k] = v

        if d.driver == 'ESRI Shapefile':
            id_ = os.path.splitext(os.path.basename(filepath))[0]
            content['assets'] = {}
            for suffix in ['shx', 'dbf', 'prj', 'shp.xml']:
                content['assets'][suffix] = {
                    'href': './{}.{}'.format(id_, suffix)
                }
    return content
Exemple #4
0
    def custom(
        cls,
        extent: List[float],
        crs: CRS,
        tile_width: int = 256,
        tile_height: int = 256,
        matrix_scale: List = [1, 1],
        extent_crs: Optional[CRS] = None,
        minzoom: int = 0,
        maxzoom: int = 24,
        title: str = "Custom TileMatrixSet",
        identifier: str = "Custom",
    ):
        """
        Construct a custom TileMatrixSet.

        Attributes
        ----------
        crs: rasterio.crs.CRS
            Tile Matrix Set coordinate reference system
        extent: list
            Bounding box of the Tile Matrix Set, (left, bottom, right, top).
        tile_width: int
            Width of each tile of this tile matrix in pixels (default is 256).
        tile_height: int
            Height of each tile of this tile matrix in pixels (default is 256).
        matrix_scale: list
            Tiling schema coalescence coefficient (default: [1, 1] for EPSG:3857).
            Should be set to [2, 1] for EPSG:4326.
            see: http://docs.opengeospatial.org/is/17-083r2/17-083r2.html#14
        extent_crs: rasterio.crs.CRS
            Extent's coordinate reference system, as a rasterio CRS object.
            (default: same as input crs)
        minzoom: int
            Tile Matrix Set minimum zoom level (default is 0).
        maxzoom: int
            Tile Matrix Set maximum zoom level (default is 24).
        title: str
            Tile Matrix Set title (default is 'Custom TileMatrixSet')
        identifier: str
            Tile Matrix Set identifier (default is 'Custom')

        Returns:
        --------
        TileMatrixSet

        """
        tms: Dict[str, Any] = {
            "title": title,
            "identifier": identifier,
            "supportedCRS": crs,
            "tileMatrix": [],
        }

        is_inverted = False
        if crs.to_epsg():
            # We use URI because with EPSG code it doesn't work in GDAL 2
            crs_uri = CRS_to_uri(crs)
            is_inverted = crs_axis_inverted(CRS.from_user_input(crs_uri))

        if is_inverted:
            tms["boundingBox"] = TMSBoundingBox(
                crs=extent_crs or crs,
                lowerCorner=[extent[1], extent[0]],
                upperCorner=[extent[2], extent[3]],
            )
        else:
            tms["boundingBox"] = TMSBoundingBox(
                crs=extent_crs or crs,
                lowerCorner=[extent[0], extent[1]],
                upperCorner=[extent[2], extent[3]],
            )

        if extent_crs:
            bbox = BoundingBox(
                *transform_bounds(extent_crs, crs, *extent, densify_pts=21))
        else:
            bbox = BoundingBox(*extent)

        x_origin = bbox.left if not is_inverted else bbox.top
        y_origin = bbox.top if not is_inverted else bbox.left

        width = abs(bbox.right - bbox.left)
        height = abs(bbox.top - bbox.bottom)
        mpu = meters_per_unit(crs)
        for zoom in range(minzoom, maxzoom + 1):
            res = max(
                width / (tile_width * matrix_scale[0]) / 2.0**zoom,
                height / (tile_height * matrix_scale[1]) / 2.0**zoom,
            )
            tms["tileMatrix"].append(
                TileMatrix(**dict(
                    identifier=str(zoom),
                    scaleDenominator=res * mpu / 0.00028,
                    topLeftCorner=[x_origin, y_origin],
                    tileWidth=tile_width,
                    tileHeight=tile_height,
                    matrixWidth=matrix_scale[0] * 2**zoom,
                    matrixHeight=matrix_scale[1] * 2**zoom,
                )))

        return cls(**tms)