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: cached_sg = from_ncfile(self.topology_file) lon_name, lat_name = cached_sg.face_coordinates lon_obj = getattr(cached_sg, lon_name) lat_obj = getattr(cached_sg, lat_name) centers = cached_sg.centers lon = centers[..., 0][lon_obj.center_slicing] lat = centers[..., 1][lat_obj.center_slicing] if isinstance(layer, Layer): data_obj = getattr(cached_sg, layer.access_name) raw_var = nc.variables[layer.access_name] if len(raw_var.shape) == 4: z_index, z_value = self.nearest_z(layer, request.GET['elevation']) raw_data = raw_var[time_index, z_index, data_obj.center_slicing[-2], data_obj.center_slicing[-1]] elif len(raw_var.shape) == 3: raw_data = raw_var[time_index, data_obj.center_slicing[-2], data_obj.center_slicing[-1]] elif len(raw_var.shape) == 2: raw_data = raw_var[data_obj.center_slicing] else: raise BaseException('Unable to trim variable {0} data.'.format(layer.access_name)) # handle edge variables if data_obj.location is not None and 'edge' in data_obj.location: raw_data = avg_to_cell_center(raw_data, data_obj.center_axis) if request.GET['image_type'] == 'pcolor': return mpl_handler.pcolormesh_response(lon, lat, data=raw_data, request=request) elif request.GET['image_type'] == 'filledcontours': return mpl_handler.contourf_response(lon, lat, data=raw_data, request=request) else: raise NotImplementedError('Image type "{}" is not supported.'.format(request.GET['image_type'])) elif isinstance(layer, VirtualLayer): x_var = None y_var = None raw_vars = [] for l in layer.layers: data_obj = getattr(cached_sg, l.access_name) raw_var = nc.variables[l.access_name] raw_vars.append(raw_var) if len(raw_var.shape) == 4: z_index, z_value = self.nearest_z(layer, request.GET['elevation']) raw_data = raw_var[time_index, z_index, data_obj.center_slicing[-2], data_obj.center_slicing[-1]] elif len(raw_var.shape) == 3: raw_data = raw_var[time_index, data_obj.center_slicing[-2], data_obj.center_slicing[-1]] elif len(raw_var.shape) == 2: raw_data = raw_var[data_obj.center_slicing] else: raise BaseException('Unable to trim variable {0} data.'.format(l.access_name)) raw_data = avg_to_cell_center(raw_data, data_obj.center_axis) if x_var is None: if data_obj.vector_axis and data_obj.vector_axis.lower() == 'x': x_var = raw_data elif data_obj.center_axis == 1: x_var = raw_data if y_var is None: if data_obj.vector_axis and data_obj.vector_axis.lower() == 'y': y_var = raw_data elif data_obj.center_axis == 0: y_var = raw_data if x_var is None or y_var is None: raise BaseException('Unable to determine x and y variables.') dim_lengths = [ len(v.dimensions) for v in raw_vars ] if len(list(set(dim_lengths))) != 1: raise AttributeError('One or both of the specified variables has screwed up dimensions.') if request.GET['image_type'] == 'vectors': angles = cached_sg.angles[lon_obj.center_slicing] vectorstep = request.GET['vectorstep'] # don't do this if the vectorstep is 1; let's save a microsecond or two # it's identical to getting all the data if vectorstep > 1: data_dim = len(lon.shape) step_slice = (np.s_[::vectorstep],) * data_dim # make sure the vector step is used for all applicable dimensions lon = lon[step_slice] lat = lat[step_slice] x_var = x_var[step_slice] y_var = y_var[step_slice] angles = angles[step_slice] vectorscale = request.GET['vectorscale'] padding_factor = calc_safety_factor(vectorscale) # figure out the average distance between lat/lon points # do the math after taking into the vectorstep if specified spatial_idx_padding = calc_lon_lat_padding(lon, lat, padding_factor) spatial_idx = data_handler.lat_lon_subset_idx(lon, lat, lonmin=wgs84_bbox.minx, latmin=wgs84_bbox.miny, lonmax=wgs84_bbox.maxx, latmax=wgs84_bbox.maxy, padding=spatial_idx_padding ) subset_lon = self._spatial_data_subset(lon, spatial_idx) subset_lat = self._spatial_data_subset(lat, spatial_idx) # rotate vectors x_rot, y_rot = rotate_vectors(x_var, y_var, angles) spatial_subset_x_rot = self._spatial_data_subset(x_rot, spatial_idx) spatial_subset_y_rot = self._spatial_data_subset(y_rot, spatial_idx) return mpl_handler.quiver_response(subset_lon, subset_lat, spatial_subset_x_rot, spatial_subset_y_rot, request, vectorscale ) else: raise NotImplementedError('Image type "{}" is not supported.'.format(request.GET['image_type']))
def get_tidal_vectors(self, layer, time, bbox, vector_scale=None, vector_step=None): vector_scale = vector_scale or 1 vector_step = vector_step or 1 with netCDF4.Dataset(self.topology_file) as nc: data_obj = nc.variables[layer.access_name] data_location = getattr(data_obj, 'location', 'node') mesh_name = data_obj.mesh ug = UGrid.from_nc_dataset(nc, 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] padding_factor = calc_safety_factor(vector_scale) padding = calc_lon_lat_padding(lon, lat, padding_factor) * vector_step spatial_idx = data_handler.ugrid_lat_lon_subset_idx( lon, lat, bbox=bbox, padding=padding) tnames = nc.get_variables_by_attributes( standard_name='tide_constituent')[0] tfreqs = nc.get_variables_by_attributes( standard_name='tide_frequency')[0] from utide import _ut_constants_fname from utide.utilities import loadmatbunch con_info = loadmatbunch(_ut_constants_fname)['const'] # Get names from the utide constant file utide_const_names = [e.strip() for e in con_info['name'].tolist()] # netCDF4-python is returning ugly arrays of bytes... names = [] for n in tnames[:]: z = ''.join([x.decode('utf-8') for x in n.tolist() if x]).strip() names.append(z) if 'STEADY' in names: names[names.index('STEADY')] = 'Z0' extract_names = list( set(utide_const_names).intersection(set(names))) ntides = data_obj.shape[data_obj.dimensions.index('ntides')] extract_mask = np.zeros(shape=(ntides, ), dtype=bool) for n in extract_names: extract_mask[names.index(n)] = True if not spatial_idx.any() or not extract_mask.any(): e = np.ma.empty(0) return e, e, e, e ua = nc.variables['u'][extract_mask, spatial_idx] va = nc.variables['v'][extract_mask, spatial_idx] up = nc.variables['u_phase'][extract_mask, spatial_idx] vp = nc.variables['v_phase'][extract_mask, spatial_idx] freqs = tfreqs[extract_mask] omega = freqs * 3600 # Convert from radians/s to radians/hour. from utide.harmonics import FUV from matplotlib.dates import date2num v, u, f = FUV( t=np.array([date2num(time) + 366.1667]), tref=np.array([0]), lind=np.array( [utide_const_names.index(x) for x in extract_names]), lat= 55, # Reference latitude for 3rd order satellites (degrees) (55 is fine always) ngflgs=[0, 0, 0, 0]) # [NodsatLint NodsatNone GwchLint GwchNone] s = calendar.timegm(time.timetuple()) / 60 / 60. v, u, f = map(np.squeeze, (v, u, f)) v = v * 2 * np.pi # Convert phase in radians. u = u * 2 * np.pi # Convert phase in radians. U = (f * ua.T * np.cos(v + s * omega + u - up.T * np.pi / 180)).sum(axis=1) V = (f * va.T * np.cos(v + s * omega + u - vp.T * np.pi / 180)).sum(axis=1) return U, V, lon[spatial_idx], lat[spatial_idx]
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 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: cached_sg = from_ncfile(self.topology_file) lon_name, lat_name = cached_sg.face_coordinates lon_obj = getattr(cached_sg, lon_name) lat_obj = getattr(cached_sg, lat_name) centers = cached_sg.centers lon = centers[..., 0][lon_obj.center_slicing] lat = centers[..., 1][lat_obj.center_slicing] if isinstance(layer, Layer): data_obj = getattr(cached_sg, layer.access_name) raw_var = nc.variables[layer.access_name] if len(raw_var.shape) == 4: z_index, z_value = self.nearest_z(layer, request.GET['elevation']) raw_data = raw_var[time_index, z_index, data_obj.center_slicing[-2], data_obj.center_slicing[-1]] elif len(raw_var.shape) == 3: raw_data = raw_var[time_index, data_obj.center_slicing[-2], data_obj.center_slicing[-1]] elif len(raw_var.shape) == 2: raw_data = raw_var[data_obj.center_slicing] else: raise BaseException( 'Unable to trim variable {0} data.'.format( layer.access_name)) # handle edge variables if data_obj.location is not None and 'edge' in data_obj.location: raw_data = avg_to_cell_center(raw_data, data_obj.center_axis) if request.GET['image_type'] == 'pcolor': return mpl_handler.pcolormesh_response(lon, lat, data=raw_data, request=request) elif request.GET['image_type'] in [ 'filledhatches', 'hatches', 'filledcontours', 'contours' ]: return mpl_handler.contouring_response(lon, lat, data=raw_data, request=request) else: raise NotImplementedError( 'Image type "{}" is not supported.'.format( request.GET['image_type'])) elif isinstance(layer, VirtualLayer): x_var = None y_var = None raw_vars = [] for l in layer.layers: data_obj = getattr(cached_sg, l.access_name) raw_var = nc.variables[l.access_name] raw_vars.append(raw_var) if len(raw_var.shape) == 4: z_index, z_value = self.nearest_z( layer, request.GET['elevation']) raw_data = raw_var[time_index, z_index, data_obj.center_slicing[-2], data_obj.center_slicing[-1]] elif len(raw_var.shape) == 3: raw_data = raw_var[time_index, data_obj.center_slicing[-2], data_obj.center_slicing[-1]] elif len(raw_var.shape) == 2: raw_data = raw_var[data_obj.center_slicing] else: raise BaseException( 'Unable to trim variable {0} data.'.format( l.access_name)) raw_data = avg_to_cell_center(raw_data, data_obj.center_axis) if x_var is None: if data_obj.vector_axis and data_obj.vector_axis.lower( ) == 'x': x_var = raw_data elif data_obj.center_axis == 1: x_var = raw_data if y_var is None: if data_obj.vector_axis and data_obj.vector_axis.lower( ) == 'y': y_var = raw_data elif data_obj.center_axis == 0: y_var = raw_data if x_var is None or y_var is None: raise BaseException( 'Unable to determine x and y variables.') dim_lengths = [len(v.dimensions) for v in raw_vars] if len(list(set(dim_lengths))) != 1: raise AttributeError( 'One or both of the specified variables has screwed up dimensions.' ) if request.GET['image_type'] == 'vectors': angles = cached_sg.angles[lon_obj.center_slicing] vectorstep = request.GET['vectorstep'] # don't do this if the vectorstep is 1; let's save a microsecond or two # it's identical to getting all the data if vectorstep > 1: data_dim = len(lon.shape) step_slice = ( np.s_[::vectorstep], ) * data_dim # make sure the vector step is used for all applicable dimensions lon = lon[step_slice] lat = lat[step_slice] x_var = x_var[step_slice] y_var = y_var[step_slice] angles = angles[step_slice] vectorscale = request.GET['vectorscale'] padding_factor = calc_safety_factor(vectorscale) # figure out the average distance between lat/lon points # do the math after taking into the vectorstep if specified spatial_idx_padding = calc_lon_lat_padding( lon, lat, padding_factor) spatial_idx = data_handler.lat_lon_subset_idx( lon, lat, lonmin=wgs84_bbox.minx, latmin=wgs84_bbox.miny, lonmax=wgs84_bbox.maxx, latmax=wgs84_bbox.maxy, padding=spatial_idx_padding) subset_lon = self._spatial_data_subset(lon, spatial_idx) subset_lat = self._spatial_data_subset(lat, spatial_idx) # rotate vectors x_rot, y_rot = rotate_vectors(x_var, y_var, angles) spatial_subset_x_rot = self._spatial_data_subset( x_rot, spatial_idx) spatial_subset_y_rot = self._spatial_data_subset( y_rot, spatial_idx) return mpl_handler.quiver_response(subset_lon, subset_lat, spatial_subset_x_rot, spatial_subset_y_rot, request, vectorscale) 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: cached_sg = from_ncfile(self.topology_file) lon_name, lat_name = cached_sg.face_coordinates lon_obj = getattr(cached_sg, lon_name) lat_obj = getattr(cached_sg, lat_name) centers = cached_sg.centers lon = centers[..., 0][lon_obj.center_slicing] lat = centers[..., 1][lat_obj.center_slicing] if request.GET['image_type'] == 'vectors': vectorstep = request.GET['vectorstep'] vectorscale = request.GET['vectorscale'] padding_factor = calc_safety_factor(vectorscale) spatial_idx_padding = calc_lon_lat_padding(lon, lat, padding_factor) else: spatial_idx_padding = 0.18 vectorstep = None spatial_idx = data_handler.lat_lon_subset_idx(lon, lat, lonmin=wgs84_bbox.minx, latmin=wgs84_bbox.miny, lonmax=wgs84_bbox.maxx, latmax=wgs84_bbox.maxy, padding=spatial_idx_padding ) subset_x = np.unique(spatial_idx[0]) subset_y = np.unique(spatial_idx[1]) if subset_x.shape == (0, ) and subset_y.shape == (0, ): return mpl_handler.empty_response() # return an empty tile if subset contains no data else: x_min_idx = subset_x.min() x_max_idx = subset_x.max() + 1 y_min_idx = subset_y.min() y_max_idx = subset_y.max() + 1 lonlat_mask = np.ones(lon.shape) lonlat_mask[spatial_idx[0], spatial_idx[1]] = 0 trimmed_lon = ma.masked_array(lon, mask=lonlat_mask).data[x_min_idx:x_max_idx:vectorstep, y_min_idx:y_max_idx:vectorstep] trimmed_lat = ma.masked_array(lat, mask=lonlat_mask).data[x_min_idx:x_max_idx:vectorstep, y_min_idx:y_max_idx:vectorstep] if isinstance(layer, Layer): data_obj = getattr(cached_sg, layer.access_name) raw_var = nc.variables[layer.access_name] raw_data = self._retrieve_data(request=request, nc_variable=raw_var, sg_variable=data_obj, layer=layer, subset_x=subset_x, subset_y=subset_y, time_index=time_index, vectorstep=1 ) # handle edge variables if data_obj.location is not None and 'edge' in data_obj.location: raw_data = avg_to_cell_center(raw_data, data_obj.center_axis) if request.GET['image_type'] == 'pcolor': return mpl_handler.pcolormesh_response(trimmed_lon, trimmed_lat, data=raw_data, request=request) elif request.GET['image_type'] == 'filledcontours': return mpl_handler.contourf_response(trimmed_lon, trimmed_lat, data=raw_data, request=request) else: raise NotImplementedError('Image type "{}" is not supported.'.format(request.GET['image_type'])) elif isinstance(layer, VirtualLayer): x_var = None y_var = None raw_vars = [] for l in layer.layers: data_obj = getattr(cached_sg, l.access_name) raw_var = nc.variables[l.access_name] raw_vars.append(raw_var) raw_data = self._retrieve_data(request=request, nc_variable=raw_var, sg_variable=data_obj, layer=layer, subset_x=subset_x, subset_y=subset_y, time_index=time_index, vectorstep=vectorstep ) raw_data = self._avg_to_cell_center(raw_data, data_obj.center_axis, vectorstep) if x_var is None: if data_obj.vector_axis and data_obj.vector_axis.lower() == 'x': x_var = raw_data elif data_obj.center_axis == 1: x_var = raw_data if y_var is None: if data_obj.vector_axis and data_obj.vector_axis.lower() == 'y': y_var = raw_data elif data_obj.center_axis == 0: y_var = raw_data if x_var is None or y_var is None: raise BaseException('Unable to determine x and y variables.') dim_lengths = [ len(v.dimensions) for v in raw_vars ] if len(list(set(dim_lengths))) != 1: raise AttributeError('One or both of the specified variables has screwed up dimensions.') if request.GET['image_type'] == 'vectors': angles = cached_sg.angles[lon_obj.center_slicing] trimmed_angles = ma.masked_array(angles, mask=lonlat_mask).data[x_min_idx:x_max_idx:vectorstep, y_min_idx:y_max_idx:vectorstep] # rotate vectors x_rot, y_rot = rotate_vectors(x_var, y_var, trimmed_angles) return mpl_handler.quiver_response(trimmed_lon, trimmed_lat, x_rot, y_rot, request, vectorscale ) 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 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']))
def get_tidal_vectors(self, layer, time, bbox, vector_scale=None, vector_step=None): vector_scale = vector_scale or 1 vector_step = vector_step or 1 with netCDF4.Dataset(self.topology_file) as nc: data_obj = nc.variables[layer.access_name] data_location = getattr(data_obj, 'location', 'node') mesh_name = data_obj.mesh ug = UGrid.from_nc_dataset(nc, 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] padding_factor = calc_safety_factor(vector_scale) padding = calc_lon_lat_padding(lon, lat, padding_factor) * vector_step spatial_idx = data_handler.ugrid_lat_lon_subset_idx(lon, lat, bbox=bbox, padding=padding) tnames = nc.get_variables_by_attributes(standard_name='tide_constituent')[0] tfreqs = nc.get_variables_by_attributes(standard_name='tide_frequency')[0] from utide import _ut_constants_fname from utide.utilities import loadmatbunch con_info = loadmatbunch(_ut_constants_fname)['const'] # Get names from the utide constant file utide_const_names = [ e.strip() for e in con_info['name'].tolist() ] # netCDF4-python is returning ugly arrays of bytes... names = [] for n in tnames[:]: z = ''.join([ x.decode('utf-8') for x in n.tolist() if x ]).strip() names.append(z) if 'STEADY' in names: names[names.index('STEADY')] = 'Z0' extract_names = list(set(utide_const_names).intersection(set(names))) ntides = data_obj.shape[data_obj.dimensions.index('ntides')] extract_mask = np.zeros(shape=(ntides,), dtype=bool) for n in extract_names: extract_mask[names.index(n)] = True if not spatial_idx.any() or not extract_mask.any(): e = np.ma.empty(0) return e, e, e, e ua = nc.variables['u'][extract_mask, spatial_idx] va = nc.variables['v'][extract_mask, spatial_idx] up = nc.variables['u_phase'][extract_mask, spatial_idx] vp = nc.variables['v_phase'][extract_mask, spatial_idx] freqs = tfreqs[extract_mask] omega = freqs * 3600 # Convert from radians/s to radians/hour. from utide.harmonics import FUV from matplotlib.dates import date2num v, u, f = FUV(t=np.array([date2num(time) + 366.1667]), tref=np.array([0]), lind=np.array([ utide_const_names.index(x) for x in extract_names ]), lat=55, # Reference latitude for 3rd order satellites (degrees) (55 is fine always) ngflgs=[0, 0, 0, 0]) # [NodsatLint NodsatNone GwchLint GwchNone] s = calendar.timegm(time.timetuple()) / 60 / 60. v, u, f = map(np.squeeze, (v, u, f)) v = v * 2 * np.pi # Convert phase in radians. u = u * 2 * np.pi # Convert phase in radians. U = (f * ua.T * np.cos(v + s * omega + u - up.T * np.pi / 180)).sum(axis=1) V = (f * va.T * np.cos(v + s * omega + u - vp.T * np.pi / 180)).sum(axis=1) return U, V, lon[spatial_idx], lat[spatial_idx]
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: with self.topology() as topo: lon = topo.get_variables_by_attributes(standard_name="longitude")[0][:] lat = topo.get_variables_by_attributes(standard_name="latitude")[0][:] if isinstance(layer, Layer): raw_var = nc.variables[layer.access_name] if len(raw_var.shape) == 4: z_index, z_value = self.nearest_z(layer, request.GET["elevation"]) raw_data = raw_var[time_index, z_index, :] elif len(raw_var.shape) == 3: raw_data = raw_var[time_index, :] elif len(raw_var.shape) == 2: raw_data = raw_var[:] else: raise BaseException("Unable to trim variable {0} data.".format(layer.access_name)) if request.GET["image_type"] == "pcolor": return mpl_handler.pcolormesh_response(lon, lat, data=raw_data, request=request) elif request.GET["image_type"] in ["filledhatches", "hatches", "filledcontours", "contours"]: return mpl_handler.contouring_response(lon, lat, data=raw_data, request=request) else: raise NotImplementedError('Image type "{}" is not supported.'.format(request.GET["image_type"])) elif isinstance(layer, VirtualLayer): x_var = None y_var = None raw_vars = [] for l in layer.layers: data_obj = getattr(cached_sg, l.access_name) raw_var = nc.variables[l.access_name] raw_vars.append(raw_var) if len(raw_var.shape) == 4: z_index, z_value = self.nearest_z(layer, request.GET["elevation"]) raw_data = raw_var[ time_index, z_index, data_obj.center_slicing[-2], data_obj.center_slicing[-1] ] elif len(raw_var.shape) == 3: raw_data = raw_var[time_index, data_obj.center_slicing[-2], data_obj.center_slicing[-1]] elif len(raw_var.shape) == 2: raw_data = raw_var[data_obj.center_slicing] else: raise BaseException("Unable to trim variable {0} data.".format(l.access_name)) raw_data = avg_to_cell_center(raw_data, data_obj.center_axis) if x_var is None: if data_obj.vector_axis and data_obj.vector_axis.lower() == "x": x_var = raw_data elif data_obj.center_axis == 1: x_var = raw_data if y_var is None: if data_obj.vector_axis and data_obj.vector_axis.lower() == "y": y_var = raw_data elif data_obj.center_axis == 0: y_var = raw_data if x_var is None or y_var is None: raise BaseException("Unable to determine x and y variables.") dim_lengths = [len(v.dimensions) for v in raw_vars] if len(list(set(dim_lengths))) != 1: raise AttributeError("One or both of the specified variables has screwed up dimensions.") if request.GET["image_type"] == "vectors": vectorstep = request.GET["vectorstep"] # don't do this if the vectorstep is 1; let's save a microsecond or two # it's identical to getting all the data if vectorstep > 1: data_dim = len(lon.shape) step_slice = ( np.s_[::vectorstep], ) * data_dim # make sure the vector step is used for all applicable dimensions lon = lon[step_slice] lat = lat[step_slice] x_var = x_var[step_slice] y_var = y_var[step_slice] vectorscale = request.GET["vectorscale"] padding_factor = calc_safety_factor(vectorscale) # figure out the average distance between lat/lon points # do the math after taking into the vectorstep if specified spatial_idx_padding = calc_lon_lat_padding(lon, lat, padding_factor) spatial_idx = data_handler.lat_lon_subset_idx( lon, lat, lonmin=wgs84_bbox.minx, latmin=wgs84_bbox.miny, lonmax=wgs84_bbox.maxx, latmax=wgs84_bbox.maxy, padding=spatial_idx_padding, ) subset_lon = self._spatial_data_subset(lon, spatial_idx) subset_lat = self._spatial_data_subset(lat, spatial_idx) spatial_subset_x_var = self._spatial_data_subset(x_var, spatial_idx) spatial_subset_y_var = self._spatial_data_subset(y_var, spatial_idx) return mpl_handler.quiver_response( subset_lon, subset_lat, spatial_subset_x_var, spatial_subset_y_var, request, vectorscale ) else: raise NotImplementedError('Image type "{}" is not supported.'.format(request.GET["image_type"]))