Example #1
0
    def setUp(self):
        """Create test data and mock pycoast/pydecorate."""
        from trollimage.xrimage import XRImage
        from pyresample.geometry import AreaDefinition
        import xarray as xr
        import dask.array as da

        proj_dict = {'proj': 'lcc', 'datum': 'WGS84', 'ellps': 'WGS84',
                     'lon_0': -95., 'lat_0': 25, 'lat_1': 25,
                     'units': 'm', 'no_defs': True}
        self.area_def = AreaDefinition(
            'test', 'test', 'test', proj_dict,
            200, 400, (-1000., -1500., 1000., 1500.),
        )
        self.orig_rgb_img = XRImage(
            xr.DataArray(da.arange(75., chunks=10).reshape(3, 5, 5) / 75.,
                         dims=('bands', 'y', 'x'),
                         coords={'bands': ['R', 'G', 'B']},
                         attrs={'name': 'test_ds', 'area': self.area_def})
        )
        self.orig_l_img = XRImage(
            xr.DataArray(da.arange(25., chunks=10).reshape(5, 5) / 75.,
                         dims=('y', 'x'),
                         attrs={'name': 'test_ds', 'area': self.area_def})
        )

        self.decorate = {
            'decorate': [
                {'logo': {'logo_path': '', 'height': 143, 'bg': 'white', 'bg_opacity': 255}},
                {'text': {
                    'txt': 'TEST',
                    'align': {'top_bottom': 'bottom', 'left_right': 'right'},
                    'font': '',
                    'font_size': 22,
                    'height': 30,
                    'bg': 'black',
                    'bg_opacity': 255,
                    'line': 'white'}},
                {'scale': {
                    'colormap': greys,
                    'extend': False,
                    'width': 1670, 'height': 110,
                    'tick_marks': 5, 'minor_tick_marks': 1,
                    'cursor': [0, 0], 'bg':'white',
                    'title':'TEST TITLE OF SCALE',
                    'fontsize': 110, 'align': 'cc'
                }}
            ]
        }

        import_mock = mock.MagicMock()
        modules = {'pycoast': import_mock.pycoast,
                   'pydecorate': import_mock.pydecorate}
        self.module_patcher = mock.patch.dict('sys.modules', modules)
        self.module_patcher.start()
Example #2
0
 def test_colormap_write(self):
     """Test writing an image with a colormap."""
     from satpy.writers.geotiff import GeoTIFFWriter
     from trollimage.xrimage import XRImage
     from trollimage.colormap import spectral
     datasets = self._get_test_datasets()
     w = GeoTIFFWriter(base_dir=self.base_dir)
     # we'd have to customize enhancements to test this through
     # save_datasets. We'll use `save_image` as a workaround.
     img = XRImage(datasets[0])
     img.palettize(spectral)
     w.save_image(img, keep_palette=True)
Example #3
0
def palettize(img, min_out, max_out, min_in=0, max_in=1.0, colormap=None, alpha=True, **kwargs):
    """Apply a colormap to data and return the indices in to that colormap."""
    import xarray as xr
    import dask.array as da
    from trollimage.xrimage import XRImage
    import trollimage.colormap as ticolormap
    from satpy import CHUNK_SIZE
    good_data_mask = kwargs['good_data_mask']

    if img.ndim > 2:
        raise ValueError("Not sure how to palettize more than 2 dimensions")

    if colormap is None:
        raise ValueError("'colormap' is required for 'palettize' rescaling")
    elif not isinstance(colormap, ticolormap.Colormap):
        raise ValueError("Unknown 'colormap' type: %s", str(type(colormap)))

    dims = ('y', 'x') if img.ndim == 2 else ('y',)
    attrs = kwargs.get('attrs', {})
    xrimg = XRImage(
        xr.DataArray(da.from_array(img, chunks=CHUNK_SIZE), dims=dims, attrs=attrs))
    if alpha:
        # use colormap as is
        tmp_cmap = colormap
        # produce LA image
        xrimg = xrimg.convert(xrimg.mode + 'A')
    else:
        # the colormap has a value at 0 (first position) that represents
        # invalid data. We should palettize based on the colormap without
        # the 0 and then increment
        tmp_cmap = ticolormap.Colormap(*zip(colormap.values[1:], colormap.colors[1:]))

    tmp_cmap.set_range(min_in, max_in)
    xrimg.palettize(tmp_cmap)
    img_data = xrimg.data.values

    if alpha:
        # multiply alpha by the output size
        img_data[1, :, :] *= max_out
    else:
        # get the single band (L)
        img_data = img_data[0]
        # increment the indexes by 1 because the colormap has a 0 fill value
        img_data += 1
        img_data[~good_data_mask] = 0
    # our data values are now integers that can't be scaled to the output type
    # because they need to match the colormap
    return img_data
Example #4
0
    def setUp(self):
        """Create test data and mock pycoast/pydecorate."""
        from trollimage.xrimage import XRImage
        from pyresample.geometry import AreaDefinition
        import xarray as xr
        import dask.array as da

        proj_dict = {'proj': 'lcc', 'datum': 'WGS84', 'ellps': 'WGS84',
                     'lon_0': -95., 'lat_0': 25, 'lat_1': 25,
                     'units': 'm', 'no_defs': True}
        self.area_def = AreaDefinition(
            'test', 'test', 'test', proj_dict,
            200, 400, (-1000., -1500., 1000., 1500.),
        )
        self.orig_rgb_img = XRImage(
            xr.DataArray(da.arange(75., chunks=10).reshape(3, 5, 5) / 75.,
                         dims=('bands', 'y', 'x'),
                         coords={'bands': ['R', 'G', 'B']},
                         attrs={'name': 'test_ds', 'area': self.area_def})
        )
        self.orig_l_img = XRImage(
            xr.DataArray(da.arange(25., chunks=10).reshape(5, 5) / 75.,
                         dims=('y', 'x'),
                         attrs={'name': 'test_ds', 'area': self.area_def})
        )

        self.decorate = {
            'decorate': [
                {'logo': {'logo_path': '', 'height': 143, 'bg': 'white', 'bg_opacity': 255}},
                {'text': {
                    'txt': 'TEST',
                    'align': {'top_bottom': 'bottom', 'left_right': 'right'},
                    'font': '',
                    'font_size': 22,
                    'height': 30,
                    'bg': 'black',
                    'bg_opacity': 255,
                    'line': 'white'}}
            ]
        }

        self.contour_writer = mock.patch('pycoast.ContourWriterAGG')
        self.dec_writer = mock.patch('pydecorate.DecoratorAGG')
        self.cw = self.contour_writer.start()
        self.dw = self.dec_writer.start()
Example #5
0
def _pil_to_xrimage(img, adef, fill_value=None):
    """Convert PIL image to trollimage.xrimage.XRImage"""
    # Get image mode, width and height
    mode = img.mode
    width = img.width
    height = img.height

    # Convert to Numpy array
    img = np.array(img)

    # Get the minimum and maximum values of the input datatype
    min_val = np.iinfo(img.dtype).min
    max_val = np.iinfo(img.dtype).max

    img = img.reshape((height, width, len(mode)))

    # Reorder for XRImage
    img = np.rollaxis(img, 2, -3)
    # Remove alpha channel if fill_value is set
    if fill_value is not None:
        if 'A' in mode:
            mask = img[-1, :, :] == min_val
            # Remove alpha channel
            img = img[:-1, :, :]
            if np.any(mask):
                # Set fill_value for each channel
                for i in range(img.shape[0]):
                    chan = img[i, :, :]
                    chan[mask] = fill_value
    bands = BANDS[img.shape[0]]

    # Add image data to scene as xarray.DataArray
    img = xr.DataArray(img, dims=['bands', 'y', 'x'])
    img['bands'] = bands
    # Add area definition
    img.attrs['area'] = adef

    # Convert to XRImage
    img = XRImage(img)
    # Static stretch
    img.crude_stretch(min_val, max_val)

    return img
Example #6
0
def to_image(dataset, copy=False, **kwargs):
    # Only add keywords if they are present
    for key in ["mode", "fill_value", "palette"]:
        if key in dataset.attrs:
            kwargs.setdefault(key, dataset.attrs[key])
    dataset = dataset.squeeze()

    if dataset.ndim < 2:
        raise ValueError("Need at least a 2D array to make an image.")
    else:
        return XRImage(dataset)
Example #7
0
def test_write_rgba():
    """Test saving an RGBA image."""
    area = STEREOGRAPHIC_AREA

    fill_value = np.nan
    arr = create_hsv_color_disk(fill_value)

    attrs = dict([('platform_name', 'NOAA-18'),
                  ('resolution', 1050),
                  ('polarization', None),
                  ('start_time', TIME - datetime.timedelta(minutes=55)),
                  ('end_time', TIME - datetime.timedelta(minutes=50)),
                  ('level', None),
                  ('sensor', 'avhrr-3'),
                  ('ancillary_variables', []),
                  ('area', area),
                  ('wavelength', None),
                  ('optional_datasets', []),
                  ('standard_name', 'overview'),
                  ('name', 'overview'),
                  ('prerequisites', [0.6, 0.8, 10.8]),
                  ('optional_prerequisites', []),
                  ('calibration', None),
                  ('modifiers', None),
                  ('mode', 'RGBA'),
                  ('enhancement_history', [{'scale': np.array([1,  1, -1]), 'offset': np.array([0, 0, 1])},
                                           {'scale': np.array([0.0266347, 0.03559078, 0.01329783]),
                                            'offset': np.array([-0.02524969, -0.01996642,  3.8918446])},
                                           {'gamma': 1.6}])])

    kwargs = {'compute': True, 'fill_value': None, 'sat_id': 6300014,
              'chan_id': 6500015, 'data_cat': 'PPRN', 'data_source': 'SMHI', 'nbits': 8}
    alpha = np.where(np.isnan(arr[0, :, :]), 0, 1)
    arr = np.nan_to_num(arr)
    arr = np.vstack((arr, alpha[np.newaxis, :, :]))
    data = da.from_array(arr.clip(0, 1), chunks=1024)

    data = xr.DataArray(data, coords={'bands': ['R', 'G', 'B', 'A']}, dims=[
                        'bands', 'y', 'x'], attrs=attrs)

    img = XRImage(data)
    with tempfile.NamedTemporaryFile(delete=DELETE_FILES) as tmpfile:
        filename = tmpfile.name
        if not DELETE_FILES:
            print(filename)
        save(img, filename, data_is_scaled_01=True, **kwargs)
        tif = TiffFile(filename)
        res = tif[0].asarray()
        for idx in range(4):
            np.testing.assert_allclose(res[:, :, idx], np.round(
                np.nan_to_num(arr[idx, :, :]) * 255).astype(np.uint8))
        np.testing.assert_allclose(res[:, :, 3] == 0, alpha == 0)
Example #8
0
    def _test_enhancement(self, func, data, expected, **kwargs):
        from trollimage.xrimage import XRImage

        pre_attrs = data.attrs
        img = XRImage(data)
        func(img, **kwargs)

        self.assertIsInstance(img.data.data, da.Array)
        self.assertListEqual(sorted(pre_attrs.keys()),
                             sorted(img.data.attrs.keys()),
                             "DataArray attributes were not preserved")

        np.testing.assert_allclose(img.data.values, expected, atol=1.e-6, rtol=0)
Example #9
0
    def test_cimss_true_color_contrast(self):
        """Test the cimss_true_color_contrast enhancement."""
        from satpy.enhancements.abi import cimss_true_color_contrast
        from trollimage.xrimage import XRImage

        expected = np.array([[
            [0., 0., 0.05261956, 0.13396146],
            [0.21530335, 0.29664525, 0.37798715, 0.45932905],
            [0.54067095, 0.62201285, 0.70335475, 0.78469665],
            [0.86603854, 0.94738044, 1., 1.],
            ]])
        img = XRImage(self.da)
        cimss_true_color_contrast(img)
        np.testing.assert_almost_equal(img.data.compute(), expected)
Example #10
0
def run_and_check_enhancement(func, data, expected, **kwargs):
    """Perform basic checks that apply to multiple tests."""
    from trollimage.xrimage import XRImage

    pre_attrs = data.attrs
    img = XRImage(data)
    func(img, **kwargs)

    assert isinstance(img.data.data, da.Array)
    old_keys = set(pre_attrs.keys())
    # It is OK to have "enhancement_history" added
    new_keys = set(img.data.attrs.keys()) - {"enhancement_history"}
    assert old_keys == new_keys

    np.testing.assert_allclose(img.data.values, expected, atol=1.e-6, rtol=0)
Example #11
0
    def test_jma_true_color_reproduction(self):
        """Test the jma_true_color_reproduction enhancement."""
        from trollimage.xrimage import XRImage

        from satpy.enhancements.ahi import jma_true_color_reproduction

        expected = [[[-109.98, 10.998, 131.976, 252.954, 373.932],
                     [494.91, 615.888, 736.866, 857.844, 978.822]],
                    [[-97.6, 9.76, 117.12, 224.48, 331.84],
                     [439.2, 546.56, 653.92, 761.28, 868.64]],
                    [[-94.27, 9.427, 113.124, 216.821, 320.518],
                     [424.215, 527.912, 631.609, 735.306, 839.003]]]

        img = XRImage(self.rgb)
        jma_true_color_reproduction(img)
        np.testing.assert_almost_equal(img.data.compute(), expected)
Example #12
0
    def _test_enhancement(self, func, data, expected, **kwargs):
        """Perform basic checks that apply to multiple tests."""
        from trollimage.xrimage import XRImage

        pre_attrs = data.attrs
        img = XRImage(data)
        func(img, **kwargs)

        assert isinstance(img.data.data, da.Array)
        assert sorted(pre_attrs.keys()) == sorted(
            img.data.attrs.keys()), "DataArray attributes were not preserved"

        np.testing.assert_allclose(img.data.values,
                                   expected,
                                   atol=1.e-6,
                                   rtol=0)
Example #13
0
def test_write_rgb_classified():
    """Test saving a transparent RGB."""
    area = STEREOGRAPHIC_AREA

    x_size, y_size = 1024, 1024
    arr = np.zeros((3, y_size, x_size))

    attrs = dict([('platform_name', 'NOAA-18'),
                  ('resolution', 1050),
                  ('polarization', None),
                  ('start_time', TIME - datetime.timedelta(minutes=65)),
                  ('end_time', TIME - datetime.timedelta(minutes=60)),
                  ('level', None),
                  ('sensor', 'avhrr-3'),
                  ('ancillary_variables', []),
                  ('area', area),
                  ('wavelength', None),
                  ('optional_datasets', []),
                  ('standard_name', 'overview'),
                  ('name', 'overview'),
                  ('prerequisites', [0.6, 0.8, 10.8]),
                  ('optional_prerequisites', []),
                  ('calibration', None),
                  ('modifiers', None),
                  ('mode', 'P')])

    kwargs = {'compute': True, 'fill_value': None, 'sat_id': 6300014,
              'chan_id': 1700015, 'data_cat': 'PPRN', 'data_source': 'SMHI', 'nbits': 8}

    data1 = da.tile(da.repeat(da.arange(4, chunks=1024), 256), 256).reshape((1, 256, 1024))
    datanan = da.ones((1, 256, 1024), chunks=1024) * 4
    data2 = da.tile(da.repeat(da.arange(4, chunks=1024), 256), 512).reshape((1, 512, 1024))
    data = da.concatenate((data1, datanan, data2), axis=1)
    data = xr.DataArray(data, coords={'bands': ['P']}, dims=['bands', 'y', 'x'], attrs=attrs)

    img = XRImage(data)
    with tempfile.NamedTemporaryFile(delete=DELETE_FILES) as tmpfile:
        filename = tmpfile.name
        if not DELETE_FILES:
            print(filename)
        save(img, filename, data_is_scaled_01=True, **kwargs)
        tif = TiffFile(filename)
        res = tif[0].asarray()
        for idx in range(3):
            np.testing.assert_allclose(res[:, :, idx], np.round(
                np.nan_to_num(arr[idx, :, :]) * 255).astype(np.uint8))
        np.testing.assert_allclose(res[:, :, 3] == 0, np.isnan(arr[0, :, :]))
Example #14
0
def to_image(dataset):
    """Convert ``dataset`` into a :class:`~trollimage.xrimage.XRImage` instance.

    Convert the ``dataset`` into an instance of the
    :class:`~trollimage.xrimage.XRImage` class.  This function makes no other
    changes.  To get an enhanced image, possibly with overlays and decoration,
    see :func:`~get_enhanced_image`.

    Args:
        dataset (xarray.DataArray): Data to be converted to an image.

    Returns:
        Instance of :class:`~trollimage.xrimage.XRImage`.

    """
    dataset = dataset.squeeze()
    if dataset.ndim < 2:
        raise ValueError("Need at least a 2D array to make an image.")
    return XRImage(dataset)
Example #15
0
def add_scale(orig, dc, img, scale):
    """Add scale to an image using the pydecorate package.

    All the features of pydecorate's ``add_scale`` are available.
    See documentation of :doc:`pydecorate:index` for more info.

    """
    LOG.info("Add scale to image.")

    dc.add_scale(**scale)

    arr = da.from_array(np.array(img) / 255.0, chunks=CHUNK_SIZE)

    new_data = xr.DataArray(arr, dims=['y', 'x', 'bands'],
                            coords={'y': orig.data.coords['y'],
                                    'x': orig.data.coords['x'],
                                    'bands': list(img.mode)},
                            attrs=orig.data.attrs)
    return XRImage(new_data)
Example #16
0
def _fake_get_enhanced_image(img, enhance=None, overlay=None, decorate=None):
    from trollimage.xrimage import XRImage
    return XRImage(img)
Example #17
0
    def save_image(self,
                   img: XRImage,
                   filename: Optional[str] = None,
                   compute: bool = True,
                   dtype: Optional[DTypeLike] = None,
                   fill_value: Optional[Union[int, float]] = None,
                   keep_palette: bool = False,
                   cmap: Optional[Colormap] = None,
                   tags: Optional[dict[str, Any]] = None,
                   overviews: Optional[list[int]] = None,
                   overviews_minsize: int = 256,
                   overviews_resampling: Optional[str] = None,
                   include_scale_offset: bool = False,
                   scale_offset_tags: Optional[tuple[str, str]] = None,
                   colormap_tag: Optional[str] = None,
                   driver: Optional[str] = None,
                   tiled: bool = True,
                   **kwargs):
        """Save the image to the given ``filename`` in geotiff_ format.

        Note for faster output and reduced memory usage the ``rasterio``
        library must be installed. This writer currently falls back to
        using ``gdal`` directly, but that will be deprecated in the future.

        Args:
            img (xarray.DataArray): Data to save to geotiff.
            filename (str): Filename to save the image to. Defaults to
                ``filename`` passed during writer creation. Unlike the
                creation ``filename`` keyword argument, this filename does not
                get formatted with data attributes.
            compute (bool): Compute dask arrays and save the image
                immediately. If ``False`` then the return value can be passed
                to :func:`~satpy.writers.compute_writer_results` to do the
                computation. This is useful when multiple images may share
                input calculations where dask can benefit from not repeating
                them multiple times. Defaults to ``True`` in the writer by
                itself, but is typically passed as ``False`` by callers where
                calculations can be combined.
            dtype (DTypeLike): Numpy data type to save the image as.
                Defaults to 8-bit unsigned integer (``np.uint8``) or the data
                type of the data to be saved if ``enhance=False``. If the
                ``dtype`` argument is provided during writer creation then
                that will be used as the default.
            fill_value (float or int): Value to use where data values are
                NaN/null. If this is specified in the writer configuration
                file that value will be used as the default.
            keep_palette (bool): Save palette/color table to geotiff.
                To be used with images that were palettized with the
                "palettize" enhancement. Setting this to ``True`` will cause
                the colormap of the image to be written as a "color table" in
                the output geotiff and the image data values will represent
                the index values in to that color table. By default, this will
                use the colormap used in the "palettize" operation.
                See the ``cmap`` option for other options. This option defaults
                to ``False`` and palettized images will be converted to RGB/A.
            cmap (trollimage.colormap.Colormap or None): Colormap to save
                as a color table in the output geotiff. See ``keep_palette``
                for more information. Defaults to the palette of the provided
                ``img`` object. The colormap's range should be set to match
                the index range of the palette
                (ex. `cmap.set_range(0, len(colors))`).
            tags (dict): Extra metadata to store in geotiff.
            overviews (list): The reduction factors of the overviews to include
                in the image, eg::

                    scn.save_datasets(overviews=[2, 4, 8, 16])

                If provided as an empty list, then levels will be
                computed as powers of two until the last level has less
                pixels than `overviews_minsize`.
                Default is to not add overviews.
            overviews_minsize (int): Minimum number of pixels for the smallest
                overview size generated when `overviews` is auto-generated.
                Defaults to 256.
            overviews_resampling (str): Resampling method
                to use when generating overviews. This must be the name of an
                enum value from :class:`rasterio.enums.Resampling` and
                only takes effect if the `overviews` keyword argument is
                provided. Common values include `nearest` (default),
                `bilinear`, `average`, and many others. See the rasterio
                documentation for more information.
            scale_offset_tags (Tuple[str, str]): If set, include inclusion of
                scale and offset in the GeoTIFF headers in the GDALMetaData
                tag.  The value of this argument should be a keyword argument
                ``(scale_label, offset_label)``, for example, ``("scale",
                "offset")``, indicating the labels to be used.
            colormap_tag (Optional[str]): If set and the image being saved was
                colorized or palettized then a comma-separated version of the
                colormap is saved to a custom geotiff tag with the provided
                name. See :meth:`trollimage.colormap.Colormap.to_csv` for more
                information.
            driver (Optional[str]): Name of GDAL driver to use to save the
                geotiff. If not specified or None (default) the "GTiff" driver
                is used. Another common option is "COG" for Cloud Optimized
                GeoTIFF. See GDAL documentation for more information.
            tiled (bool): For performance this defaults to ``True``.
                Pass ``False`` to created striped TIFF files.
            include_scale_offset (deprecated, bool): Deprecated.
                Use ``scale_offset_tags=("scale", "offset")`` to include scale
                and offset tags.

        .. _geotiff: http://trac.osgeo.org/geotiff/

        """
        filename = filename or self.get_filename(**img.data.attrs)

        gdal_options = self._get_gdal_options(kwargs)
        if fill_value is None:
            # fall back to fill_value from configuration file
            fill_value = self.info.get('fill_value')

        dtype = dtype if dtype is not None else self.dtype
        if dtype is None and self.enhancer is not False:
            dtype = np.uint8
        elif dtype is None:
            dtype = img.data.dtype.type

        if "alpha" in kwargs:
            raise ValueError(
                "Keyword 'alpha' is automatically set based on 'fill_value' "
                "and should not be specified")
        if np.issubdtype(dtype, np.floating):
            if img.mode != "L":
                raise ValueError("Image must be in 'L' mode for floating "
                                 "point geotiff saving")
            if fill_value is None:
                LOG.debug("Alpha band not supported for float geotiffs, "
                          "setting fill value to 'NaN'")
                fill_value = np.nan
        if keep_palette and cmap is None and img.palette is not None:
            from satpy.enhancements import create_colormap
            cmap = create_colormap({'colors': img.palette})
            cmap.set_range(0, len(img.palette) - 1)

        if tags is None:
            tags = {}
        tags.update(self.tags)

        return img.save(filename,
                        fformat='tif',
                        driver=driver,
                        fill_value=fill_value,
                        dtype=dtype,
                        compute=compute,
                        keep_palette=keep_palette,
                        cmap=cmap,
                        tags=tags,
                        include_scale_offset_tags=include_scale_offset,
                        scale_offset_tags=scale_offset_tags,
                        colormap_tag=colormap_tag,
                        overviews=overviews,
                        overviews_resampling=overviews_resampling,
                        overviews_minsize=overviews_minsize,
                        tiled=tiled,
                        **gdal_options)
Example #18
0
def add_overlay(orig, area, coast_dir, color=(0, 0, 0), width=0.5, resolution=None,
                level_coast=1, level_borders=1, fill_value=None):
    """Add coastline and political borders to image.

    Uses ``color`` for feature colors where ``color`` is a 3-element tuple
    of integers between 0 and 255 representing (R, G, B).

    .. warning::

        This function currently loses the data mask (alpha band).

    ``resolution`` is chosen automatically if None (default), otherwise it should be one of:

    +-----+-------------------------+---------+
    | 'f' | Full resolution         | 0.04 km |
    | 'h' | High resolution         | 0.2 km  |
    | 'i' | Intermediate resolution | 1.0 km  |
    | 'l' | Low resolution          | 5.0 km  |
    | 'c' | Crude resolution        | 25  km  |
    +-----+-------------------------+---------+

    """

    if area is None:
        raise ValueError("Area of image is None, can't add overlay.")

    from pycoast import ContourWriterAGG
    if isinstance(area, str):
        area = get_area_def(area)
    LOG.info("Add coastlines and political borders to image.")

    if resolution is None:

        x_resolution = ((area.area_extent[2] -
                         area.area_extent[0]) /
                        area.x_size)
        y_resolution = ((area.area_extent[3] -
                         area.area_extent[1]) /
                        area.y_size)
        res = min(x_resolution, y_resolution)

        if res > 25000:
            resolution = "c"
        elif res > 5000:
            resolution = "l"
        elif res > 1000:
            resolution = "i"
        elif res > 200:
            resolution = "h"
        else:
            resolution = "f"

        LOG.debug("Automagically choose resolution %s", resolution)

    if hasattr(orig, 'convert'):
        # image must be in RGB space to work with pycoast/pydecorate
        orig = orig.convert('RGBA' if orig.mode.endswith('A') else 'RGB')
    elif not orig.mode.startswith('RGB'):
        raise RuntimeError("'trollimage' 1.6+ required to support adding "
                           "overlays/decorations to non-RGB data.")
    img = orig.pil_image(fill_value=fill_value)
    cw_ = ContourWriterAGG(coast_dir)
    cw_.add_coastlines(img, area, outline=color,
                       resolution=resolution, width=width, level=level_coast)
    cw_.add_borders(img, area, outline=color,
                    resolution=resolution, width=width, level=level_borders)

    arr = da.from_array(np.array(img) / 255.0, chunks=CHUNK_SIZE)

    new_data = xr.DataArray(arr, dims=['y', 'x', 'bands'],
                            coords={'y': orig.data.coords['y'],
                                    'x': orig.data.coords['x'],
                                    'bands': list(img.mode)},
                            attrs=orig.data.attrs)
    return XRImage(new_data)
Example #19
0
def _fake_get_enhanced_image(img):
    from trollimage.xrimage import XRImage
    return XRImage(img)
Example #20
0
def add_overlay(orig,
                area,
                coast_dir,
                color=(0, 0, 0),
                width=0.5,
                resolution=None,
                level_coast=1,
                level_borders=1,
                fill_value=None,
                grid=None):
    """Add coastline, political borders and grid(graticules) to image.

    Uses ``color`` for feature colors where ``color`` is a 3-element tuple
    of integers between 0 and 255 representing (R, G, B).

    .. warning::

        This function currently loses the data mask (alpha band).

    ``resolution`` is chosen automatically if None (default),
    otherwise it should be one of:

    +-----+-------------------------+---------+
    | 'f' | Full resolution         | 0.04 km |
    +-----+-------------------------+---------+
    | 'h' | High resolution         | 0.2 km  |
    +-----+-------------------------+---------+
    | 'i' | Intermediate resolution | 1.0 km  |
    +-----+-------------------------+---------+
    | 'l' | Low resolution          | 5.0 km  |
    +-----+-------------------------+---------+
    | 'c' | Crude resolution        | 25  km  |
    +-----+-------------------------+---------+

    ``grid`` is a dictionary with key values as documented in detail in pycoast

    eg. overlay={'grid': {'major_lonlat': (10, 10),
                          'write_text': False,
                          'outline': (224, 224, 224),
                          'width': 0.5}}

    Here major_lonlat is plotted every 10 deg for both longitude and latitude,
    no labels for the grid lines are plotted, the color used for the grid lines
    is light gray, and the width of the gratucules is 0.5 pixels.

    For grid if aggdraw is used, font option is mandatory, if not
    ``write_text`` is set to False::

        font = aggdraw.Font('black', '/usr/share/fonts/truetype/msttcorefonts/Arial.ttf',
                            opacity=127, size=16)

    """

    if area is None:
        raise ValueError("Area of image is None, can't add overlay.")

    from pycoast import ContourWriterAGG
    if isinstance(area, str):
        area = get_area_def(area)
    LOG.info("Add coastlines and political borders to image.")

    if resolution is None:

        x_resolution = ((area.area_extent[2] - area.area_extent[0]) /
                        area.x_size)
        y_resolution = ((area.area_extent[3] - area.area_extent[1]) /
                        area.y_size)
        res = min(x_resolution, y_resolution)

        if res > 25000:
            resolution = "c"
        elif res > 5000:
            resolution = "l"
        elif res > 1000:
            resolution = "i"
        elif res > 200:
            resolution = "h"
        else:
            resolution = "f"

        LOG.debug("Automagically choose resolution %s", resolution)

    if hasattr(orig, 'convert'):
        # image must be in RGB space to work with pycoast/pydecorate
        orig = orig.convert('RGBA' if orig.mode.endswith('A') else 'RGB')
    elif not orig.mode.startswith('RGB'):
        raise RuntimeError("'trollimage' 1.6+ required to support adding "
                           "overlays/decorations to non-RGB data.")
    img = orig.pil_image(fill_value=fill_value)
    cw_ = ContourWriterAGG(coast_dir)
    cw_.add_coastlines(img,
                       area,
                       outline=color,
                       resolution=resolution,
                       width=width,
                       level=level_coast)
    cw_.add_borders(img,
                    area,
                    outline=color,
                    resolution=resolution,
                    width=width,
                    level=level_borders)
    # Only add grid if major_lonlat is given.
    if grid and 'major_lonlat' in grid and grid['major_lonlat']:
        major_lonlat = grid.pop('major_lonlat')
        minor_lonlat = grid.pop('minor_lonlat', major_lonlat)

        cw_.add_grid(img, area, major_lonlat, minor_lonlat, **grid)

    arr = da.from_array(np.array(img) / 255.0, chunks=CHUNK_SIZE)

    new_data = xr.DataArray(arr,
                            dims=['y', 'x', 'bands'],
                            coords={
                                'y': orig.data.coords['y'],
                                'x': orig.data.coords['x'],
                                'bands': list(img.mode)
                            },
                            attrs=orig.data.attrs)
    return XRImage(new_data)
Example #21
0
def to_image(dataset):
    dataset = dataset.squeeze()
    if dataset.ndim < 2:
        raise ValueError("Need at least a 2D array to make an image.")
    else:
        return XRImage(dataset)
Example #22
0
def test_write_rgb_with_a():
    """Test saving a transparent RGB."""
    from pyninjotiff.ninjotiff import save
    from pyninjotiff.tifffile import TiffFile

    area = FakeArea(
        {
            'ellps': 'WGS84',
            'lat_0': '90.0',
            'lat_ts': '60.0',
            'lon_0': '0.0',
            'proj': 'stere'
        }, (-1000000.0, -4500000.0, 2072000.0, -1428000.0), 1024, 1024)

    x_size, y_size = 1024, 1024
    arr = np.zeros((3, y_size, x_size))
    radius = min(x_size, y_size) / 2.0
    centre = x_size / 2, y_size / 2

    for x in range(x_size):
        for y in range(y_size):
            rx = x - centre[0]
            ry = y - centre[1]
            s = ((x - centre[0])**2.0 + (y - centre[1])**2.0)**0.5 / radius
            if s <= 1.0:
                h = ((np.arctan2(ry, rx) / np.pi) + 1.0) / 2.0
                rgb = colorsys.hsv_to_rgb(h, s, 1.0)
                arr[:, y, x] = np.array(rgb)
            else:
                arr[:, y, x] = np.nan

    attrs = dict([('platform_name', 'NOAA-18'), ('resolution', 1050),
                  ('polarization', None),
                  ('start_time', TIME - datetime.timedelta(minutes=55)),
                  ('end_time', TIME - datetime.timedelta(minutes=50)),
                  ('level', None), ('sensor', 'avhrr-3'),
                  ('ancillary_variables', []), ('area', area),
                  ('wavelength', None), ('optional_datasets', []),
                  ('standard_name', 'overview'), ('name', 'overview'),
                  ('prerequisites', [0.6, 0.8, 10.8]),
                  ('optional_prerequisites', []), ('calibration', None),
                  ('modifiers', None), ('mode', 'RGB'),
                  ('enhancement_history', [{
                      'scale': np.array([1, 1, -1]),
                      'offset': np.array([0, 0, 1])
                  }, {
                      'scale':
                      np.array([0.0266347, 0.03559078, 0.01329783]),
                      'offset':
                      np.array([-0.02524969, -0.01996642, 3.8918446])
                  }, {
                      'gamma': 1.6
                  }])])

    kwargs = {
        'compute': True,
        'fill_value': None,
        'sat_id': 6300014,
        'chan_id': 6500015,
        'data_cat': 'PPRN',
        'data_source': 'SMHI',
        'nbits': 8
    }
    data = da.from_array(arr.clip(0, 1), chunks=1024)

    data = xr.DataArray(data,
                        coords={'bands': ['R', 'G', 'B']},
                        dims=['bands', 'y', 'x'],
                        attrs=attrs)
    from trollimage.xrimage import XRImage
    img = XRImage(data)
    with tempfile.NamedTemporaryFile(delete=DELETE_FILES) as tmpfile:
        filename = tmpfile.name
        if not DELETE_FILES:
            print(filename)
        save(img, filename, data_is_scaled_01=True, **kwargs)
        tif = TiffFile(filename)
        res = tif[0].asarray()
        for idx in range(3):
            np.testing.assert_allclose(
                res[:, :, idx],
                np.round(np.nan_to_num(arr[idx, :, :]) * 255).astype(np.uint8))
        np.testing.assert_allclose(res[:, :, 3] == 0, np.isnan(arr[0, :, :]))
Example #23
0
def test_write_rgb_tb():
    """Test saving a non-trasparent RGB with thumbnails."""
    from pyninjotiff.ninjotiff import save
    from pyninjotiff.tifffile import TiffFile

    area = FakeArea(
        {
            'ellps': 'WGS84',
            'lat_0': 90.0,
            'lat_ts': 60.0,
            'lon_0': 0.0,
            'proj': 'stere'
        }, (-1000000.0, -4500000.0, 2072000.0, -1428000.0), 1024, 1024)

    x_size, y_size = 1024, 1024
    arr = np.zeros((3, y_size, x_size))
    radius = min(x_size, y_size) / 2.0
    centre = x_size / 2, y_size / 2

    for x in range(x_size):
        for y in range(y_size):
            rx = x - centre[0]
            ry = y - centre[1]
            s = ((x - centre[0])**2.0 + (y - centre[1])**2.0)**0.5 / radius
            if s <= 1.0:
                h = ((np.arctan2(ry, rx) / np.pi) + 1.0) / 2.0
                rgb = colorsys.hsv_to_rgb(h, s, 1.0)
                arr[:, y, x] = np.array(rgb)

    attrs = dict([('platform_name', 'NOAA-18'), ('resolution', 1050),
                  ('polarization', None), ('level', None),
                  ('sensor', 'avhrr-3'), ('ancillary_variables', []),
                  ('area', area),
                  ('start_time', TIME - datetime.timedelta(minutes=45)),
                  ('end_time', TIME - datetime.timedelta(minutes=40)),
                  ('wavelength', None), ('optional_datasets', []),
                  ('standard_name', 'overview'), ('name', 'overview'),
                  ('prerequisites', [0.6, 0.8, 10.8]),
                  ('optional_prerequisites', []), ('calibration', None),
                  ('modifiers', None), ('mode', 'RGB'),
                  ('enhancement_history', [{
                      'scale': np.array([1, 1, -1]),
                      'offset': np.array([0, 0, 1])
                  }, {
                      'scale':
                      np.array([0.0266347, 0.03559078, 0.01329783]),
                      'offset':
                      np.array([-0.02524969, -0.01996642, 3.8918446])
                  }, {
                      'gamma': 1.6
                  }])])

    kwargs = {
        'compute': True,
        'fill_value': None,
        'sat_id': 6300014,
        'chan_id': 6500015,
        'data_cat': 'PPRN',
        'data_source': 'SMHI',
        'nbits': 8,
        'tile_length': 256,
        'tile_width': 256
    }
    data = da.from_array(arr.clip(0, 1), chunks=1024)
    data = xr.DataArray(data,
                        coords={'bands': ['R', 'G', 'B']},
                        dims=['bands', 'y', 'x'],
                        attrs=attrs)

    from trollimage.xrimage import XRImage
    img = XRImage(data)

    with tempfile.NamedTemporaryFile(delete=DELETE_FILES) as tmpfile:
        filename = tmpfile.name
        if not DELETE_FILES:
            print(filename)
        save(img, filename, data_is_scaled_01=False, **kwargs)
        tif = TiffFile(filename)
        res = tif[0].asarray()
        assert (tif.pages[0].tags['tile_length'].value == 256)
        assert (tif.pages[1].tags['tile_length'].value == 128)
        assert (tif.pages[0].tags['tile_width'].value == 256)
        assert (tif.pages[1].tags['tile_width'].value == 128)
        assert (len(tif.pages) == 2)
        assert (tif.pages[0].shape == (1024, 1024, 4))
        assert (tif.pages[1].shape == (512, 512, 4))
        for idx in range(3):
            np.testing.assert_allclose(
                res[:, :, idx],
                np.round(arr[idx, :, :] * 255).astype(np.uint8))

        tags = {
            'new_subfile_type':
            0,
            'image_width':
            1024,
            'image_length':
            1024,
            'bits_per_sample': (8, 8, 8, 8),
            'compression':
            32946,
            'photometric':
            2,
            'orientation':
            1,
            'samples_per_pixel':
            4,
            'planar_configuration':
            1,
            'software':
            b'tifffile/pytroll',
            'datetime':
            b'2020:01:17 14:17:23',
            'tile_width':
            256,
            'tile_length':
            256,
            'tile_offsets':
            (951, 24414, 77352, 126135, 141546, 206260, 272951, 318709, 349650,
             413166, 475735, 519168, 547960, 570326, 615924, 666705),
            'tile_byte_counts':
            (23463, 52938, 48783, 15411, 64714, 66691, 45758, 30941, 63516,
             62569, 43433, 28792, 22366, 45598, 50781, 13371),
            'extra_samples':
            2,
            'sample_format': (1, 1, 1, 1),
            'model_pixel_scale':
            (0.026949458523585643, 0.027040118922685666, 0.0),
            'model_tie_point':
            (0.0, 0.0, 0.0, -35.00279008179894, 73.3850622630575, 0.0),
            '40000':
            b'NINJO',
            '40001':
            6300014,
            '40002':
            1579264321,
            '40003':
            1579267043,
            '40004':
            6500015,
            '40005':
            2,
            '40006':
            b'/tmp/tmpb4kn93qt',
            '40007':
            b'PPRN',
            '40008':
            b'',
            '40009':
            24,
            '40010':
            b'SMHI',
            '40011':
            1,
            '40012':
            1024,
            '40013':
            1,
            '40014':
            1024,
            '40015':
            b'NPOL',
            '40016':
            -35.00278854370117,
            '40017':
            24.72344398498535,
            '40018':
            6378137.0,
            '40019':
            6356752.5,
            '40021':
            60.0,
            '40022':
            0.0,
            '40023':
            0.0,
            '40024':
            b'None',
            '40025':
            b'None',
            '40026':
            0,
            '40027':
            255,
            '40028':
            1.0,
            '40029':
            0.0,
            '40040':
            0,
            '40041':
            0,
            '40042':
            1,
            '40043':
            0,
            '50000':
            0,
            'fill_order':
            1,
            'rows_per_strip':
            4294967295,
            'resolution_unit':
            2,
            'predictor':
            1,
            'ycbcr_subsampling':
            1,
            'ycbcr_positioning':
            1
        }
        read_tags = tif.pages[0].tags
        assert (read_tags.keys() == tags.keys())
        for key, val in tags.items():
            if key in ['datetime', '40002', '40003', '40006']:
                continue
            assert (val == read_tags[key].value)