예제 #1
0
파일: crs.py 프로젝트: diversoft/rasterio
def from_string(prjs):
    """Turn a PROJ.4 string into a mapping of parameters.

    Bare parameters like "+no_defs" are given a value of ``True``. All keys
    are checked against the ``all_proj_keys`` list.

    EPSG:nnnn is allowed.

    JSON text-encoded strings are allowed.
    """
    if '{' in prjs:
        # may be json, try to decode it
        try:
            val = json.loads(prjs, strict=False)
        except ValueError:
            raise CRSError('crs appears to be JSON but is not valid')

        if not val:
            raise CRSError("crs is empty JSON")
        else:
            return val

    if prjs.strip().upper().startswith('EPSG:'):
        return from_epsg(prjs.split(':')[1])

    parts = [o.lstrip('+') for o in prjs.strip().split()]

    def parse(v):
        if v in ('True', 'true'):
            return True
        elif v in ('False', 'false'):
            return False
        else:
            try:
                return int(v)
            except ValueError:
                pass
            try:
                return float(v)
            except ValueError:
                return v

    items = map(
        lambda kv: len(kv) == 2 and (kv[0], parse(kv[1])) or (kv[0], True),
        (p.split('=') for p in parts))

    out = dict((k, v) for k, v in items if k in all_proj_keys)

    if not out:
        raise CRSError("crs is empty or invalid: {}".format(prjs))

    return out
예제 #2
0
    def from_string(cls, string, morph_from_esri_dialect=False):
        """Make a CRS from an EPSG, PROJ, or WKT string

        Parameters
        ----------
        string : str
            An EPSG, PROJ, or WKT string.
        morph_from_esri_dialect : bool, optional
            If True, items in the input using Esri's dialect of WKT
            will be replaced by OGC standard equivalents.

        Returns
        -------
        CRS

        """
        try:
            string = string.strip()
        except AttributeError:
            pass

        if not string:
            raise CRSError("CRS is empty or invalid: {!r}".format(string))

        elif string.upper().startswith('EPSG:'):
            auth, val = string.split(':')
            if not val:
                raise CRSError("Invalid CRS: {!r}".format(string))
            return cls.from_epsg(val)

        elif string.startswith('{') or string.startswith('['):
            # may be json, try to decode it
            try:
                val = json.loads(string, strict=False)
            except ValueError:
                raise CRSError('CRS appears to be JSON but is not valid')

            if not val:
                raise CRSError("CRS is empty JSON")
            else:
                return cls.from_dict(**val)
        elif string.endswith("]"):
            return cls.from_wkt(
                string, morph_from_esri_dialect=morph_from_esri_dialect)
        elif "=" in string:
            return cls.from_proj4(string)

        obj = cls()
        obj._crs = _CRS.from_user_input(
            string, morph_from_esri_dialect=morph_from_esri_dialect)
        return obj
예제 #3
0
    def from_user_input(cls, value, morph_from_esri_dialect=False):
        """Make a CRS from various input

        Dispatches to from_epsg, from_proj, or from_string

        Parameters
        ----------
        value : obj
            A Python int, dict, or str.
        morph_from_esri_dialect : bool, optional
            If True, items in the input using Esri's dialect of WKT
            will be replaced by OGC standard equivalents.

        Returns
        -------
        CRS

        """
        if isinstance(value, cls):
            return value
        elif hasattr(value, "to_wkt") and callable(value.to_wkt):
            return cls.from_wkt(
                value.to_wkt(),
                morph_from_esri_dialect=morph_from_esri_dialect,
            )
        elif isinstance(value, int):
            return cls.from_epsg(value)
        elif isinstance(value, dict):
            return cls(**value)
        elif isinstance(value, string_types):
            return cls.from_string(
                value, morph_from_esri_dialect=morph_from_esri_dialect)
        else:
            raise CRSError("CRS is invalid: {!r}".format(value))
예제 #4
0
    def to_dict(self, projjson=False):
        """Convert CRS to a PROJ dict

        .. note:: If there is a corresponding EPSG code, it will be used
           when returning PROJ parameter dict.

        .. versionadded:: 1.3.0

        Parameters
        ----------
        projjson: bool, default=False
            If True, will convert to PROJ JSON dict (Requites GDAL 3.1+
            and PROJ 6.2+).  If False, will convert to PROJ parameter
            dict.

        Returns
        -------
        dict

        """
        if self._crs is None:
            raise CRSError("Undefined CRS has no dict representation")

        if projjson:
            text = self._crs.projjson()
            return json.loads(text) if text else {}

        epsg_code = self.to_epsg()
        if epsg_code:
            return {'init': 'epsg:{}'.format(epsg_code)}
        else:
            try:
                return self._crs.to_dict()
            except CRSError:
                return {}
예제 #5
0
파일: crs.py 프로젝트: Geosynopsis/xgeo
    def from_any(cls, proj: dict or str or int):
        """
        Makes CRS from any supported system

        Parameters
        ----------
        proj: dict or str or int
            Projection in PROJ, EPSG, WKT or CF.

        Returns
        -------
        CRS: XCRS
        """
        if isinstance(proj, dict):
            try:
                return cls.from_dict(proj)
            except CRSError:
                return cls.from_cf_dict(proj)
            except Exception:
                raise CRSError("{} is neither a PROJ4 or CF dict".format(proj))
        elif isinstance(proj, str):
            for method in ['from_string', 'from_proj4', 'from_wkt']:
                try:
                    return getattr(cls, method)(proj)
                except CRSError:
                    pass
            raise CRSError(
                "{} is neither a EPSG, PROJ4 or WKT string".format(proj))
        elif isinstance(proj, int):
            try:
                return cls.from_epsg(proj)
            except CRSError:
                raise CRSError("{} is not valid EPSG code".format(proj))
        elif isinstance(proj, cls):
            return proj
        else:
            raise IOError(
                "{} is not valid data type. Only dictionary, string or integer are supported"
                .format(proj))
예제 #6
0
def _to_crs_str(value):
    # After rasterio=1.0.14 WKT is backbone so try it first
    try:
        crs_ = CRS.from_wkt(value)
        crs_.is_valid
    except CRSError as err:
        logger.debug('Could not parse CRS as WKT', err)
        try:
            crs_ = CRS.from_string(value)
            crs_.is_valid
        except CRSError as err:
            logger.debug('Could not parse CRS as Proj4', err)
            raise CRSError('Could not interpret CRS input as '
                           'either WKT or Proj4')
    return crs_
예제 #7
0
def _parse_crs(crs):
    """Parse a coordinate reference system from a variety of representations.

    Parameters
    ----------
    crs : {str, dict, int, CRS}
        Must be either a rasterio CRS object, a proj-string, rasterio supported
        dictionary, WKT string, or EPSG integer.

    Returns
    -------
    rasterio.crs.CRS
        The parsed CRS.

    Raises
    ------
    CRSError
        Raises an error if the input cannot be parsed.

    """

    #
    # NOTE: This doesn't currently throw an error if the EPSG code is invalid.
    #
    parsed = None
    if isinstance(crs, CRS):
        parsed = crs
    elif isinstance(crs, str):
        try:
            # proj-string or wkt
            parsed = CRS.from_string(crs)
        except CRSError:
            # wkt
            parsed = CRS.from_wkt(crs)
    elif isinstance(crs, dict):
        parsed = CRS(crs)
    elif isinstance(crs, int):
        parsed = CRS.from_epsg(crs)
    elif isinstance(crs, pyproj.Proj):
        parsed = CRS.from_proj4(crs.proj4_init)

    if parsed is None or not parsed.is_valid:
        raise CRSError('Could not parse CRS: {}'.format(crs))

    return parsed
예제 #8
0
    def reproject(self, dst_crs):
        """
        Reprojects all the features to the new crs
        Args:
            dst_crs: rasterio.CRS or any acceptable by your rasterio version input (str, dict, epsg code), ot 'utm'

        Returns:
            new reprojected FeatureCollection
        """
        if isinstance(dst_crs, str) and dst_crs == 'utm':
            lon1, lat1, lon2, lat2 = self.index.bounds
            dst_crs = _utm_zone((lat1 + lat2)/2, (lon1 + lon2)/2)
        else:
            dst_crs = dst_crs if isinstance(dst_crs, CRS) else CRS.from_user_input(dst_crs)

        # Old rasterio compatibility: a separate check for validity
        if not dst_crs.is_valid:
            raise CRSError('Invalid CRS {} given'.format(dst_crs))

        features = [f.reproject(dst_crs) for f in self.features]
        return FeatureCollection(features, dst_crs)
예제 #9
0
파일: warp_.py 프로젝트: elfmanryan/nd
def get_extent(ds):
    """Extract the extent (bounding box) from the dataset.

    Parameters
    ----------
    ds : xarray.Dataset
        The input dataset

    Returns
    -------
    tuple
        The extent (left, bottom, right, top) in latitude and longitude
        coordinates.
    """

    #
    # Check if latitude and longitude are stored as coordinates.
    #
    if 'lon' in ds.coords and 'lat' in ds.coords:
        return BoundingBox(left=ds.lon.values.min(),
                           bottom=ds.lat.values.min(),
                           right=ds.lon.values.max(),
                           top=ds.lat.values.max())

    #
    # Otherwise, get extent from projection information
    # by projecting the corner coordinates onto EPSG:4326
    # to obtain the latitude and longitude at the four corners.
    #
    src_crs = get_crs(ds)
    if src_crs is None:
        raise CRSError('Could not determine the CRS.')

    dst_crs = CRS(init='epsg:4326')
    proj_bounds = get_bounds(ds)
    bounds = rasterio.warp.transform_bounds(src_crs, dst_crs,
                                            **proj_bounds._asdict())
    return BoundingBox(*bounds)
예제 #10
0
    def to_dict(self):
        """Convert CRS to a PROJ4 dict

        Notes
        -----
        If there is a corresponding EPSG code, it will be used.

        Returns
        -------
        dict

        """
        if self._crs is None:
            raise CRSError("Undefined CRS has no dict representation")

        else:
            epsg_code = self.to_epsg()
            if epsg_code:
                return {'init': 'epsg:{}'.format(epsg_code)}
            else:
                try:
                    return self._crs.to_dict()
                except CRSError:
                    return {}
예제 #11
0
def _reproject(ds,
               src_crs=None,
               dst_crs=None,
               dst_transform=None,
               width=None,
               height=None,
               res=None,
               extent=None,
               **kwargs):
    """Reproject a Dataset or DataArray.

    Parameters
    ----------
    ds : xarray.Dataset or xarray.DataArray
        The input dataset
    src_crs : CRS-like, optional
        An object that can be parsed into a CRS. By default, try to infer from
        input dataset.
    dst_crs : CRS-like, optional
        An object that can be parsed into a CRS. By default, use the same
        CRS as the input dataset.
    dst_transform : affine.Affine, optional
        The geometric transform of the output dataset.
    width : int, optional
        The width of the output dataset.
    height : int, optional
        The height of the output dataset.
    res : tuple (float, float), optional
        The resolution of the output dataset.
    extent : tuple, optional
        The output extent. By default this is determined from the input data.
    **kwargs : dict, optional
        Extra keyword arguments for ``rasterio.warp.reproject``.

    Returns
    -------
    xarray.Dataset or xarray.DataArray
        The projected dataset.
    """

    if src_crs is None:
        src_crs = get_crs(ds)
    if src_crs is None:
        raise CRSError('Could not infer projection from input data. '
                       'Please provide the parameter `src_crs`.')
    src_bounds = get_bounds(ds)
    if extent is not None:
        extent = BoundingBox(*extent)

    #
    # Only allow inferring of width or height from aspect ratio
    # if the CRS is not changed.
    #
    if dst_crs is None:
        dst_crs = src_crs
        if width is None and height is not None:
            width = int(ncols(ds) * height / nrows(ds))
        elif height is None and width is not None:
            height = int(nrows(ds) * width / ncols(ds))

    # Given: transform, shape
    # Given: transform, extent
    # Given: res, extent
    # Given: shape, res
    # Given: shape, extent

    if dst_transform is not None:
        #
        # If the transform is given, we also need the width and height or
        # the extent.
        #
        if width is not None and height is not None:
            pass
        elif extent is not None:
            # Calculate width and height from extent
            width = int(abs(
                (extent.right - extent.left) / dst_transform.a)) + 1
            height = int(abs(
                (extent.top - extent.bottom) / dst_transform.e)) + 1
        else:
            raise ValueError('Not enough information provided.')

    elif extent is not None:
        #
        # Transform can be calculated from extent, if either width and height
        # or the resolution are given.
        #
        if res is not None:
            width = int(abs((extent.right - extent.left) / res[0])) + 1
            height = int(abs((extent.top - extent.bottom) / res[1])) + 1

        # The following doesn't give the correct result.
        dst_transform = rasterio.transform.from_bounds(*extent,
                                                       width=width - 1,
                                                       height=height - 1)

    else:
        #
        # If neither the transform nor the extent are given, infer the best
        # possible parameters from the width, height, and the resolution.
        #
        dst_transform, width, height = \
            rasterio.warp.calculate_default_transform(
                src_crs, dst_crs,
                ncols(ds), nrows(ds),
                resolution=res,
                dst_width=width,
                dst_height=height,
                **src_bounds._asdict())

    src_transform = get_transform(ds)
    src_dims = get_dims(ds)
    dst_crs = _parse_crs(dst_crs)

    #
    # Prepare new x and y coordinate arrays
    #
    dst_x, _ = dst_transform * (np.arange(width), np.zeros(width, dtype=int))
    _, dst_y = dst_transform * (np.zeros(height, dtype=int), np.arange(height))
    dst_coords = {'x': dst_x, 'y': dst_y}

    #
    # Handle the case where there are extra dimensions, e.g. 'time'
    # or 'band'
    #
    extra_dims = set(src_dims) - {'y', 'x'}

    for c in extra_dims:
        dst_coords[c] = ds.coords[c]

    def _reproject_da(da, shape):
        #
        # Reproject a single data array
        #
        coord_dims = tuple(c for c in ('y', 'x') if c in da.dims)
        extra_dims = set(da.dims) - set(coord_dims)
        # Preserve original dimension order
        orig_dim_order = get_dims(da)
        ordered_extra_dims = \
            tuple(d for d in orig_dim_order if d in extra_dims)
        dim_order = ordered_extra_dims + coord_dims

        # Determine best resampling method from data type
        if np.issubdtype(da.dtype, np.integer):
            nodata = 0
            default_resampling = rasterio.warp.Resampling.nearest
        else:
            nodata = np.nan
            default_resampling = rasterio.warp.Resampling.bilinear
        if 'resampling' not in kwargs:
            kwargs['resampling'] = default_resampling

        # Get values as numpy array such that last two axes are
        # y and x
        values = da.transpose(*dim_order, transpose_coords=True).values

        # Flatten multidimensional data to ONE extra dimension
        if values.ndim > 2:
            output_shape = values.shape[:-2] + shape
            values = values.reshape((-1, ) + values.shape[-2:])
            output_shape_flat = (values.shape[0], ) + shape
        else:
            output_shape = shape
            output_shape_flat = shape

        # rasterio cannot deal with float16
        if da.dtype == np.float16:
            values = values.astype(np.float32)

        # Integers cannot be set to nan
        if np.issubdtype(values.dtype, np.integer):
            values = values.astype(np.float32)

        output = np.zeros(output_shape_flat, dtype=values.dtype)
        output[:] = np.nan

        rasterio.warp.reproject(values,
                                output,
                                src_transform=src_transform,
                                src_crs=src_crs,
                                dst_transform=dst_transform,
                                dst_crs=dst_crs,
                                dst_nodata=nodata,
                                **kwargs)

        if da.dtype == np.float16:
            output = output.astype(np.float16)

        # Final reshape in case the input was one-dimensional
        return output.reshape(output_shape)

    if isinstance(ds, xr.Dataset):
        result = xr.Dataset(coords=dst_coords)

        #
        # Also reproject coordinate arrays that are defined over
        # x and y
        #
        for v in ds.coords:
            #
            # If the projection is the same (i.e. resampling),
            # also reproject coordinate arrays
            # that are defined over only one variable.
            #
            if dst_crs == src_crs and v not in ds.dims:

                if len(ds.coords[v].dims) == 0:
                    result.coords[v] = (ds.coords[v].dims, ds.coords[v].data)

                else:
                    expanded = _expand_var_to_xy(ds.coords[v], ds.coords)
                    if ds.coords[v].dims == ('x', ):
                        result.coords[v] = (('y', 'x'),
                                            _reproject_da(
                                                expanded, (height, width)))
                    elif ds.coords[v].dims == ('y', ):
                        result.coords[v] = (('y', 'x'),
                                            _reproject_da(
                                                expanded, (height, width)))

                    # Remove redundant dimensions
                    coords = _collapse_coords(result.coords[v])
                    result.coords[v] = (coords.dims, coords)

            if not set(ds.coords[v].dims).issuperset({'x', 'y'}):
                continue

            shape = (height, width)
            result.coords[v] = (('y', 'x'), _reproject_da(ds.coords[v], shape))

        #
        # Reproject the actual data
        #
        for v in ds.data_vars:
            vdims = _get_projection_dim_order(ds[v])
            common = set(vdims).intersection(ds[v].dims)
            shape = (height, width)
            if set(ds[v].dims) == set(vdims) or set(ds[v].dims) == {'y', 'x'}:
                result[v] = (vdims, _reproject_da(ds[v], shape))
                # Reorder dimensions of each variable to match original.
                result[v] = result[v].transpose(*get_dims(ds[v]),
                                                transpose_coords=True)
            elif common == {'x'} or common == {'y'}:
                # Does the data contain either x or y dimension?
                # Expand to grid and then reproject as two dimensional.
                result[v] = (vdims,
                             _reproject_da(_expand_var_to_xy(ds[v], ds.coords),
                                           shape))
            else:
                # The variable doesn't contain either y or x dimension.
                result[v] = (ds[v].dims, ds[v].data)

        #
        # Create lat and lon coordinates
        #
        # if 'lat' in ds.coords and 'lon' in ds.coords:
        #     lon, lat = rasterio.warp.transform(
        #         src_crs, dst_crs, ds.coords['x'], ds.coords['y'])
        #     result.coords['lon'] = (('x',), lon)
        #     result.coords['lat'] = (('y',), lat)

    elif isinstance(ds, xr.DataArray):
        shape = (height, width)
        dst_dims = _get_projection_dim_order(ds)
        result = xr.DataArray(_reproject_da(ds, shape),
                              dims=dst_dims,
                              coords=dst_coords,
                              name=ds.name)

        # Reorder dimensions to match original.
        result = result.transpose(*get_dims(ds), transpose_coords=True)

    #
    # Add metadata
    #
    result.attrs = ds.attrs

    # Serialize transform to tuple and store in metadata
    result.attrs['transform'] = dst_transform[:6]
    # Store CRS info in metadata
    result.attrs['crs'] = dst_crs.to_string()
    result.attrs['coordinate_system_string'] = dst_crs.wkt
    # Store new data shape in metadata
    result.attrs['lines'] = nrows(result)
    result.attrs['samples'] = ncols(result)

    _add_latlon(result)

    return result