def determine_grid_type(dataset_url): try: UGrid.from_ncfile(dataset_url) grid_type = 'ugrid' except ValueError: try: from_ncfile(dataset_url) grid_type = 'sgrid' except SGridNonCompliantError: grid_type = None return grid_type
def wgs84_bounds(self, layer): with netCDF4.Dataset(self.topology_file) as nc: try: data_location = nc.variables['u'].location mesh_name = nc.variables['u'].mesh # Use local topology for pulling bounds data ug = UGrid.from_ncfile(self.topology_file, mesh_name=mesh_name) coords = np.empty(0) if data_location == 'node': coords = ug.nodes elif data_location == 'face': coords = ug.face_coordinates elif data_location == 'edge': coords = ug.edge_coordinates minx = np.nanmin(coords[:, 0]) miny = np.nanmin(coords[:, 1]) maxx = np.nanmax(coords[:, 0]) maxy = np.nanmax(coords[:, 1]) return DotDict(minx=minx, miny=miny, maxx=maxx, maxy=maxy, bbox=(minx, miny, maxx, maxy)) except AttributeError: pass
def add_mesh(cube, url): """ Adds the unstructured mesh info the to cube. Soon in an iris near you! """ from pyugrid import UGrid ug = UGrid.from_ncfile(url) cube.mesh = ug cube.mesh_dimension = 1 return cube
def wgs84_bounds(self, layer): with self.dataset() as nc: try: data_location = nc.variables[layer.access_name].location mesh_name = nc.variables[layer.access_name].mesh # Use local topology for pulling bounds data ug = UGrid.from_ncfile(self.topology_file, mesh_name=mesh_name) coords = np.empty(0) if data_location == 'node': coords = ug.nodes elif data_location == 'face': coords = ug.face_coordinates elif data_location == 'edge': coords = ug.edge_coordinates minx = np.nanmin(coords[:, 1]) miny = np.nanmin(coords[:, 0]) maxx = np.nanmax(coords[:, 1]) maxy = np.nanmax(coords[:, 0]) return DotDict(minx=minx, miny=miny, maxx=maxx, maxy=maxy) except AttributeError: pass
def getmap(self, layer, request): time_index, time_value = self.nearest_time(layer, request.GET['time']) wgs84_bbox = request.GET['wgs84_bbox'] with self.dataset() as nc: data_obj = nc.variables[layer.access_name] data_location = data_obj.location mesh_name = data_obj.mesh ug = UGrid.from_ncfile(self.topology_file, mesh_name=mesh_name) coords = np.empty(0) if data_location == 'node': coords = ug.nodes elif data_location == 'face': coords = ug.face_coordinates elif data_location == 'edge': coords = ug.edge_coordinates lon = coords[:, 0] lat = coords[:, 1] if request.GET[ 'vectorscale'] is not None: # is not None if vectors are being plotted vectorscale = request.GET['vectorscale'] padding_factor = calc_safety_factor(vectorscale) vectorstep = request.GET[ 'vectorstep'] # returns 1 by default if vectors are being plotted spatial_idx_padding = calc_lon_lat_padding( lon, lat, padding_factor) * vectorstep spatial_idx = data_handler.lat_lon_subset_idx( lon, lat, wgs84_bbox.minx, wgs84_bbox.miny, wgs84_bbox.maxx, wgs84_bbox.maxy, padding=spatial_idx_padding) if vectorstep > 1: np.random.shuffle(spatial_idx) nvec = int(len(spatial_idx) / vectorstep) spatial_idx = spatial_idx[:nvec] else: spatial_idx = data_handler.lat_lon_subset_idx( lon, lat, wgs84_bbox.minx, wgs84_bbox.miny, wgs84_bbox.maxx, wgs84_bbox.maxy) face_indicies = ug.faces[:] face_indicies_spatial_idx = data_handler.faces_subset_idx( face_indicies, spatial_idx) # If no triangles intersect the field of view, return a transparent tile if (len(spatial_idx) == 0) or (len(face_indicies_spatial_idx) == 0): logger.debug( "No triangles in field of view, returning empty tile.") return self.empty_response(layer, request) if isinstance(layer, Layer): if (len(data_obj.shape) == 3): z_index, z_value = self.nearest_z(layer, request.GET['elevation']) data = data_obj[time_index, z_index, :] elif (len(data_obj.shape) == 2): data = data_obj[time_index, :] elif len(data_obj.shape) == 1: data = data_obj[:] else: logger.debug( "Dimension Mismatch: data_obj.shape == {0} and time = {1}" .format(data_obj.shape, time_value)) return self.empty_response(layer, request) if request.GET['image_type'] == 'filledcontours': mask = np.isnan(data) # array with NaNs appearing as True if mask.any(): data_mask = ~mask # negate the NaN boolean array; mask for non-NaN data elements # slice the data, lon, and lat to get elements that correspond to non-NaN values data = data_mask[data_mask] lon = lon[data_mask] lat = lat[data_mask] # recalculate the spatial index using the subsetted lat/lon spatial_idx = data_handler.lat_lon_subset_idx( lon, lat, wgs84_bbox.minx, wgs84_bbox.miny, wgs84_bbox.maxx, wgs84_bbox.maxy) face_indicies_spatial_idx = data_handler.faces_subset_idx( face_indicies, spatial_idx) tri_subset = Tri.Triangulation( lon, lat, triangles=face_indicies[face_indicies_spatial_idx]) return mpl_handler.tricontourf_response( tri_subset, data, request) else: raise NotImplementedError( 'Image type "{}" is not supported.'.format( request.GET['image_type'])) elif isinstance(layer, VirtualLayer): # Data needs to be [var1,var2] where var are 1D (nodes only, elevation and time already handled) data = [] for l in layer.layers: data_obj = nc.variables[l.var_name] if (len(data_obj.shape) == 3): z_index, z_value = self.nearest_z( layer, request.GET['elevation']) data.append(data_obj[time_index, z_index, :]) elif (len(data_obj.shape) == 2): data.append(data_obj[time_index, :]) elif len(data_obj.shape) == 1: data.append(data_obj[:]) else: logger.debug( "Dimension Mismatch: data_obj.shape == {0} and time = {1}" .format(data_obj.shape, time_value)) return self.empty_response(layer, request) if request.GET['image_type'] == 'vectors': return mpl_handler.quiver_response(lon[spatial_idx], lat[spatial_idx], data[0][spatial_idx], data[1][spatial_idx], request, vectorscale) else: raise NotImplementedError( 'Image type "{}" is not supported.'.format( request.GET['image_type']))
def minmax(self, layer, request): time_index, time_value = self.nearest_time(layer, request.GET['time']) wgs84_bbox = request.GET['wgs84_bbox'] with self.dataset() as nc: data_obj = nc.variables[layer.access_name] data_location = data_obj.location mesh_name = data_obj.mesh ug = UGrid.from_ncfile(self.topology_file, mesh_name=mesh_name) coords = np.empty(0) if data_location == 'node': coords = ug.nodes elif data_location == 'face': coords = ug.face_coordinates elif data_location == 'edge': coords = ug.edge_coordinates lon = coords[:, 0] lat = coords[:, 1] spatial_idx = data_handler.lat_lon_subset_idx( lon, lat, wgs84_bbox.minx, wgs84_bbox.miny, wgs84_bbox.maxx, wgs84_bbox.maxy) vmin = None vmax = None data = None if isinstance(layer, Layer): if (len(data_obj.shape) == 3): z_index, z_value = self.nearest_z(layer, request.GET['elevation']) data = data_obj[time_index, z_index, spatial_idx] elif (len(data_obj.shape) == 2): data = data_obj[time_index, spatial_idx] elif len(data_obj.shape) == 1: data = data_obj[spatial_idx] else: logger.debug( "Dimension Mismatch: data_obj.shape == {0} and time = {1}" .format(data_obj.shape, time_value)) if data is not None: vmin = np.nanmin(data).item() vmax = np.nanmax(data).item() elif isinstance(layer, VirtualLayer): # Data needs to be [var1,var2] where var are 1D (nodes only, elevation and time already handled) data = [] for l in layer.layers: data_obj = nc.variables[l.var_name] if (len(data_obj.shape) == 3): z_index, z_value = self.nearest_z( layer, request.GET['elevation']) data.append(data_obj[time_index, z_index, spatial_idx]) elif (len(data_obj.shape) == 2): data.append(data_obj[time_index, spatial_idx]) elif len(data_obj.shape) == 1: data.append(data_obj[spatial_idx]) else: logger.debug( "Dimension Mismatch: data_obj.shape == {0} and time = {1}" .format(data_obj.shape, time_value)) if ',' in layer.var_name and data: # Vectors, so return magnitude data = [ sqrt((u * u) + (v * v)) for ( u, v, ) in data.T if u != np.nan and v != np.nan ] vmin = min(data) vmax = max(data) return gmd_handler.from_dict(dict(min=vmin, max=vmax))
def getmap(self, layer, request): time_index, time_value = self.nearest_time(layer, request.GET['time']) wgs84_bbox = request.GET['wgs84_bbox'] with self.dataset() as nc: data_obj = nc.variables[layer.access_name] data_location = data_obj.location mesh_name = data_obj.mesh ug = UGrid.from_ncfile(self.topology_file, mesh_name=mesh_name) coords = np.empty(0) if data_location == 'node': coords = ug.nodes elif data_location == 'face': coords = ug.face_coordinates elif data_location == 'edge': coords = ug.edge_coordinates lon = coords[:, 0] lat = coords[:, 1] # Calculate any vector padding if we need to padding = None vector_step = request.GET['vectorstep'] if request.GET['image_type'] == 'vectors': padding_factor = calc_safety_factor(request.GET['vectorscale']) padding = calc_lon_lat_padding(lon, lat, padding_factor) * vector_step # Calculate the boolean spatial mask to slice with bool_spatial_idx = data_handler.ugrid_lat_lon_subset_idx(lon, lat, bbox=wgs84_bbox.bbox, padding=padding) # Randomize vectors to subset if we need to if request.GET['image_type'] == 'vectors' and vector_step > 1: num_vec = int(bool_spatial_idx.size / vector_step) step = int(bool_spatial_idx.size / num_vec) bool_spatial_idx[np.where(bool_spatial_idx==True)][0::step] = False # noqa: E225 # If no triangles intersect the field of view, return a transparent tile if not np.any(bool_spatial_idx): logger.info("No triangles in field of view, returning empty tile.") return self.empty_response(layer, request) if isinstance(layer, Layer): if (len(data_obj.shape) == 3): z_index, z_value = self.nearest_z(layer, request.GET['elevation']) data = data_obj[time_index, z_index, :] elif (len(data_obj.shape) == 2): data = data_obj[time_index, :] elif len(data_obj.shape) == 1: data = data_obj[:] else: logger.debug("Dimension Mismatch: data_obj.shape == {0} and time = {1}".format(data_obj.shape, time_value)) return self.empty_response(layer, request) if request.GET['image_type'] in ['pcolor', 'contours', 'filledcontours']: # Avoid triangles with nan values bool_spatial_idx[np.isnan(data)] = False # Get the faces to plot faces = ug.faces[:] face_idx = data_handler.face_idx_from_node_idx(faces, bool_spatial_idx) faces_subset = faces[face_idx] tri_subset = Tri.Triangulation(lon, lat, triangles=faces_subset) if request.GET['image_type'] == 'pcolor': return mpl_handler.tripcolor_response(tri_subset, data, request, data_location=data_location) else: return mpl_handler.tricontouring_response(tri_subset, data, request) elif request.GET['image_type'] in ['filledhatches', 'hatches']: raise NotImplementedError('matplotlib does not support hatching on triangular grids... sorry!') else: raise NotImplementedError('Image type "{}" is not supported.'.format(request.GET['image_type'])) elif isinstance(layer, VirtualLayer): # Data needs to be [var1,var2] where var are 1D (nodes only, elevation and time already handled) data = [] for l in layer.layers: data_obj = nc.variables[l.var_name] if (len(data_obj.shape) == 3): z_index, z_value = self.nearest_z(layer, request.GET['elevation']) data.append(data_obj[time_index, z_index, bool_spatial_idx]) elif (len(data_obj.shape) == 2): data.append(data_obj[time_index, bool_spatial_idx]) elif len(data_obj.shape) == 1: data.append(data_obj[bool_spatial_idx]) else: logger.debug("Dimension Mismatch: data_obj.shape == {0} and time = {1}".format(data_obj.shape, time_value)) return self.empty_response(layer, request) if request.GET['image_type'] == 'vectors': return mpl_handler.quiver_response(lon[bool_spatial_idx], lat[bool_spatial_idx], data[0], data[1], request) else: raise NotImplementedError('Image type "{}" is not supported.'.format(request.GET['image_type']))
def getmap(self, layer, request): time_index, time_value = self.nearest_time(layer, request.GET['time']) wgs84_bbox = request.GET['wgs84_bbox'] with self.dataset() as nc: data_obj = nc.variables[layer.access_name] data_location = data_obj.location mesh_name = data_obj.mesh ug = UGrid.from_ncfile(self.topology_file, mesh_name=mesh_name) coords = np.empty(0) if data_location == 'node': coords = ug.nodes elif data_location == 'face': coords = ug.face_coordinates elif data_location == 'edge': coords = ug.edge_coordinates lon = coords[:, 0] lat = coords[:, 1] if request.GET['vectorscale'] is not None: # is not None if vectors are being plotted vectorscale = request.GET['vectorscale'] padding_factor = calc_safety_factor(vectorscale) vectorstep = request.GET['vectorstep'] # returns 1 by default if vectors are being plotted spatial_idx_padding = calc_lon_lat_padding(lon, lat, padding_factor) * vectorstep spatial_idx = data_handler.lat_lon_subset_idx(lon, lat, wgs84_bbox.minx, wgs84_bbox.miny, wgs84_bbox.maxx, wgs84_bbox.maxy, padding=spatial_idx_padding ) if vectorstep > 1: np.random.shuffle(spatial_idx) nvec = int(len(spatial_idx) / vectorstep) spatial_idx = spatial_idx[:nvec] else: spatial_idx = data_handler.lat_lon_subset_idx(lon, lat, wgs84_bbox.minx, wgs84_bbox.miny, wgs84_bbox.maxx, wgs84_bbox.maxy ) face_indicies = ug.faces[:] face_indicies_spatial_idx = data_handler.faces_subset_idx(face_indicies, spatial_idx) # If no triangles intersect the field of view, return a transparent tile if (len(spatial_idx) == 0) or (len(face_indicies_spatial_idx) == 0): logger.debug("No triangles in field of view, returning empty tile.") return self.empty_response(layer, request) if isinstance(layer, Layer): if (len(data_obj.shape) == 3): z_index, z_value = self.nearest_z(layer, request.GET['elevation']) data = data_obj[time_index, z_index, :] elif (len(data_obj.shape) == 2): data = data_obj[time_index, :] elif len(data_obj.shape) == 1: data = data_obj[:] else: logger.debug("Dimension Mismatch: data_obj.shape == {0} and time = {1}".format(data_obj.shape, time_value)) return self.empty_response(layer, request) if request.GET['image_type'] == 'filledcontours': mask = np.isnan(data) # array with NaNs appearing as True if mask.any(): data_mask = ~mask # negate the NaN boolean array; mask for non-NaN data elements # slice the data, lon, and lat to get elements that correspond to non-NaN values data = data_mask[data_mask] lon = lon[data_mask] lat = lat[data_mask] # recalculate the spatial index using the subsetted lat/lon spatial_idx = data_handler.lat_lon_subset_idx(lon, lat, wgs84_bbox.minx, wgs84_bbox.miny, wgs84_bbox.maxx, wgs84_bbox.maxy ) face_indicies_spatial_idx = data_handler.faces_subset_idx(face_indicies, spatial_idx) tri_subset = Tri.Triangulation(lon, lat, triangles=face_indicies[face_indicies_spatial_idx] ) return mpl_handler.tricontourf_response(tri_subset, data, request ) else: raise NotImplementedError('Image type "{}" is not supported.'.format(request.GET['image_type'])) elif isinstance(layer, VirtualLayer): # Data needs to be [var1,var2] where var are 1D (nodes only, elevation and time already handled) data = [] for l in layer.layers: data_obj = nc.variables[l.var_name] if (len(data_obj.shape) == 3): z_index, z_value = self.nearest_z(layer, request.GET['elevation']) data.append(data_obj[time_index, z_index, :]) elif (len(data_obj.shape) == 2): data.append(data_obj[time_index, :]) elif len(data_obj.shape) == 1: data.append(data_obj[:]) else: logger.debug("Dimension Mismatch: data_obj.shape == {0} and time = {1}".format(data_obj.shape, time_value)) return self.empty_response(layer, request) if request.GET['image_type'] == 'vectors': return mpl_handler.quiver_response(lon[spatial_idx], lat[spatial_idx], data[0][spatial_idx], data[1][spatial_idx], request, vectorscale ) else: raise NotImplementedError('Image type "{}" is not supported.'.format(request.GET['image_type']))
def minmax(self, layer, request): time_index, time_value = self.nearest_time(layer, request.GET['time']) wgs84_bbox = request.GET['wgs84_bbox'] with self.dataset() as nc: data_obj = nc.variables[layer.access_name] data_location = data_obj.location mesh_name = data_obj.mesh ug = UGrid.from_ncfile(self.topology_file, mesh_name=mesh_name) coords = np.empty(0) if data_location == 'node': coords = ug.nodes elif data_location == 'face': coords = ug.face_coordinates elif data_location == 'edge': coords = ug.edge_coordinates lon = coords[:, 0] lat = coords[:, 1] spatial_idx = data_handler.lat_lon_subset_idx(lon, lat, wgs84_bbox.minx, wgs84_bbox.miny, wgs84_bbox.maxx, wgs84_bbox.maxy) vmin = None vmax = None data = None if isinstance(layer, Layer): if (len(data_obj.shape) == 3): z_index, z_value = self.nearest_z(layer, request.GET['elevation']) data = data_obj[time_index, z_index, spatial_idx] elif (len(data_obj.shape) == 2): data = data_obj[time_index, spatial_idx] elif len(data_obj.shape) == 1: data = data_obj[spatial_idx] else: logger.debug("Dimension Mismatch: data_obj.shape == {0} and time = {1}".format(data_obj.shape, time_value)) if data is not None: vmin = np.nanmin(data).item() vmax = np.nanmax(data).item() elif isinstance(layer, VirtualLayer): # Data needs to be [var1,var2] where var are 1D (nodes only, elevation and time already handled) data = [] for l in layer.layers: data_obj = nc.variables[l.var_name] if (len(data_obj.shape) == 3): z_index, z_value = self.nearest_z(layer, request.GET['elevation']) data.append(data_obj[time_index, z_index, spatial_idx]) elif (len(data_obj.shape) == 2): data.append(data_obj[time_index, spatial_idx]) elif len(data_obj.shape) == 1: data.append(data_obj[spatial_idx]) else: logger.debug("Dimension Mismatch: data_obj.shape == {0} and time = {1}".format(data_obj.shape, time_value)) if ',' in layer.var_name and data: # Vectors, so return magnitude data = [ sqrt((u*u) + (v*v)) for (u, v,) in data.T if u != np.nan and v != np.nan] vmin = min(data) vmax = max(data) return gmd_handler.from_dict(dict(min=vmin, max=vmax))
def getmap(self, layer, request): time_index, time_value = self.nearest_time(layer, request.GET['time']) wgs84_bbox = request.GET['wgs84_bbox'] with self.dataset() as nc: data_obj = nc.variables[layer.access_name] data_location = data_obj.location mesh_name = data_obj.mesh ug = UGrid.from_ncfile(self.topology_file, mesh_name=mesh_name) coords = np.empty(0) if data_location == 'node': coords = ug.nodes elif data_location == 'face': coords = ug.face_coordinates elif data_location == 'edge': coords = ug.edge_coordinates lon = coords[:, 0] lat = coords[:, 1] # Calculate any vector padding if we need to padding = None vector_step = request.GET['vectorstep'] if request.GET['image_type'] == 'vectors': padding_factor = calc_safety_factor(request.GET['vectorscale']) padding = calc_lon_lat_padding(lon, lat, padding_factor) * vector_step # Calculate the boolean spatial mask to slice with bool_spatial_idx = data_handler.ugrid_lat_lon_subset_idx(lon, lat, bbox=wgs84_bbox.bbox, padding=padding) # Randomize vectors to subset if we need to if request.GET['image_type'] == 'vectors' and vector_step > 1: num_vec = int(bool_spatial_idx.size / vector_step) step = int(bool_spatial_idx.size / num_vec) bool_spatial_idx[np.where(bool_spatial_idx==True)][0::step] = False # If no triangles intersect the field of view, return a transparent tile if not np.any(bool_spatial_idx): logger.warning("No triangles in field of view, returning empty tile.") return self.empty_response(layer, request) if isinstance(layer, Layer): if (len(data_obj.shape) == 3): z_index, z_value = self.nearest_z(layer, request.GET['elevation']) data = data_obj[time_index, z_index, :] elif (len(data_obj.shape) == 2): data = data_obj[time_index, :] elif len(data_obj.shape) == 1: data = data_obj[:] else: logger.debug("Dimension Mismatch: data_obj.shape == {0} and time = {1}".format(data_obj.shape, time_value)) return self.empty_response(layer, request) if request.GET['image_type'] in ['pcolor', 'contours', 'filledcontours']: # Avoid triangles with nan values bool_spatial_idx[np.isnan(data)] = False # Get the faces to plot faces = ug.faces[:] face_idx = data_handler.face_idx_from_node_idx(faces, bool_spatial_idx) faces_subset = faces[face_idx] tri_subset = Tri.Triangulation(lon, lat, triangles=faces_subset) if request.GET['image_type'] == 'pcolor': return mpl_handler.tripcolor_response(tri_subset, data, request, data_location=data_location) else: return mpl_handler.tricontouring_response(tri_subset, data, request) elif request.GET['image_type'] in ['filledhatches', 'hatches']: raise NotImplementedError('matplotlib does not support hatching on triangular grids... sorry!') else: raise NotImplementedError('Image type "{}" is not supported.'.format(request.GET['image_type'])) elif isinstance(layer, VirtualLayer): # Data needs to be [var1,var2] where var are 1D (nodes only, elevation and time already handled) data = [] for l in layer.layers: data_obj = nc.variables[l.var_name] if (len(data_obj.shape) == 3): z_index, z_value = self.nearest_z(layer, request.GET['elevation']) data.append(data_obj[time_index, z_index, bool_spatial_idx]) elif (len(data_obj.shape) == 2): data.append(data_obj[time_index, bool_spatial_idx]) elif len(data_obj.shape) == 1: data.append(data_obj[bool_spatial_idx]) else: logger.debug("Dimension Mismatch: data_obj.shape == {0} and time = {1}".format(data_obj.shape, time_value)) return self.empty_response(layer, request) if request.GET['image_type'] == 'vectors': return mpl_handler.quiver_response(lon[bool_spatial_idx], lat[bool_spatial_idx], data[0], data[1], request) else: raise NotImplementedError('Image type "{}" is not supported.'.format(request.GET['image_type']))