def __call__(self, projectables, **kwargs): """Generate the composite.""" projectables = self.match_data_arrays(projectables) day_data = projectables[0] night_data = projectables[1] lim_low = np.cos(np.deg2rad(self.lim_low)) lim_high = np.cos(np.deg2rad(self.lim_high)) try: coszen = np.cos(np.deg2rad(projectables[2])) except IndexError: from pyorbital.astronomy import cos_zen LOG.debug("Computing sun zenith angles.") # Get chunking that matches the data try: chunks = day_data.sel(bands=day_data['bands'][0]).chunks except KeyError: chunks = day_data.chunks lons, lats = day_data.attrs["area"].get_lonlats(chunks=chunks) coszen = xr.DataArray(cos_zen(day_data.attrs["start_time"], lons, lats), dims=['y', 'x'], coords=[day_data['y'], day_data['x']]) # Calculate blending weights coszen -= np.min((lim_high, lim_low)) coszen /= np.abs(lim_low - lim_high) coszen = coszen.clip(0, 1) # Apply enhancements to get images day_data = enhance2dataset(day_data) night_data = enhance2dataset(night_data) # Adjust bands so that they match # L/RGB -> RGB/RGB # LA/RGB -> RGBA/RGBA # RGB/RGBA -> RGBA/RGBA day_data = add_bands(day_data, night_data['bands']) night_data = add_bands(night_data, day_data['bands']) # Replace missing channel data with zeros day_data = zero_missing_data(day_data, night_data) night_data = zero_missing_data(night_data, day_data) # Get merged metadata attrs = combine_metadata(day_data, night_data) # Blend the two images together data = (1 - coszen) * night_data + coszen * day_data data.attrs = attrs # Split to separate bands so the mode is correct data = [data.sel(bands=b) for b in data['bands']] return super(DayNightCompositor, self).__call__(data, **kwargs)
def sunzen_corr(self, time_slot, lonlats=None, limit=80., mode='cos'): '''Perform Sun zenith angle correction for the channel at *time_slot* (datetime.datetime() object) and return the corrected channel. The parameter *limit* can be used to set the maximum zenith angle for which the correction is calculated. For larger angles, the correction is the same as at the *limit* (default: 80.0 degrees). Coordinate values can be given as a 2-tuple or a two-element list *lonlats* of numpy arrays; if None, the coordinates will be read from the channel data. Parameter *mode* is a placeholder for other possible illumination corrections. The name of the new channel will be *original_chan.name+'_SZC'*, eg. "VIS006_SZC". This name is also stored to the info dictionary of the originating channel. ''' import mpop.tools try: from pyorbital import astronomy except ImportError: LOG.warning("Could not load pyorbital.astronomy") return None if lonlats is None or len(lonlats) != 2: # Read coordinates LOG.debug("No valid coordinates given, reading from the " "channel data") lons, lats = self.area.get_lonlats() else: lons, lats = lonlats # Calculate Sun zenith angles and the cosine cos_zen = astronomy.cos_zen(time_slot, lons, lats) # Copy the channel new_ch = copy.deepcopy(self) # Set the name new_ch.name += '_SZC' if mode == 'cos': new_ch.data = mpop.tools.sunzen_corr_cos(new_ch.data, cos_zen, limit=limit) else: # Placeholder for other correction methods pass # Add information about the corrected version to original # channel self.info["sun_zen_corrected"] = self.name + '_SZC' return new_ch
def __call__(self, projectables, **kwargs): day_data = projectables[0] night_data = projectables[1] lim_low = np.cos(np.deg2rad(self.lim_low)) lim_high = np.cos(np.deg2rad(self.lim_high)) try: coszen = xu.cos(xu.deg2rad(projectables[2])) except IndexError: from pyorbital.astronomy import cos_zen LOG.debug("Computing sun zenith angles.") # Get chunking that matches the data try: chunks = day_data.sel(bands=day_data['bands'][0]).chunks except KeyError: chunks = day_data.chunks lons, lats = day_data.attrs["area"].get_lonlats_dask(chunks) coszen = xr.DataArray(cos_zen(day_data.attrs["start_time"], lons, lats), dims=['y', 'x'], coords=[day_data['y'], day_data['x']]) # Calculate blending weights coszen -= np.min((lim_high, lim_low)) coszen /= np.abs(lim_low - lim_high) coszen = coszen.clip(0, 1) # Apply enhancements to get images day_data = enhance2dataset(day_data) night_data = enhance2dataset(night_data) # Adjust bands so that they match # L/RGB -> RGB/RGB # LA/RGB -> RGBA/RGBA # RGB/RGBA -> RGBA/RGBA day_data = add_bands(day_data, night_data['bands']) night_data = add_bands(night_data, day_data['bands']) # Get merged metadata attrs = combine_metadata(day_data, night_data) # Blend the two images together data = (1 - coszen) * night_data + coszen * day_data data.attrs = attrs # Split to separate bands so the mode is correct data = [data.sel(bands=b) for b in data['bands']] res = super(DayNightCompositor, self).__call__(data, **kwargs) return res
def __call__(self, projectables, **info): vis = projectables[0] if vis.info.get("sunz_corrected"): LOG.debug("Sun zen correction already applied") return vis if hasattr(vis.info["area"], 'name'): area_name = vis.info["area"].name else: area_name = 'swath' + str(vis.shape) key = (vis.info["start_time"], area_name) tic = time.time() LOG.debug("Applying sun zen correction") if len(projectables) == 1: if key not in self.coszen: from pyorbital.astronomy import cos_zen LOG.debug("Computing sun zenith angles.") self.coszen[key] = np.ma.masked_outside( cos_zen(vis.info["start_time"], *vis.info["area"].get_lonlats()), # about 88 degrees. 0.035, 1, copy=False) coszen = self.coszen[key] else: coszen = np.cos(np.deg2rad(projectables[1])) if vis.shape != coszen.shape: # assume we were given lower resolution szen data than band data LOG.debug( "Interpolating coszen calculations for higher resolution band") factor = int(vis.shape[1] / coszen.shape[1]) coszen = np.repeat(np.repeat(coszen, factor, axis=0), factor, axis=1) # sunz correction will be in place so we need a copy proj = vis.copy() proj = self._apply_correction(proj, coszen) vis.mask[coszen < 0] = True self.apply_modifier_info(vis, proj) LOG.debug( "Sun-zenith correction applied. Computation time: %5.1f (sec)", time.time() - tic) return proj
def __call__(self, projectables, **info): """Generate the composite.""" projectables = self.match_data_arrays( list(projectables) + list(info.get('optional_datasets', []))) vis = projectables[0] if vis.attrs.get("sunz_corrected"): logger.debug("Sun zen correction already applied") return vis area_name = hash(vis.attrs['area']) key = (vis.attrs["start_time"], area_name) tic = time.time() logger.debug("Applying sun zen correction") coszen = self.coszen.get(key) if coszen is None and not info.get('optional_datasets'): # we were not given SZA, generate SZA then calculate cos(SZA) from pyorbital.astronomy import cos_zen logger.debug("Computing sun zenith angles.") lons, lats = vis.attrs["area"].get_lonlats(chunks=vis.data.chunks) coords = {} if 'y' in vis.coords and 'x' in vis.coords: coords['y'] = vis['y'] coords['x'] = vis['x'] coszen = xr.DataArray(cos_zen(vis.attrs["start_time"], lons, lats), dims=['y', 'x'], coords=coords) if self.max_sza is not None: coszen = coszen.where(coszen >= self.max_sza_cos) self.coszen[key] = coszen elif coszen is None: # we were given the SZA, calculate the cos(SZA) coszen = np.cos(np.deg2rad(projectables[1])) self.coszen[key] = coszen proj = self._apply_correction(vis, coszen) proj.attrs = vis.attrs.copy() self.apply_modifier_info(vis, proj) logger.debug( "Sun-zenith correction applied. Computation time: %5.1f (sec)", time.time() - tic) return proj
def __call__(self, projectables, **info): vis = projectables[0] if vis.info.get("sunz_corrected"): LOG.debug("Sun zen correction already applied") return vis if hasattr(vis.info["area"], 'name'): area_name = vis.info["area"].name else: area_name = 'swath' + str(vis.info["area"].lons.shape) key = (vis.info["start_time"], area_name) LOG.debug("Applying sun zen correction") if len(projectables) == 1: if key not in self.coszen: from pyorbital.astronomy import cos_zen LOG.debug("Computing sun zenith angles.") self.coszen[key] = np.ma.masked_outside(cos_zen(vis.info["start_time"], *vis.info["area"].get_lonlats()), # about 88 degrees. 0.035, 1, copy=False) coszen = self.coszen[key] else: coszen = np.cos(np.deg2rad(projectables[1])) if vis.shape != coszen.shape: # assume we were given lower resolution szen data than band data LOG.debug( "Interpolating coszen calculations for higher resolution band") factor = int(vis.shape[1] / coszen.shape[1]) coszen = np.repeat( np.repeat(coszen, factor, axis=0), factor, axis=1) # sunz correction will be in place so we need a copy proj = vis.copy() proj = sunzen_corr_cos(proj, coszen) vis.mask[coszen < 0] = True self.apply_modifier_info(vis, proj) return proj
def __call__(self, projectables, **info): projectables = self.check_areas(projectables) vis = projectables[0] if vis.attrs.get("sunz_corrected"): LOG.debug("Sun zen correction already applied") return vis if hasattr(vis.attrs["area"], 'name'): area_name = vis.attrs["area"].name else: area_name = 'swath' + str(vis.shape) key = (vis.attrs["start_time"], area_name) tic = time.time() LOG.debug("Applying sun zen correction") if len(projectables) == 1: coszen = self.coszen.get(key) if coszen is None: from pyorbital.astronomy import cos_zen LOG.debug("Computing sun zenith angles.") lons, lats = vis.attrs["area"].get_lonlats_dask(CHUNK_SIZE) coszen = xr.DataArray(cos_zen(vis.attrs["start_time"], lons, lats), dims=['y', 'x'], coords=[vis['y'], vis['x']]) coszen = coszen.where((coszen > 0.035) & (coszen < 1)) self.coszen[key] = coszen else: coszen = xu.cos(xu.deg2rad(projectables[1])) self.coszen[key] = coszen proj = self._apply_correction(vis, coszen) proj.attrs = vis.attrs.copy() self.apply_modifier_info(vis, proj) LOG.debug( "Sun-zenith correction applied. Computation time: %5.1f (sec)", time.time() - tic) return proj
def sunzen_corr(self, time_slot, lonlats=None, limit=80., mode='cos', sunmask=False): '''Perform Sun zenith angle correction for the channel at *time_slot* (datetime.datetime() object) and return the corrected channel. The parameter *limit* can be used to set the maximum zenith angle for which the correction is calculated. For larger angles, the correction is the same as at the *limit* (default: 80.0 degrees). Coordinate values can be given as a 2-tuple or a two-element list *lonlats* of numpy arrays; if None, the coordinates will be read from the channel data. Parameter *mode* is a placeholder for other possible illumination corrections. The name of the new channel will be *original_chan.name+'_SZC'*, eg. "VIS006_SZC". This name is also stored to the info dictionary of the originating channel. ''' if self.info.get('sun_zen_correction_applied'): LOG.debug("Sun zenith correction already applied, skipping") return self import mpop.tools try: from pyorbital import astronomy except ImportError: LOG.warning("Could not load pyorbital.astronomy") return None if lonlats is None or len(lonlats) != 2: # Read coordinates LOG.debug("No valid coordinates given, reading from the " "channel data") lons, lats = self.area.get_lonlats() else: lons, lats = lonlats # Calculate Sun zenith angles and the cosine cos_zen = astronomy.cos_zen(time_slot, lons, lats) # Copy the channel new_ch = copy.deepcopy(self) # Set the name new_ch.name += '_SZC' if mode == 'cos': new_ch.data = mpop.tools.sunzen_corr_cos(new_ch.data, cos_zen, limit=limit) else: # Placeholder for other correction methods pass # Add information about the corrected version to original # channel self.info["sun_zen_corrected"] = self.name + '_SZC' if sunmask: if isinstance(sunmask, (float, int)): sunmask = sunmask else: sunmask = 90. cos_limit = np.cos(np.radians(sunmask)) LOG.debug( "Masking out data where sun-zenith " + "is greater than %f deg", sunmask) LOG.debug("cos_limit = %f", cos_limit) # Mask out data where the sun elevation is below a threshold: new_ch.data = np.ma.masked_where(cos_zen < cos_limit, new_ch.data, copy=False) new_ch.info["sun_zen_correction_applied"] = True return new_ch
def sunzen_corr(self, time_slot, lonlats=None, limit=80., mode='cos', sunmask=False): '''Perform Sun zenith angle correction for the channel at *time_slot* (datetime.datetime() object) and return the corrected channel. The parameter *limit* can be used to set the maximum zenith angle for which the correction is calculated. For larger angles, the correction is the same as at the *limit* (default: 80.0 degrees). Coordinate values can be given as a 2-tuple or a two-element list *lonlats* of numpy arrays; if None, the coordinates will be read from the channel data. Parameter *mode* is a placeholder for other possible illumination corrections. The name of the new channel will be *original_chan.name+'_SZC'*, eg. "VIS006_SZC". This name is also stored to the info dictionary of the originating channel. ''' if self.info.get('sun_zen_correction_applied'): LOG.debug("Sun zenith correction already applied, skipping") return self import mpop.tools try: from pyorbital import astronomy except ImportError: LOG.warning("Could not load pyorbital.astronomy") return None if lonlats is None or len(lonlats) != 2: # Read coordinates LOG.debug("No valid coordinates given, reading from the " "channel data") lons, lats = self.area.get_lonlats() else: lons, lats = lonlats # Calculate Sun zenith angles and the cosine cos_zen = astronomy.cos_zen(time_slot, lons, lats) # Copy the channel new_ch = copy.deepcopy(self) # Set the name new_ch.name += '_SZC' if mode == 'cos': new_ch.data = mpop.tools.sunzen_corr_cos(new_ch.data, cos_zen, limit=limit) else: # Placeholder for other correction methods pass # Add information about the corrected version to original # channel self.info["sun_zen_corrected"] = self.name + '_SZC' if sunmask: if isinstance(sunmask, (float, int)): sunmask = sunmask else: sunmask = 90. cos_limit = np.cos(np.radians(sunmask)) LOG.debug("Masking out data where sun-zenith " + "is greater than %f deg", sunmask) LOG.debug("cos_limit = %f", cos_limit) # Mask out data where the sun elevation is below a threshold: new_ch.data = np.ma.masked_where( cos_zen < cos_limit, new_ch.data, copy=False) new_ch.info["sun_zen_correction_applied"] = True return new_ch
def combine(p1, p2, area_of_interest): """Combine passes together. """ try: return combination[p1, p2] except KeyError: pass area = area_of_interest.poly.area() def pscore(poly, coeff=1): if poly is None: return 0 else: return poly.area() * coeff twi1 = get_twilight_poly(p1.uptime) twi2 = get_twilight_poly(p2.uptime) ip1, sip1 = p1.score.get(area_of_interest, (None, None)) if sip1 is None: ip1 = p1.boundary.contour_poly.intersection(area_of_interest.poly) # FIXME: ip1 or ip2 could be None if the pass is entirely inside the # area (or vice versa) if ip1 is None: return 0 ip1d = ip1.intersection(twi1) if ip1d is None: lon, lat = np.rad2deg(ip1.vertices[0, :]) theta = astronomy.cos_zen(p1.uptime, lon, lat) if np.sign(theta) > 0: ip1d = ip1 ip1n = None else: ip1n = ip1 else: twi1.invert() ip1n = ip1.intersection(twi1) twi1.invert() ns1 = pscore(ip1n, p1.satellite.score.night / area) ds1 = pscore(ip1d, p1.satellite.score.day / area) sip1 = ns1 + ds1 p1.score[area_of_interest] = (ip1, sip1) ip2, sip2 = p2.score.get(area_of_interest, (None, None)) if sip2 is None: ip2 = p2.boundary.contour_poly.intersection(area_of_interest.poly) if ip2 is None: return 0 ip2d = ip2.intersection(twi2) if ip2d is None: lon, lat = np.rad2deg(ip2.vertices[0, :]) theta = astronomy.cos_zen(p2.uptime, lon, lat) if np.sign(theta) > 0: ip2d = ip2 ip2n = None else: ip2n = ip2 else: twi2.invert() ip2n = ip2.intersection(twi2) twi2.invert() ns2 = pscore(ip2n, p2.satellite.score.night / area) ds2 = pscore(ip2d, p2.satellite.score.day / area) sip2 = ns2 + ds2 p2.score[area_of_interest] = (ip2, sip2) ip1p2 = ip1.intersection(ip2) if ip1p2 is None: sip1p2 = 0 else: ip1p2da = ip1p2.intersection(twi1) twi1.invert() ip1p2na = ip1p2.intersection(twi1) twi1.invert() ip1p2db = ip1p2.intersection(twi2) twi2.invert() ip1p2nb = ip1p2.intersection(twi2) twi2.invert() ns12a = pscore(ip1p2na, p1.satellite.score.night / area) ds12a = pscore(ip1p2da, p1.satellite.score.day / area) ns12b = pscore(ip1p2nb, p2.satellite.score.night / area) ds12b = pscore(ip1p2db, p2.satellite.score.day / area) sip1p2a = ns12a + ds12a sip1p2b = ns12b + ds12b sip1p2 = (sip1p2a + sip1p2b) / 2.0 if p2 > p1: tdiff = (p2.uptime - p1.uptime).seconds / 3600. else: tdiff = (p1.uptime - p2.uptime).seconds / 3600. res = fermia(tdiff) * (sip1 + sip2) - fermib(tdiff) * sip1p2 combination[p1, p2] = res return res
def combine(p1, p2, area_of_interest, scores): """Combine passes together. """ try: return combination[p1, p2] except KeyError: pass area = area_of_interest.poly.area() def pscore(poly, coeff=1): if poly is None: return 0 else: return poly.area() * coeff twi1 = get_twilight_poly(p1.uptime) twi2 = get_twilight_poly(p2.uptime) ip1, sip1 = p1.score.get(area_of_interest, (None, None)) if sip1 is None: ip1 = p1.boundary.contour_poly.intersection(area_of_interest.poly) # FIXME: ip1 or ip2 could be None if the pass is entirely inside the # area (or vice versa) if ip1 is None: return 0 ip1d = ip1.intersection(twi1) if ip1d is None: lon, lat = np.rad2deg(ip1.vertices[0, :]) theta = astronomy.cos_zen(p1.uptime, lon, lat) if np.sign(theta) > 0: ip1d = ip1 ip1n = None else: ip1n = ip1 else: twi1.invert() ip1n = ip1.intersection(twi1) twi1.invert() ns1 = pscore(ip1n, scores[p1.satellite][0] / area) ds1 = pscore(ip1d, scores[p1.satellite][1] / area) sip1 = ns1 + ds1 p1.score[area_of_interest] = (ip1, sip1) ip2, sip2 = p2.score.get(area_of_interest, (None, None)) if sip2 is None: ip2 = p2.boundary.contour_poly.intersection(area_of_interest.poly) if ip2 is None: return 0 ip2d = ip2.intersection(twi2) if ip2d is None: lon, lat = np.rad2deg(ip2.vertices[0, :]) theta = astronomy.cos_zen(p2.uptime, lon, lat) if np.sign(theta) > 0: ip2d = ip2 ip2n = None else: ip2n = ip2 else: twi2.invert() ip2n = ip2.intersection(twi2) twi2.invert() ns2 = pscore(ip2n, scores[p2.satellite][0] / area) ds2 = pscore(ip2d, scores[p2.satellite][1] / area) sip2 = ns2 + ds2 p2.score[area_of_interest] = (ip2, sip2) ip1p2 = ip1.intersection(ip2) if ip1p2 is None: sip1p2 = 0 else: ip1p2da = ip1p2.intersection(twi1) twi1.invert() ip1p2na = ip1p2.intersection(twi1) twi1.invert() ip1p2db = ip1p2.intersection(twi2) twi2.invert() ip1p2nb = ip1p2.intersection(twi2) twi2.invert() ns12a = pscore(ip1p2na, scores[p1.satellite][0] / area) ds12a = pscore(ip1p2da, scores[p1.satellite][1] / area) ns12b = pscore(ip1p2nb, scores[p2.satellite][0] / area) ds12b = pscore(ip1p2db, scores[p2.satellite][1] / area) sip1p2a = ns12a + ds12a sip1p2b = ns12b + ds12b sip1p2 = (sip1p2a + sip1p2b) / 2.0 if p2 > p1: tdiff = (p2.uptime - p1.uptime).seconds / 3600. else: tdiff = (p1.uptime - p2.uptime).seconds / 3600. res = fermia(tdiff) * (sip1 + sip2) - fermib(tdiff) * sip1p2 combination[p1, p2] = res return res