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
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
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
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
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
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