Exemple #1
0
    def algorithm(self, inputs, coordinates):
        cs = [Coordinates.from_xarray(x) for x in inputs.values()]
        if any(c != cs[0] for c in cs):
            raise NodeException("Cannot combine inputs with different coordinates")

        data = np.stack([inputs[key] for key in self.inputs], axis=-1)
        return self.create_output_array(cs[0], data=data)
Exemple #2
0
    def coordinates(self):
        if self.data is None:
            _log.warning("No coordinates found in EGI source")
            return Coordinates([], dims=[])

        return Coordinates.from_xarray(self.data.coords,
                                       crs=self.data.attrs["crs"])
Exemple #3
0
    def open(cls, *args, **kwargs):
        """
        Open an :class:`podpac.UnitsDataArray` from a file or file-like object containing a single data variable.

        This is a wrapper around :func:`xarray.open_datarray`.
        The inputs to this function are passed directly to :func:`xarray.open_datarray`.
        See http://xarray.pydata.org/en/stable/generated/xarray.open_dataarray.html#xarray.open_dataarray.

        The DataArray passed back from :func:`xarray.open_datarray` is used to create a units data array using :func:`creare_dataarray`.

        Returns
        -------
        :class:`podpac.UnitsDataArray`
        """
        da = xr.open_dataarray(*args, **kwargs)
        coords = Coordinates.from_xarray(da)

        # pass in kwargs to constructor
        uda_kwargs = {"attrs": da.attrs}
        if "output" in da.dims:
            uda_kwargs.update({"outputs": da.coords["output"]})
        return cls.create(coords, data=da.data, **uda_kwargs)
Exemple #4
0
def to_geotiff(fp, data, geotransform=None, crs=None, **kwargs):
    """Export a UnitsDataArray to a Geotiff

    Params
    -------
    fp:  str, file object or pathlib.Path object
        A filename or URL, a file object opened in binary ('rb') mode, or a Path object. If not supplied, the results will
        be written to a memfile object
    data: UnitsDataArray, xr.DataArray, np.ndarray
        The data to be saved. If there is more than 1 band, this should be the last dimension of the array.
        If given a np.ndarray, ensure that the 'lat' dimension is aligned with the rows of the data, with an appropriate
        geotransform.
    geotransform: tuple, optional
        The geotransform that describes the input data. If not given, will look for data.attrs['geotransform']
    crs: str, optional
        The coordinate reference system for the data
    kwargs: **dict
        Additional key-word arguments that overwrite defaults used in the `rasterio.open` function. This function
        populates the following defaults:
                drive="GTiff"
                height=data.shape[0]
                width=data.shape[1]
                count=data.shape[2]
                dtype=data.dtype
                mode="w"

    Returns
    --------
    MemoryFile, list
        If fp is given, results a list of the results for writing to each band r.append(dst.write(data[..., i], i + 1))
        If fp is None, returns the MemoryFile object
    """

    # This only works for data that essentially has lat/lon only
    dims = list(data.coords.keys())
    if "lat" not in dims or "lon" not in dims:
        raise NotImplementedError("Cannot export GeoTIFF for dataset with lat/lon coordinates.")
    if "time" in dims and len(data.coords["time"]) > 1:
        raise NotImplemented("Cannot export GeoTIFF for dataset with multiple times,")
    if "alt" in dims and len(data.coords["alt"]) > 1:
        raise NotImplemented("Cannot export GeoTIFF for dataset with multiple altitudes.")

    # TODO: add proper checks, etc. to make sure we handle edge cases and throw errors when we cannot support
    #       i.e. do work to remove this warning.
    _logger.warning("GeoTIFF export assumes data is in a uniform, non-rotated coordinate system.")

    # Get the crs and geotransform that describes the coordinates
    if crs is None:
        crs = data.attrs.get("crs")
    if crs is None:
        raise ValueError(
            "The `crs` of the data needs to be provided to save as GeoTIFF. If supplying a UnitsDataArray, created "
            " through a PODPAC Node, the crs should be automatically populated. If not, please file an issue."
        )
    if geotransform is None:
        geotransform = data.attrs.get("geotransform")
        # Geotransform should ALWAYS be defined as (lon_origin, lon_dj, lon_di, lat_origin, lat_dj, lat_di)
        # if isinstance(data, xr.DataArray) and data.dims.index('lat') > data.dims.index('lon'):
        # geotransform = geotransform[3:] + geotransform[:3]

    if geotransform is None:
        try:
            geotransform = Coordinates.from_xarray(data).geotransform
        except (TypeError, AttributeError):
            raise ValueError(
                "The `geotransform` of the data needs to be provided to save as GeoTIFF. If the geotransform attribute "
                "wasn't automatically populated as part of the dataset, it means that the data is in a non-uniform "
                "coordinate system. This can sometimes happen when the data is transformed to a different CRS than the "
                "native CRS, which can cause the coordinates to seems non-uniform due to floating point precision. "
            )

    # Make all types into a numpy array
    if isinstance(data, xr.DataArray):
        data = data.data

    # Get the data
    dtype = kwargs.get("dtype", np.float32)
    data = data.astype(dtype).squeeze()

    if len(data.shape) == 2:
        data = data[:, :, None]

    geotransform = affine.Affine.from_gdal(*geotransform)

    # Update the kwargs that rasterio will use. Anything added by the user will take priority.
    kwargs2 = dict(
        driver="GTiff",
        height=data.shape[0],
        width=data.shape[1],
        count=data.shape[2],
        dtype=data.dtype,
        crs=crs,
        transform=geotransform,
    )
    kwargs2.update(kwargs)

    # Write the file
    if fp is None:
        # Write to memory file
        r = rasterio.io.MemoryFile()
        with r.open(**kwargs2) as dst:
            for i in range(data.shape[2]):
                dst.write(data[..., i], i + 1)
    else:
        r = []
        kwargs2["mode"] = "w"
        with rasterio.open(fp, **kwargs2) as dst:
            for i in range(data.shape[2]):
                r.append(dst.write(data[..., i], i + 1))

    return r