def time_equal(a, b): if (a is None) and (b is None): return True elif (a is None) or (b is None): return False else: return _to_datetime(a) == _to_datetime(b)
def coordinates(valid_time, initial_time, pressures, pressure): valid = _to_datetime(valid_time) initial = _to_datetime(initial_time) hours = (valid - initial).total_seconds() / (60 * 60) length = "T{:+}".format(int(hours)) level = "Sea Surface" return { 'valid': [valid], 'initial': [initial], 'length': [length], 'level': [level] }
def _load_cube(self, path, variable, lon0, lat0, time=None): """ Load vertical profile slice from file via iris. """ try: cube = iris.load_cube(path, variable) except iris.exceptions.ConstraintMismatchError as e: print("WARNING: {} No data found for profile plot".format(type(e).__name__)) return { "x": [], "y": []} # reference longitude axis by "axis='X'" and latitude axis as axis='Y', # to accommodate various types of coordinate system. # e.g. 'grid_longitude'. See iris.utils.guess_coord_axis. if cube.coord(axis='X').points[-1] > 180.0: # get circular longitude values lon0 = iris.analysis.cartography.wrap_lons(np.asarray(lon0), 0, 360) # Construct constraint lon_nearest = _find_nearest(lon0, cube.coord(axis='X').points) lat_nearest = _find_nearest(lat0, cube.coord(axis='Y').points) coord_values={ cube.coord(axis='X').standard_name: (lambda cell: lon_nearest == cell.point), cube.coord(axis='Y').standard_name: (lambda cell: lat_nearest == cell.point), } if time is not None and 'time' in [coord.name() for coord in cube.coords()]: coord_values['time'] = ( lambda cell: _to_datetime(time) == cell ) constraint = iris.Constraint(coord_values=coord_values) # Extract nearest profile cube = cube.extract(constraint) assert cube is not None, ("Error: No profile data found for {}\n\t" "at these coordinates: time,lat,lon {},{},{}").format( path, _to_datetime(time), lat_nearest, lon_nearest) # Get level info and data values if 'pressure' in [coord.name() for coord in cube.coords()]: pressure_coord = cube.coord('pressure') pressures = pressure_coord.points.tolist() else: pressures = [0,] if time is None and 'time' in [coord.name() for coord in cube.coords()]: print("Warning: no time specified, selecting first element of array") values = cube.data[0,...] else: values = cube.data return { "x": values, "y": pressures}
def coordinates(valid_time, initial_time, pressures, pressure): valid = _to_datetime(valid_time) initial = _to_datetime(initial_time) hours = (valid - initial).total_seconds() / (60 * 60) length = "T{:+}".format(int(hours)) if (len(pressures) > 0) and (pressure is not None): level = "{} hPa".format(int(pressure)) else: level = "Surface" return { 'valid': [valid], 'initial': [initial], 'length': [length], 'level': [level] }
def select_args(state): """Select args needed by :func:`ProfileView.render` .. note:: If all criteria are not present None is returned :returns: args tuple or None """ if any(att not in state for att in [ "variable", "initial_time", "position"]): return if "valid_time" in state: optional = (_to_datetime(state["valid_time"]),) else: optional = () return ( _to_datetime(state["initial_time"]), state["variable"], state["position"]["x"], state["position"]["y"], state["tools"]["profile"]) + optional
def image(self, state): cube = self._cubes[state.variable] valid_datetime = _to_datetime(state.valid_time) cube = self.extract_cube(cube, valid_datetime) if cube is None: data = empty_image() else: data = geo.stretch_image( cube.coord('longitude').points, cube.coord('latitude').points, cube.data) data.update( coordinates(state.valid_time, state.initial_time, state.pressures, state.pressure)) data.update({'name': [self._label], 'units': [str(cube.units)]}) return data
def scatter(self, state): """Scatter plot of flash position colored by time since flash""" valid_time = _to_datetime(state.valid_time) paths = self.locator.find(valid_time) frame = self.loader.load(paths) frame = self.select_date(frame, valid_time) frame = frame[:400] # Limit points frame["time_since_flash"] = self.since_flash(frame["date"], valid_time) if len(frame) == 0: return self.empty_image x, y = geo.web_mercator(frame.longitude, frame.latitude) self.color_mapper.low = np.min(frame.time_since_flash) self.color_mapper.high = np.max(frame.time_since_flash) self.sources["scatter"].data = { "x": x, "y": y, "date": frame.date, "longitude": frame.longitude, "latitude": frame.latitude, "flash_type": frame.flash_type, "time_since_flash": frame.time_since_flash, }
def image(self, state): cube = self._cubes[state.variable] valid_datetime = _to_datetime(state.valid_time) cube = cube.extract(iris.Constraint(time=valid_datetime)) if cube is None: data = empty_image() else: data = geo.stretch_image( cube.coord("longitude").points, cube.coord("latitude").points, cube.data, ) data.update( coordinates( state.valid_time, state.initial_time, state.pressures, state.pressure, )) data.update({"name": [self._label], "units": [str(cube.units)]}) return data
def image(self, state): """Image colored by time since flash or flash density""" valid_time = _to_datetime(state.valid_time) # 15 minute/1 hour slice of data? window = dt.timedelta(minutes=60) # 1 hour window paths = self.locator.find_period(valid_time, window) frame = self.loader.load(paths) frame = self.select_date(frame, valid_time, window) # Filter intra-cloud/cloud-ground rows if "intra-cloud" in state.variable.lower(): frame = frame[frame["flash_type"] == "IC"] elif "cloud-ground" in state.variable.lower(): frame = frame[frame["flash_type"] == "CG"] # EarthNetworks validity box (not needed if tiling algorithm) longitude_range = (26, 40) latitude_range = (-12, 4) x_range, y_range = geo.web_mercator(longitude_range, latitude_range) x, y = geo.web_mercator(frame["longitude"], frame["latitude"]) frame["x"] = x frame["y"] = y pixels = 256 canvas = datashader.Canvas( plot_width=pixels, plot_height=pixels, x_range=x_range, y_range=y_range, ) if "density" in state.variable.lower(): # N flashes per pixel agg = canvas.points(frame, "x", "y", datashader.count()) else: frame["since_flash"] = self.since_flash(frame["date"], valid_time) agg = canvas.points(frame, "x", "y", datashader.max("since_flash")) # Note: DataArray objects are not JSON serializable, .values is the # same data cast as a numpy array x = agg.x.values.min() y = agg.y.values.min() dw = agg.x.values.max() - x dh = agg.y.values.max() - y image = np.ma.masked_array( agg.values.astype(np.float), mask=np.isnan(agg.values) ) if "density" in state.variable.lower(): image[image == 0] = np.ma.masked # Remove pixels with no data # Update color_mapper color_mapper = self.color_mappers["image"] if "density" in state.variable.lower(): color_mapper.palette = bokeh.palettes.all_palettes["Spectral"][8] color_mapper.low = 0 color_mapper.high = agg.values.max() else: color_mapper.palette = bokeh.palettes.all_palettes["RdGy"][8] color_mapper.low = 0 color_mapper.high = 60 * 60 # 1 hour # Update tooltips for hover_tool in self.hover_tools["image"]: hover_tool.tooltips = self.tooltips(state.variable) hover_tool.formatters = self.formatters(state.variable) if "density" in state.variable.lower(): units = "events" else: units = "seconds" data = { "x": [x], "y": [y], "dw": [dw], "dh": [dh], "image": [image], } meta_data = { "variable": [state.variable], "date": [valid_time], "units": [units], "window": [window.total_seconds()], } data.update(meta_data) self.sources["image"].data = data
def _key(t): return str(_to_datetime(t))
def datetimes(self): return [_to_datetime(t) for t in self.times]