def command(paths):
    """ Show a lowest elevation image. """
    # order
    index = dict(
        NL60=0,
        NL61=1,
        nhb=2,
        ess=3,
        emd=4,
        JAB=5,
    )

    # load
    d_rang, d_elev, d_anth = {}, {}, {}
    for p in paths:
        with h5py.File(p, 'r') as h5:
            for r in h5:
                if r in d_rang or r == 'ase':
                    continue
                d_rang[r] = h5[r]['range'][:]
                d_elev[r] = h5[r]['elevation'][:]
                d_anth[r] = h5[r].attrs['antenna_height']
    radars = d_anth.keys()
    elev = np.ma.empty((len(radars), ) + d_rang[radars[0]].shape)
    rang = np.ma.empty((len(radars), ) + d_rang[radars[0]].shape)
    anth = np.empty((len(radars), 1, 1))
    for r in radars:
        elev[index[r]] = np.ma.masked_equal(d_elev[r], config.NODATAVALUE)
        rang[index[r]] = np.ma.masked_equal(d_rang[r], config.NODATAVALUE)
        anth[index[r]] = float(d_anth[r]) / 1000

    # calculate
    theta = calc.calculate_theta(
        rang=rang,
        elev=np.radians(elev),
        anth=anth,
    )
    alt = calc.calculate_height(
        theta=theta,
        elev=np.radians(elev),
        anth=anth,
    )
    which = np.ma.where(
        alt == alt.min(0),
        np.indices(alt.shape)[0],
        -1,
    ).max(0)
    what = alt.min(0)

    # colors
    hue = cm.hsv(colors.Normalize(vmax=len(radars))(which), bytes=True)
    sat = 1 - colors.Normalize(vmax=5, clip=True)(what)[..., np.newaxis]

    hue[..., :3] *= sat
    hue[sat.mask[..., 0]] = 255
    Image.fromarray(hue).save('elevation_image.png')
    return 0
Beispiel #2
0
def command(paths):
    """ Show a lowest elevation image. """
    # order
    index = dict(
        NL60=0,
        NL61=1,
        nhb=2,
        ess=3,
        emd=4,
        JAB=5,
    )

    # load
    d_rang, d_elev, d_anth = {}, {}, {}
    for p in paths:
        with h5py.File(p, 'r') as h5:
            for r in h5:
                if r in d_rang or r == 'ase':
                    continue
                d_rang[r] = h5[r]['range'][:]
                d_elev[r] = h5[r]['elevation'][:]
                d_anth[r] = h5[r].attrs['antenna_height']
    radars = d_anth.keys()
    elev = np.ma.empty((len(radars),) + d_rang[radars[0]].shape)
    rang = np.ma.empty((len(radars),) + d_rang[radars[0]].shape)
    anth = np.empty((len(radars), 1, 1))
    for r in radars:
        elev[index[r]] = np.ma.masked_equal(d_elev[r], config.NODATAVALUE)
        rang[index[r]] = np.ma.masked_equal(d_rang[r], config.NODATAVALUE)
        anth[index[r]] = float(d_anth[r]) / 1000

    # calculate
    theta = calc.calculate_theta(
        rang=rang, elev=np.radians(elev), anth=anth,
    )
    alt = calc.calculate_height(
        theta=theta, elev=np.radians(elev), anth=anth,
    )
    which = np.ma.where(
        alt == alt.min(0),
        np.indices(alt.shape)[0],
        -1,
    ).max(0)
    what = alt.min(0)

    # colors
    hue = cm.hsv(colors.Normalize(vmax=len(radars))(which), bytes=True)
    sat = 1 - colors.Normalize(vmax=5, clip=True)(what)[..., np.newaxis]

    hue[..., :3] *= sat
    hue[sat.mask[..., 0]] = 255
    Image.fromarray(hue).save('elevation_image.png')
    return 0
Beispiel #3
0
    def _calculate(self, datasets):
        """
        Return a composite dataset based on the
        weighted lowest altitudes method.
        """
        if not datasets:
            return np.ma.array(
                np.zeros(self.grid.get_shape()),
                mask=True,
                fill_value=config.NODATAVALUE,
            )

        # Read datasets
        metadata = [dict(ds.attrs) for ds in datasets]

        stations = [m['station'] for m in metadata]

        anth = np.array(
            [json.loads(m['antenna_height']) for m in metadata],
        ).reshape((-1, 1, 1)) / 1000

        rain = np.ma.array(
            [gridtools.h5ds2ma(ds['rain']) for ds in datasets],
            fill_value=config.NODATAVALUE,
        )

        rang = np.ma.array(
            [gridtools.h5ds2ma(ds['range']) for ds in datasets],
            fill_value=config.NODATAVALUE,
        )

        elev = np.ma.array(
            [gridtools.h5ds2ma(ds['elevation']) for ds in datasets],
            fill_value=config.NODATAVALUE,
        )

        # Extend mask around NL stations
        if 'NL61' in stations and 'NL62' in stations:
            for i in map(stations.index, ['NL61', 'NL62']):
                rain.mask[i][np.less(rang[i], 15)] = True

        # Extend mask around JAB
        if 'JAB' in stations and 'NL62' in stations:
            i = stations.index('JAB')
            rain.mask[i][np.less(rang[i], 15)] = True

        # Do history declutter
        if self.declutter['history']:  # None or 0 disables history declutter
            logging.debug('Starting history declutter, threshold {}'.format(
                self.declutter['history']
            ))

            # Initialize clutter array of same dimensions as rain array
            clutter = np.zeros(rain.shape, rain.dtype)
            declutter_mask = h5py.File(
                os.path.join(config.MISC_DIR, config.DECLUTTER_FILEPATH), 'r',
            )
            left, right, upper, lower = self._get_window(self.grid)
            for i, radar in enumerate(stations):
                if radar in declutter_mask:
                    clutter[i, upper:lower, left:right] = declutter_mask[radar]
            declutter_mask.close()

            while True:
                clutter[rain.mask] = 0
                extra = reduce(np.logical_and, [
                    # clutter above threshold for that pixel
                    np.greater(clutter, self.declutter['history']),
                    # at least two unmasked pixels left
                    np.greater((~rain.mask).sum(0), 1),
                    # the maximum clutter must be masked
                    np.equal(clutter, clutter.max(0)),
                ])
                # Extend rain mask with cluttermask.
                count = extra.sum()
                if not count:
                    break
                logging.debug(
                    'Masking {} historically suspicious pixels'.format(count),
                )
                rain.mask[extra] = True

        rang.mask = rain.mask
        elev.mask = rain.mask

        # Calculate heights
        theta = calc.calculate_theta(
            rang=rang, elev=np.radians(elev), anth=anth,
        )

        alt_min = calc.calculate_height(
            theta=theta, elev=np.radians(elev - 0.5), anth=anth,
        )
        alt_max = calc.calculate_height(
            theta=theta, elev=np.radians(elev + 0.5), anth=anth,
        )

        lowest_alt_max = np.ma.where(
            np.equal(alt_min, alt_min.min(0)),
            alt_max,
            np.ma.masked,
        ).min(0)

        bandwidth = alt_max - alt_min

        vertdist1 = (lowest_alt_max - alt_min)
        vertdist1[np.ma.less(vertdist1, 0)] = 0

        vertdist2 = (lowest_alt_max - alt_max)
        vertdist2[np.ma.less(vertdist2, 0)] = 0

        overlap = vertdist1 - vertdist2

        weight = overlap / bandwidth

        composite = (rain * weight).sum(0) / weight.sum(0)
        composite.fill_value = config.NODATAVALUE

        return composite
Beispiel #4
0
    def get(self):
        """ Return gdal dataset in rd projection. """
        data = self.data()
        rang, azim, elev = data['polar']
        rain = data['rain']
        latlon = data['latlon']
        anth = data['ant_alt']

        theta = calc.calculate_theta(
            rang=rang,
            elev=np.radians(elev),
            anth=anth / 1000,
        )

        points_aeqd = calc.calculate_cartesian(
            theta=theta,
            azim=np.radians(azim),
        )

        projections = (
            utils.projection_aeqd(*latlon),
            utils.projection(utils.RD),
        )

        points_rd = utils.coordinate_transformer.transform(
            points=points_aeqd,
            projections=projections,
        )

        # Interpolate
        grid_rain, grid_rang, grid_elev = self._interpolate(
            points=points_rd,
            values=(
                rain,
                rang * np.ones(rain.shape),
                elev * np.ones(rain.shape),
            ),
            grid=self.grid.get_grid(),
        )

        location = utils.transform((0, 0), projections)

        ds_rd = self.grid.create_dataset(bands=3)
        ds_rd.SetMetadata(dict(
            source=self.signature.get_scanname(),
            timestamp=self.signature.get_timestamp(),
            station=self.signature.get_code(),
            location=json.dumps(location),
            antenna_height=json.dumps(anth),
            max_elevation=json.dumps(elev.max()),
            min_elevation=json.dumps(elev.min()),
            max_range=json.dumps(rang.max()),
        ))

        banddata = {
            BAND_RAIN: grid_rain,
            BAND_RANG: grid_rang,
            BAND_ELEV: grid_elev,
        }

        for i in range(1, ds_rd.RasterCount + 1):
            band = ds_rd.GetRasterBand(i)
            band.SetNoDataValue(banddata[i].fill_value)
            band.WriteArray(banddata[i].filled())
            band.SetMetadata(BAND_META[i])

        return ds_rd
Beispiel #5
0
    def _calculate(self, datasets):
        """
        Return a composite dataset based on the
        weighted lowest altitudes method.
        """
        if not datasets:
            return np.ma.array(
                np.zeros(self.grid.get_shape()),
                mask=True,
                fill_value=config.NODATAVALUE,
            )

        # Read datasets
        metadata = [dict(ds.attrs) for ds in datasets]

        stations = [m['station'] for m in metadata]

        anth = np.array([json.loads(m['antenna_height'])
                         for m in metadata], ).reshape((-1, 1, 1)) / 1000

        rain = np.ma.array(
            [gridtools.h5ds2ma(ds['rain']) for ds in datasets],
            fill_value=config.NODATAVALUE,
        )

        rang = np.ma.array(
            [gridtools.h5ds2ma(ds['range']) for ds in datasets],
            fill_value=config.NODATAVALUE,
        )

        elev = np.ma.array(
            [gridtools.h5ds2ma(ds['elevation']) for ds in datasets],
            fill_value=config.NODATAVALUE,
        )

        # Extend mask around NL stations
        if 'NL60' in stations and 'NL61' in stations:
            for i in map(stations.index, ['NL60', 'NL61']):
                rain.mask[i][np.less(rang[i], 15)] = True

        # Extend mask around JAB
        if 'JAB' in stations and 'NL60' in stations:
            i = stations.index('JAB')
            rain.mask[i][np.less(rang[i], 15)] = True

        # Do history declutter
        if self.declutter['history']:  # None or 0 disables history declutter
            logging.debug('Starting history declutter, threshold {}'.format(
                self.declutter['history']))

            # Initialize clutter array of same dimensions as rain array
            clutter = np.zeros(rain.shape, rain.dtype)
            declutter_mask = h5py.File(
                os.path.join(config.MISC_DIR, config.DECLUTTER_FILEPATH),
                'r',
            )
            left, right, upper, lower = self._get_window(self.grid)
            for i, radar in enumerate(stations):
                if radar in declutter_mask:
                    clutter[i, upper:lower, left:right] = declutter_mask[radar]
            declutter_mask.close()

            while True:
                clutter[rain.mask] = 0
                extra = reduce(
                    np.logical_and,
                    [
                        # clutter above threshold for that pixel
                        np.greater(clutter, self.declutter['history']),
                        # at least two unmasked pixels left
                        np.greater((~rain.mask).sum(0), 1),
                        # the maximum clutter must be masked
                        np.equal(clutter, clutter.max(0)),
                    ])
                # Extend rain mask with cluttermask.
                count = extra.sum()
                if not count:
                    break
                logging.debug(
                    'Masking {} historically suspicious pixels'.format(count),
                )
                rain.mask[extra] = True

        rang.mask = rain.mask
        elev.mask = rain.mask

        # Calculate heights
        theta = calc.calculate_theta(
            rang=rang,
            elev=np.radians(elev),
            anth=anth,
        )

        alt_min = calc.calculate_height(
            theta=theta,
            elev=np.radians(elev - 0.5),
            anth=anth,
        )
        alt_max = calc.calculate_height(
            theta=theta,
            elev=np.radians(elev + 0.5),
            anth=anth,
        )

        lowest_alt_max = np.ma.where(
            np.equal(alt_min, alt_min.min(0)),
            alt_max,
            np.ma.masked,
        ).min(0)

        bandwidth = alt_max - alt_min

        vertdist1 = (lowest_alt_max - alt_min)
        vertdist1[np.ma.less(vertdist1, 0)] = 0

        vertdist2 = (lowest_alt_max - alt_max)
        vertdist2[np.ma.less(vertdist2, 0)] = 0

        overlap = vertdist1 - vertdist2

        weight = overlap / bandwidth

        composite = (rain * weight).sum(0) / weight.sum(0)
        composite.fill_value = config.NODATAVALUE

        return composite
Beispiel #6
0
    def get(self):
        """ Return gdal dataset in rd projection. """
        data = self.data()
        rang, azim, elev = data['polar']
        rain = data['rain']
        latlon = data['latlon']
        anth = data['ant_alt']

        theta = calc.calculate_theta(
            rang=rang,
            elev=np.radians(elev),
            anth=anth / 1000,
        )

        points_aeqd = calc.calculate_cartesian(
            theta=theta,
            azim=np.radians(azim),
        )

        projections = (
            utils.projection_aeqd(*latlon),
            utils.projection(utils.RD),
        )

        points_rd = utils.coordinate_transformer.transform(
            points=points_aeqd,
            projections=projections,
        )

        # Interpolate
        grid_rain, grid_rang, grid_elev = self._interpolate(
            points=points_rd,
            values=(
                rain,
                rang * np.ones(rain.shape),
                elev * np.ones(rain.shape),
            ),
            grid=self.grid.get_grid(),
        )

        location = utils.transform((0, 0), projections)

        ds_rd = self.grid.create_dataset(bands=3)
        ds_rd.SetMetadata(
            dict(
                source=self.signature.get_scanname(),
                timestamp=self.signature.get_timestamp(),
                station=self.signature.get_code(),
                location=json.dumps(location),
                antenna_height=json.dumps(anth),
                max_elevation=json.dumps(elev.max()),
                min_elevation=json.dumps(elev.min()),
                max_range=json.dumps(rang.max()),
            ))

        banddata = {
            BAND_RAIN: grid_rain,
            BAND_RANG: grid_rang,
            BAND_ELEV: grid_elev,
        }

        for i in range(1, ds_rd.RasterCount + 1):
            band = ds_rd.GetRasterBand(i)
            band.SetNoDataValue(banddata[i].fill_value)
            band.WriteArray(banddata[i].filled())
            band.SetMetadata(BAND_META[i])

        return ds_rd