def wrap_computer_stats(query, dataset_name, lon_values): #determine east or west wrap if any(p > 180 for p in lon_values): #points to the east of the east dateline wrap_val=180 elif any(p < -180 for p in lon_values): #points to the west of the west dateline wrap_val=-180 else: raise ClientError(gettext("something went wrong. It seems you are trying to create a plot across the international date line." + "While we do support this function it must be done within 360 deg of the default map view. Try refreshing the page and try again")) variables = query.get('variable') if isinstance(variables, str) or isinstance(variables, unicode): variables = variables.split(',') area_data = Stats(query) area_data.set_lons(lon_values) area_data.split_area(wrap_val) area_data.names, area_data.inner_area.all_rings = get_names_rings(area_data.inner_area.area_query) _, area_data.outter_area.all_rings = get_names_rings(area_data.outter_area.area_query) area_data.inner_area.bounds = compute_bounds(area_data.inner_area.all_rings) area_data.outter_area.bounds = compute_bounds(area_data.outter_area.all_rings) area_data.inner_area.area_polys, area_data.inner_area.output = fill_polygons(area_data.inner_area.area_query) area_data.outter_area.area_polys, area_data.outter_area.output = fill_polygons(area_data.outter_area.area_query) area_data.inner_area.width = area_data.inner_area.bounds[3] - area_data.inner_area.bounds[1] area_data.outter_area.width = area_data.outter_area.bounds[3] - area_data.outter_area.bounds[1] spacing = math.floor((area_data.inner_area.width/(area_data.inner_area.width+area_data.outter_area.width))*50) area_data.inner_area.spaced_points = np.linspace(area_data.inner_area.bounds[1], area_data.inner_area.bounds[3], spacing) area_data.outter_area.spaced_points = np.linspace(area_data.outter_area.bounds[1], area_data.outter_area.bounds[3], 50-spacing) area_data.get_values(area_data.inner_area , dataset_name, variables) area_data.get_values(area_data.outter_area, dataset_name, variables) area_data.combine_stats() if int(area_data.area.stats[0]['variables'][0]['num']) == 0: raise ClientError(gettext("there are no datapoints in the area you selected. \ You may have selected a area on land or you may \ have an ara that is too small. \ Try selection a different area or \ a larger area")) return json.dumps(sorted(area_data.area.stats, key=itemgetter('name')))
def stats(dataset_name, query): try: area = query.get('area') data = None for idx, a in enumerate(area): if isinstance(a, str): sp = a.split('/', 1) if data is None: data = list_areas(sp[0], simplify=False) b = [x for x in data if x.get('key') == a] a = b[0] area[idx] = a points_lat =[] for p in area[0]['polygons'][0]: points_lat.append(p[1]) except Exception as e: raise ServerError(gettext("Unknown Error: you have tried something that we did not expect. \ Please try again or try something else. If you would like to report \ this error please contact [email protected]. ") + str(e)) if (max(points_lat)-min(points_lat))>360: raise ClientError(gettext("Error: you are trying to create a plot that is wider than the world. \ The desired information is ambiguous please select a smaller area and try again")) elif any((p > 180 or p < -180) for p in points_lat) and any(-180 <= p <= 180 for p in points_lat): #if there area points on both sides of the date line return wrap_computer_stats(query, dataset_name, points_lat) else: # no world wrap return computer_stats(area, query, dataset_name) raise ServerError(gettext("Unknown Error: you have tried something that we did not expect. \ Please try again or try something else. If you would like to report \ this error please contact [email protected]"))
def computer_stats(area, query, dataset_name): area_data = Stats(query) lon_values = [] for p in area_data.area.area_query[0]['polygons'][0]: p[1] = convert_to_bounded_lon(p[1]) lon_values.append(p[1]) area_data.set_lons(lon_values) variables = query.get('variable') if isinstance(variables, str) or isinstance(variables, unicode): variables = variables.split(',') area_data.names, area_data.area.all_rings = get_names_rings(area_data.area.area_query) area_data.area.bounds = compute_bounds(area_data.area.all_rings) area_data.area.area_polys, area_data.area.output = fill_polygons(area_data.area.area_query) area_data.area.spaced_points = np.linspace(area_data.area.bounds[1], area_data.area.bounds[3], 50) area_data.get_values(area_data.area, dataset_name, variables) if int(area_data.area.stats[0]['variables'][0]['num']) == 0: raise ClientError(gettext("there are no datapoints in the area you selected. \ You may have selected a area on land or you may \ have an ara that is too small. \ Try selection a different area or \ a larger area")) return json.dumps(sorted(area_data.area.stats, key=itemgetter('name')))
def load_data(self): with open_dataset(get_dataset_url(self.dataset_name)) as d: if self.time < 0: self.time += len(d.timestamps) time = np.clip(self.time, 0, len(d.timestamps) - 1) timestamp = d.timestamps[time] try: self.load_misc(d, self.variables) except IndexError as e: raise ClientError( gettext( "The selected variable(s) were not found in the dataset. \ Most likely, this variable is a derived product from existing dataset variables. \ Please select another variable. ") + str(e)) point_data, point_depths = self.get_data(d, self.variables, time) point_data = self.apply_scale_factors(point_data) self.variable_units, point_data = self.kelvin_to_celsius( self.variable_units, point_data) self.data = self.subtract_other(point_data) self.depths = point_depths self.timestamp = timestamp
def load_data(self): with open_dataset(self.dataset_config, timestamp=self.time, variable=self.variables) as ds: try: self.load_misc(ds, self.variables) except IndexError as e: raise ClientError( gettext( "The selected variable(s) were not found in the dataset. \ Most likely, this variable is a derived product from existing dataset variables. \ Please select another variable. ") + str(e)) point_data, point_depths = self.get_data(ds, self.variables, self.time) self.iso_timestamp = ds.nc_data.timestamp_to_iso_8601(self.time) self.data = self.subtract_other(point_data) self.depths = point_depths
from plotting.sound import SoundSpeedPlotter from plotting.stats import stats as areastats from plotting.stick import StickPlotter from plotting.timeseries import TimeseriesPlotter from plotting.transect import TransectPlotter from plotting.ts import TemperatureSalinityPlotter from utils.errors import APIError, ClientError, ErrorBase bp_v1_0 = Blueprint('api_v1_0', __name__) # ~~~~~~~~~~~~~~~~~~~~~~~ # API INTERFACE V1.0 # ~~~~~~~~~~~~~~~~~~~~~~~ MAX_CACHE = 315360000 FAILURE = ClientError("Bad API usage") @bp_v1_0.errorhandler(ErrorBase) def handle_error_v1(error): response = jsonify(error.to_dict()) response.status_code = error.status_code return response @bp_v1_0.route('/api/') def info_v1(): raise APIError( "This is the Ocean Navigator API - Additional Parameters are required to complete a request, help can be found at ..." )
def _map_plot(points, path=True, quiver=True): minlat = np.min(points[0, :]) maxlat = np.max(points[0, :]) minlon = np.min(points[1, :]) maxlon = np.max(points[1, :]) lat_d = max(maxlat - minlat, 20) lon_d = max(maxlon - minlon, 20) minlat -= lat_d / 3 minlon -= lon_d / 3 maxlat += lat_d / 3 maxlon += lon_d / 3 if minlat < -90: minlat = -90 if maxlat > 90: maxlat = 90 m = Basemap( llcrnrlon=minlon, llcrnrlat=minlat, urcrnrlon=maxlon, urcrnrlat=maxlat, lat_0=np.mean(points[0, :]), lon_0=np.mean(points[1, :]), resolution='c', projection='merc', rsphere=(6378137.00, 6356752.3142), ) if path: marker = '' if (np.round(points[1, :], 2) == np.round(points[1, 0], 2)).all() and \ (np.round(points[0, :], 2) == np.round(points[0, 0], 2)).all(): marker = '.' m.plot(points[1, :], points[0, :], latlon=True, color='r', linestyle='-', marker=marker) if quiver: qx, qy = m([points[1, -1]], [points[0, -1]]) qu = points[1, -1] - points[1, -2] qv = points[0, -1] - points[0, -2] qmag = np.sqrt(qu**2 + qv**2) qu /= qmag qv /= qmag m.quiver(qx, qy, qu, qv, pivot='tip', scale=8, width=0.25, minlength=0.25, color='r') else: for idx in range(0, points.shape[1]): m.plot(points[1, idx], points[0, idx], 'o', latlon=True, color='r') # Draw a realistic background "blue marble" try: m.bluemarble() m.drawparallels(np.arange(round(minlat), round(maxlat), round(lat_d / 1.5)), labels=[0, 1, 0, 0]) m.drawmeridians(np.arange(round(minlon), round(maxlon), round(lon_d / 1.5)), labels=[0, 0, 0, 1]) except: raise ClientError( "Plot is too close to pole. Changing your projection may solve this - Return to the main page, under settings, then Projection" )
def load_data(self): if isinstance(self.observation[0], numbers.Number): self.observation_variable_names = [] self.observation_variable_units = [] self.data = [] self.timestamps = [] self.observation_times = [] self.names = [] for idx, o in enumerate(self.observation): station = db.session.query(Station).get(o) observation = { 'time': station.time.isoformat(), 'longitude': station.longitude, 'latitude': station.latitude, } self.observation_time = station.time self.observation_times.append(station.time) if station.name: self.names.append(station.name) else: self.names.append( f"({station.latitude:.4f}, {station.longitude:.4f})") datatype_keys = [ k[0] for k in db.session.query( db.func.distinct(Sample.datatype_key)).filter( Sample.station == station).all() ] datatypes = db.session.query(DataType).filter( DataType.key.in_(datatype_keys)).order_by( DataType.key).all() observation['datatypes'] = [ f"{dt.name} [{dt.unit}]" for dt in datatypes ] data = [] for dt in datatypes: data.append( db.session.query(Sample.depth, Sample.value).filter( Sample.station == station, Sample.datatype == dt).all()) if idx == 0: self.observation_variable_names.append(dt.name) self.observation_variable_units.append(dt.unit) observation['data'] = np.ma.array(data) #.transpose() self.observation[idx] = observation self.points = [[o['latitude'], o['longitude']] for o in self.observation] cftime = datetime_to_timestamp(station.time, self.dataset_config.time_dim_units) with open_dataset( self.dataset_config, variable=self.variables, timestamp=int(cftime), nearest_timestamp=True, ) as dataset: ts = dataset.nc_data.timestamps observation_times = [] timestamps = [] for o in self.observation: observation_time = dateutil.parser.parse( o['time']).replace(tzinfo=pytz.UTC) observation_times.append(observation_time) deltas = [(x - observation_time).total_seconds() for x in ts] time = np.abs(deltas).argmin() timestamp = ts[time] timestamps.append(timestamp) try: self.load_misc(dataset, self.variables) except IndexError as e: raise ClientError( gettext( "The selected variable(s) were not found in the dataset. \ Most likely, this variable is a derived product from existing dataset variables. \ Please select another variable.") + str(e)) point_data, self.depths = self.get_data( dataset, self.variables, datetime_to_timestamp(timestamps[0], self.dataset_config.time_dim_units)) point_data = np.ma.array(point_data) self.data = point_data self.observation_time = observation_time self.observation_times = observation_times self.timestamps = timestamps self.timestamp = timestamp
def load_data(self): if isinstance(self.observation[0], numbers.Number): self.observation_variable_names = [] self.observation_variable_units = [] with Dataset(current_app.config["OBSERVATION_AGG_URL"], 'r') as ds: t = netcdftime.utime(ds['time'].units) for idx, o in enumerate(self.observation): observation = {} ts = t.num2date(ds['time'][o]).replace(tzinfo=pytz.UTC) observation['time'] = ts.isoformat() observation['longitude'] = ds['lon'][o] observation['latitude'] = ds['lat'][o] observation['depth'] = ds['z'][:] observation['depthunit'] = ds['z'].units observation['datatypes'] = [] data = [] for v in sorted(ds.variables): if v in ['z', 'lat', 'lon', 'profile', 'time']: continue var = ds[v] if var.datatype == '|S1': continue observation['datatypes'].append("%s [%s]" % ( var.long_name, var.units )) data.append(var[o, :]) if idx == 0: self.observation_variable_names.append( var.long_name) self.observation_variable_units.append(var.units) observation['data'] = np.ma.array(data).transpose() self.observation[idx] = observation self.points = [[o['latitude'], o['longitude']] for o in self.observation] with open_dataset(self.dataset_config) as dataset: ts = dataset.timestamps observation_times = [] timestamps = [] for o in self.observation: observation_time = dateutil.parser.parse(o['time']) observation_times.append(observation_time) deltas = [ (x.replace(tzinfo=pytz.UTC) - observation_time).total_seconds() for x in ts] time = np.abs(deltas).argmin() timestamp = ts[time] timestamps.append(timestamp) try: self.load_misc(dataset, self.variables) except IndexError as e: raise ClientError(gettext("The selected variable(s) were not found in the dataset. \ Most likely, this variable is a derived product from existing dataset variables. \ Please select another variable.") + str(e)) point_data, self.depths = self.get_data(dataset, self.variables, time) point_data = np.ma.array(point_data) point_data = self.apply_scale_factors(point_data) self.data = point_data self.observation_time = observation_time self.observation_times = observation_times self.timestamps = timestamps self.timestamp = timestamp
def parse_query(self, query): super().parse_query(query) if len(self.variables) > 1: raise ClientError( f"MapPlotter only supports 1 variable. Received multiple: {self.variables}" ) self.projection = query.get('projection') self.area = query.get('area') names = [] centroids = [] all_rings = [] data = None for idx, a in enumerate(self.area): if isinstance(a, str): sp = a.split('/', 1) if data is None: data = list_areas(sp[0], simplify=False) b = [x for x in data if x.get('key') == a] a = b[0] self.area[idx] = a else: self.points = copy.deepcopy(np.array(a['polygons'])) a['polygons'] = self.points.tolist() a['name'] = " " rings = [LinearRing(po) for po in a['polygons']] if len(rings) > 1: u = cascaded_union(rings) else: u = rings[0] all_rings.append(u) if a.get('name'): names.append(a.get('name')) centroids.append(u.centroid) nc = sorted(zip(names, centroids)) self.names = [n for (n, c) in nc] self.centroids = [c for (n, c) in nc] data = None if len(all_rings) > 1: combined = cascaded_union(all_rings) else: combined = all_rings[0] self.combined_area = combined combined = combined.envelope self.centroid = list(combined.centroid.coords)[0] self.bounds = combined.bounds self.show_bathymetry = bool(query.get('bathymetry')) self.show_area = bool(query.get('showarea')) self.quiver = query.get('quiver') self.contour = query.get('contour')
def load_data(self): distance = VincentyDistance() height = distance.measure( (self.bounds[0], self.centroid[1]), (self.bounds[2], self.centroid[1])) * 1000 * 1.25 width = distance.measure( (self.centroid[0], self.bounds[1]), (self.centroid[0], self.bounds[3])) * 1000 * 1.25 if self.projection == 'EPSG:32661': # north pole projection near_pole, covers_pole = self.pole_proximity(self.points[0]) blat = min(self.bounds[0], self.bounds[2]) blat = 5 * np.floor(blat / 5) if self.centroid[0] > 80 or near_pole or covers_pole: self.basemap = basemap.load_map( 'npstere', self.centroid, height, width, min(self.bounds[0], self.bounds[2])) else: self.basemap = basemap.load_map('lcc', self.centroid, height, width) elif self.projection == 'EPSG:3031': # south pole projection near_pole, covers_pole = self.pole_proximity(self.points[0]) blat = max(self.bounds[0], self.bounds[2]) blat = 5 * np.ceil(blat / 5) # is centerered close to the south pole if ((self.centroid[0] < -80 or self.bounds[1] < -80 or self.bounds[3] < -80) or covers_pole) or near_pole: self.basemap = basemap.load_map( 'spstere', self.centroid, height, width, max(self.bounds[0], self.bounds[2])) else: self.basemap = basemap.load_map('lcc', self.centroid, height, width) elif abs(self.centroid[1] - self.bounds[1]) > 90: height_bounds = [self.bounds[0], self.bounds[2]] width_bounds = [self.bounds[1], self.bounds[3]] height_buffer = (abs(height_bounds[1] - height_bounds[0])) * 0.1 width_buffer = (abs(width_bounds[0] - width_bounds[1])) * 0.1 if abs(width_bounds[1] - width_bounds[0]) > 360: raise ClientError( gettext( "You have requested an area that exceeds the width of the world. \ Thinking big is good but plots need to be less than 360 deg wide." )) if height_bounds[1] < 0: height_bounds[1] = height_bounds[1] + height_buffer else: height_bounds[1] = height_bounds[1] + height_buffer if height_bounds[0] < 0: height_bounds[0] = height_bounds[0] - height_buffer else: height_bounds[0] = height_bounds[0] - height_buffer new_width_bounds = [] new_width_bounds.append(width_bounds[0] - width_buffer) new_width_bounds.append(width_bounds[1] + width_buffer) if abs(new_width_bounds[1] - new_width_bounds[0]) > 360: width_buffer = np.floor( (360 - abs(width_bounds[1] - width_bounds[0])) / 2) new_width_bounds[0] = width_bounds[0] - width_buffer new_width_bounds[1] = width_bounds[1] + width_buffer if new_width_bounds[0] < -360: new_width_bounds[0] = -360 if new_width_bounds[1] > 720: new_width_bounds[1] = 720 self.basemap = basemap.load_map( 'merc', self.centroid, (height_bounds[0], height_bounds[1]), (new_width_bounds[0], new_width_bounds[1])) else: self.basemap = basemap.load_map('lcc', self.centroid, height, width) if self.basemap.aspect < 1: gridx = 500 gridy = int(500 * self.basemap.aspect) else: gridy = 500 gridx = int(500 / self.basemap.aspect) self.longitude, self.latitude = self.basemap.makegrid(gridx, gridy) variables_to_load = self.variables[:] # we don't want to change self,variables so copy it if self.__load_quiver(): variables_to_load.append(self.quiver['variable']) with open_dataset(self.dataset_config, variable=variables_to_load, timestamp=self.time) as dataset: self.variable_unit = self.get_variable_units( dataset, self.variables)[0] self.variable_name = self.get_variable_names( dataset, self.variables)[0] if self.cmap is None: self.cmap = colormap.find_colormap(self.variable_name) if self.depth == 'bottom': depth_value_map = 'Bottom' else: self.depth = np.clip(int(self.depth), 0, len(dataset.depths) - 1) depth_value = dataset.depths[self.depth] depth_value_map = depth_value data = [] var = dataset.variables[self.variables[0]] if self.filetype in ['csv', 'odv', 'txt']: d, depth_value_map = dataset.get_area(np.array( [self.latitude, self.longitude]), self.depth, self.time, self.variables[0], self.interp, self.radius, self.neighbours, return_depth=True) else: d = dataset.get_area(np.array([self.latitude, self.longitude]), self.depth, self.time, self.variables[0], self.interp, self.radius, self.neighbours) data.append(d) if self.filetype not in ['csv', 'odv', 'txt']: if len(var.dimensions) == 3: self.depth_label = "" elif self.depth == 'bottom': self.depth_label = " at Bottom" else: self.depth_label = " at " + \ str(int(np.round(depth_value_map))) + " m" self.data = data[0] quiver_data = [] # Store the quiver data on the same grid as the main variable. This # will only be used for CSV export. quiver_data_fullgrid = [] if self.__load_quiver(): var = dataset.variables[self.quiver['variable']] quiver_unit = self.dataset_config.variable[var].unit quiver_name = self.dataset_config.variable[var].name quiver_x_var = self.dataset_config.variable[ var].east_vector_component quiver_y_var = self.dataset_config.variable[ var].north_vector_component quiver_lon, quiver_lat = self.basemap.makegrid(50, 50) x_vals = dataset.get_area( np.array([quiver_lat, quiver_lon]), self.depth, self.time, quiver_x_var, self.interp, self.radius, self.neighbours, ) quiver_data.append(x_vals) y_vals = dataset.get_area( np.array([quiver_lat, quiver_lon]), self.depth, self.time, quiver_y_var, self.interp, self.radius, self.neighbours, ) quiver_data.append(y_vals) mag_data = dataset.get_area( np.array([quiver_lat, quiver_lon]), self.depth, self.time, self.quiver['variable'], self.interp, self.radius, self.neighbours, ) self.quiver_magnitude = mag_data # Get the quiver data on the same grid as the main # variable. x_vals = dataset.get_area( np.array([self.latitude, self.longitude]), self.depth, self.time, quiver_x_var, self.interp, self.radius, self.neighbours, ) quiver_data_fullgrid.append(x_vals) y_vals = dataset.get_area( np.array([self.latitude, self.longitude]), self.depth, self.time, quiver_y_var, self.interp, self.radius, self.neighbours, ) quiver_data_fullgrid.append(y_vals) self.quiver_name = self.get_variable_names( dataset, [self.quiver['variable']])[0] self.quiver_longitude = quiver_lon self.quiver_latitude = quiver_lat self.quiver_unit = quiver_unit self.quiver_data = quiver_data self.quiver_data_fullgrid = quiver_data_fullgrid if all([ dataset.variables[v].is_surface_only() for v in variables_to_load ]): self.depth = 0 contour_data = [] if self.contour is not None and \ self.contour['variable'] != '' and \ self.contour['variable'] != 'none': d = dataset.get_area( np.array([self.latitude, self.longitude]), self.depth, self.time, self.contour['variable'], self.interp, self.radius, self.neighbours, ) vc = self.dataset_config.variable[self.contour['variable']] contour_unit = vc.unit contour_name = vc.name contour_data.append(d) self.contour_unit = contour_unit self.contour_name = contour_name self.contour_data = contour_data self.timestamp = dataset.nc_data.timestamp_to_iso_8601(self.time) if self.compare: self.variable_name += " Difference" compare_config = DatasetConfig(self.compare['dataset']) with open_dataset(compare_config, variable=self.compare['variables'], timestamp=self.compare['time']) as dataset: data = [] for v in self.compare['variables']: var = dataset.variables[v] d = dataset.get_area( np.array([self.latitude, self.longitude]), self.compare['depth'], self.compare['time'], v, self.interp, self.radius, self.neighbours, ) data.append(d) data = data[0] self.data -= data # Load bathymetry data self.bathymetry = overlays.bathymetry(self.basemap, self.latitude, self.longitude, blur=2) if self.depth != 'bottom' and self.depth != 0: if quiver_data: quiver_bathymetry = overlays.bathymetry( self.basemap, quiver_lat, quiver_lon) self.data[np.where( self.bathymetry < depth_value_map)] = np.ma.masked for d in self.quiver_data: d[np.where(quiver_bathymetry < depth_value)] = np.ma.masked for d in self.contour_data: d[np.where(self.bathymetry < depth_value_map)] = np.ma.masked else: mask = maskoceans(self.longitude, self.latitude, self.data, True, 'h', 1.25).mask self.data[~mask] = np.ma.masked for d in self.quiver_data: mask = maskoceans(self.quiver_longitude, self.quiver_latitude, d).mask d[~mask] = np.ma.masked for d in contour_data: mask = maskoceans(self.longitude, self.latitude, d).mask d[~mask] = np.ma.masked if self.area and self.filetype in ['csv', 'odv', 'txt', 'geotiff']: area_polys = [] for a in self.area: rings = [LinearRing(p) for p in a['polygons']] innerrings = [LinearRing(p) for p in a['innerrings']] polygons = [] for r in rings: inners = [] for ir in innerrings: if r.contains(ir): inners.append(ir) polygons.append(Poly(r, inners)) area_polys.append(MultiPolygon(polygons)) points = [ Point(p) for p in zip(self.latitude.ravel(), self.longitude.ravel()) ] indicies = [] for a in area_polys: indicies.append( np.where( list(map(lambda p, poly=a: poly.contains(p), points)))[0]) indicies = np.unique(np.array(indicies).ravel()) newmask = np.ones(self.data.shape, dtype=bool) newmask[np.unravel_index(indicies, newmask.shape)] = False self.data.mask |= newmask self.depth_value_map = depth_value_map
def get_values(self, area_info, dataset_name, variables): config = DatasetConfig(dataset_name) with open_dataset(config) as dataset: if self.time is None or (type(self.time) == str and len(self.time) == 0): time = -1 else: time = int(self.time) if time < 0: time += len(dataset.nc_data.timestamps) time = np.clip(time, 0, len(dataset.nc_data.timestamps) - 1) depth = 0 depthm = 0 if self.depth: if self.depth == 'bottom': depth = 'bottom' depthm = 'Bottom' if len(self.depth) > 0 and \ self.depth != 'bottom': depth = int(self.depth) depth = np.clip(depth, 0, len(dataset.depths) - 1) depthm = dataset.depths[depth] lat, lon = np.meshgrid( np.linspace(area_info.bounds[0], area_info.bounds[2], 50), area_info.spaced_points ) output_fmtstr = "%6.5g" for v_idx, v in enumerate(variables): var = dataset.variables[v] variable_name = config.variable[var].name variable_unit = config.variable[var].unit lat, lon, d = dataset.get_raw_point( lat.ravel(), lon.ravel(), depth, time, v ) lon[np.where(lon > 180)] -= 360 if len(var.dimensions) == 3: variable_depth = "" elif depth == 'bottom': variable_depth = "(@ Bottom)" else: variable_depth = "(@%d m)" % np.round(depthm) points = [Point(p) for p in zip(lat.values.ravel(), lon.values.ravel())] for i, a in enumerate(area_info.area_query): indices = np.where( map( lambda p, poly=area_info.area_polys[i]: poly.contains(p), points ) ) selection = np.ma.array(d.values.ravel()[indices]) if len(selection) > 0 and not selection.mask.all(): area_info.output[i]['variables'].append({ 'name': ("%s %s" % (variable_name, variable_depth)).strip(), 'unit': variable_unit, 'min': output_fmtstr % ( np.ma.amin(selection).astype(float) ), 'max': output_fmtstr % ( np.ma.amax(selection).astype(float) ), 'mean': output_fmtstr % ( np.ma.mean(selection).astype(float) ), 'median': output_fmtstr % ( np.ma.median(selection).astype(float) ), 'stddev': output_fmtstr % ( np.ma.std(selection).astype(float) ), 'num': "%d" % selection.count(), }) else: area_info.output[i]['variables'].append({ 'name': ("%s %s" % (variable_name, variable_depth)).strip(), 'unit': variable_unit, 'min': gettext("No Data"), 'max': gettext("No Data"), 'mean': gettext("No Data"), 'median': gettext("No Data"), 'stddev': gettext("No Data"), 'num': "0", }) ClientError(gettext("there are no datapoints in the area you selected. \ you may have selected a area on land or you may \ have an ara that is smallenough to fit between \ the datapoints try selection a different area or \ a larger area")) area_info.stats = area_info.output return raise ServerError(gettext("An Error has occurred. When opening the dataset. \ Please try again or try a different dataset. \ If you would like to report this error please \ contact [email protected]"))
def load_data(self): width_scale = 1.25 height_scale = 1.25 if self.projection == "EPSG:32661": # north pole projection near_pole, covers_pole = self.pole_proximity(self.points[0]) blat = min(self.bounds[0], self.bounds[2]) blat = 5 * np.floor(blat / 5) if self.centroid[0] > 80 or near_pole or covers_pole: self.plot_projection = ccrs.Stereographic( central_latitude=self.centroid[0], central_longitude=self.centroid[1], ) width_scale = 1.5 else: self.plot_projection = ccrs.LambertConformal( central_latitude=self.centroid[0], central_longitude=self.centroid[1], ) elif self.projection == "EPSG:3031": # south pole projection near_pole, covers_pole = self.pole_proximity(self.points[0]) blat = max(self.bounds[0], self.bounds[2]) blat = 5 * np.ceil(blat / 5) # is centerered close to the south pole if ((self.centroid[0] < -80 or self.bounds[1] < -80 or self.bounds[3] < -80) or covers_pole) or near_pole: self.plot_projection = ccrs.Stereographic( central_latitude=self.centroid[0], central_longitude=self.centroid[1], ) width_scale = 1.5 else: self.plot_projection = ccrs.LambertConformal( central_latitude=self.centroid[0], central_longitude=self.centroid[1], ) elif abs(self.centroid[1] - self.bounds[1]) > 90: if abs(self.bounds[3] - self.bounds[1]) > 360: raise ClientError( gettext( "You have requested an area that exceeds the width \ of the world. Thinking big is good but plots need to \ be less than 360 deg wide.")) self.plot_projection = ccrs.Mercator( central_longitude=self.centroid[1]) else: self.plot_projection = ccrs.LambertConformal( central_latitude=self.centroid[0], central_longitude=self.centroid[1]) proj_bounds = self.plot_projection.transform_points( self.pc_projection, np.array([self.bounds[1], self.bounds[3]]), np.array([self.bounds[0], self.bounds[2]]), ) proj_size = np.diff(proj_bounds, axis=0) width = proj_size[0][0] * width_scale height = proj_size[0][1] * height_scale aspect_ratio = height / width if aspect_ratio < 1: gridx = 500 gridy = int(500 * aspect_ratio) else: gridy = 500 gridx = int(500 / aspect_ratio) self.plot_res = basemap.get_resolution(height, width) x_grid, y_grid, self.plot_extent = cimg_transform.mesh_projection( self.plot_projection, gridx, gridy, x_extents=(-width / 2, width / 2), y_extents=(-height / 2, height / 2), ) latlon_grid = self.pc_projection.transform_points( self.plot_projection, x_grid, y_grid) self.longitude = latlon_grid[:, :, 0] self.latitude = latlon_grid[:, :, 1] variables_to_load = self.variables[:] # we don't want to change self,variables so copy it if self.__load_quiver(): variables_to_load.append(self.quiver["variable"]) with open_dataset(self.dataset_config, variable=variables_to_load, timestamp=self.time) as dataset: self.variable_unit = self.get_variable_units( dataset, self.variables)[0] self.variable_name = self.get_variable_names( dataset, self.variables)[0] if self.cmap is None: self.cmap = colormap.find_colormap(self.variable_name) if self.depth == "bottom": depth_value_map = "Bottom" else: self.depth = np.clip(int(self.depth), 0, len(dataset.depths) - 1) depth_value = dataset.depths[self.depth] depth_value_map = depth_value data = [] var = dataset.variables[self.variables[0]] if self.filetype in ["csv", "odv", "txt"]: d, depth_value_map = dataset.get_area( np.array([self.latitude, self.longitude]), self.depth, self.time, self.variables[0], self.interp, self.radius, self.neighbours, return_depth=True, ) else: d = dataset.get_area( np.array([self.latitude, self.longitude]), self.depth, self.time, self.variables[0], self.interp, self.radius, self.neighbours, ) data.append(d) if self.filetype not in ["csv", "odv", "txt"]: if len(var.dimensions) == 3: self.depth_label = "" elif self.depth == "bottom": self.depth_label = " at Bottom" else: self.depth_label = (" at " + str(int(np.round(depth_value_map))) + " m") self.data = data[0] quiver_data = [] # Store the quiver data on the same grid as the main variable. This # will only be used for CSV export. quiver_data_fullgrid = [] if self.__load_quiver(): var = dataset.variables[self.quiver["variable"]] quiver_unit = self.dataset_config.variable[var].unit quiver_name = self.dataset_config.variable[var].name quiver_x_var = self.dataset_config.variable[ var].east_vector_component quiver_y_var = self.dataset_config.variable[ var].north_vector_component quiver_x, quiver_y, _ = cimg_transform.mesh_projection( self.plot_projection, 50, 50, self.plot_extent[:2], self.plot_extent[2:], ) quiver_coords = self.pc_projection.transform_points( self.plot_projection, quiver_x, quiver_y) quiver_lon = quiver_coords[:, :, 0] quiver_lat = quiver_coords[:, :, 1] x_vals = dataset.get_area( np.array([quiver_lat, quiver_lon]), self.depth, self.time, quiver_x_var, self.interp, self.radius, self.neighbours, ) quiver_data.append(x_vals) y_vals = dataset.get_area( np.array([quiver_lat, quiver_lon]), self.depth, self.time, quiver_y_var, self.interp, self.radius, self.neighbours, ) quiver_data.append(y_vals) mag_data = dataset.get_area( np.array([quiver_lat, quiver_lon]), self.depth, self.time, self.quiver["variable"], self.interp, self.radius, self.neighbours, ) self.quiver_magnitude = mag_data # Get the quiver data on the same grid as the main # variable. x_vals = dataset.get_area( np.array([self.latitude, self.longitude]), self.depth, self.time, quiver_x_var, self.interp, self.radius, self.neighbours, ) quiver_data_fullgrid.append(x_vals) y_vals = dataset.get_area( np.array([self.latitude, self.longitude]), self.depth, self.time, quiver_y_var, self.interp, self.radius, self.neighbours, ) quiver_data_fullgrid.append(y_vals) self.quiver_name = self.get_variable_names( dataset, [self.quiver["variable"]])[0] self.quiver_longitude = quiver_lon self.quiver_latitude = quiver_lat self.quiver_unit = quiver_unit self.quiver_data = quiver_data self.quiver_data_fullgrid = quiver_data_fullgrid if all([ dataset.variables[v].is_surface_only() for v in variables_to_load ]): self.depth = 0 contour_data = [] if (self.contour is not None and self.contour["variable"] != "" and self.contour["variable"] != "none"): d = dataset.get_area( np.array([self.latitude, self.longitude]), self.depth, self.time, self.contour["variable"], self.interp, self.radius, self.neighbours, ) vc = self.dataset_config.variable[self.contour["variable"]] contour_unit = vc.unit contour_name = vc.name contour_data.append(d) self.contour_unit = contour_unit self.contour_name = contour_name self.contour_data = contour_data self.timestamp = dataset.nc_data.timestamp_to_iso_8601(self.time) if self.compare: self.variable_name += " Difference" compare_config = DatasetConfig(self.compare["dataset"]) with open_dataset( compare_config, variable=self.compare["variables"], timestamp=self.compare["time"], ) as dataset: data = [] for v in self.compare["variables"]: var = dataset.variables[v] d = dataset.get_area( np.array([self.latitude, self.longitude]), self.compare["depth"], self.compare["time"], v, self.interp, self.radius, self.neighbours, ) data.append(d) data = data[0] self.data -= data # Load bathymetry data self.bathymetry = overlays.bathymetry(self.latitude, self.longitude, blur=2) if self.depth != "bottom" and self.depth != 0: if quiver_data: quiver_bathymetry = overlays.bathymetry(quiver_lat, quiver_lon) self.data[np.where( self.bathymetry < depth_value_map)] = np.ma.masked for d in self.quiver_data: d[np.where(quiver_bathymetry < depth_value)] = np.ma.masked for d in self.contour_data: d[np.where(self.bathymetry < depth_value_map)] = np.ma.masked else: mask = maskoceans(self.longitude, self.latitude, self.data, True, "h", 1.25).mask self.data[~mask] = np.ma.masked for d in self.quiver_data: mask = maskoceans(self.quiver_longitude, self.quiver_latitude, d).mask d[~mask] = np.ma.masked for d in contour_data: mask = maskoceans(self.longitude, self.latitude, d).mask d[~mask] = np.ma.masked if self.area and self.filetype in ["csv", "odv", "txt", "geotiff"]: area_polys = [] for a in self.area: rings = [LinearRing(p) for p in a["polygons"]] innerrings = [LinearRing(p) for p in a["innerrings"]] polygons = [] for r in rings: inners = [] for ir in innerrings: if r.contains(ir): inners.append(ir) polygons.append(Poly(r, inners)) area_polys.append(MultiPolygon(polygons)) points = [ Point(p) for p in zip(self.latitude.ravel(), self.longitude.ravel()) ] indicies = [] for a in area_polys: indicies.append( np.where( list(map(lambda p, poly=a: poly.contains(p), points)))[0]) indicies = np.unique(np.array(indicies).ravel()) newmask = np.ones(self.data.shape, dtype=bool) newmask[np.unravel_index(indicies, newmask.shape)] = False self.data.mask |= newmask self.depth_value_map = depth_value_map
def load_data(self): distance = VincentyDistance() height = distance.measure( (self.bounds[0], self.centroid[1]), (self.bounds[2], self.centroid[1])) * 1000 * 1.25 width = distance.measure( (self.centroid[0], self.bounds[1]), (self.centroid[0], self.bounds[3])) * 1000 * 1.25 if self.projection == 'EPSG:32661': # north pole projection near_pole, covers_pole = self.pole_proximity(self.points[0]) blat = min(self.bounds[0], self.bounds[2]) blat = 5 * np.floor(blat / 5) if self.centroid[0] > 80 or near_pole or covers_pole: self.basemap = basemap.load_map( 'npstere', self.centroid, height, width, min(self.bounds[0], self.bounds[2])) else: self.basemap = basemap.load_map('lcc', self.centroid, height, width) elif self.projection == 'EPSG:3031': # south pole projection near_pole, covers_pole = self.pole_proximity(self.points[0]) blat = max(self.bounds[0], self.bounds[2]) blat = 5 * np.ceil(blat / 5) if ((self.centroid[0] < -80 or self.bounds[1] < -80 or self.bounds[3] < -80) or covers_pole ) or near_pole: # is centerered close to the south pole self.basemap = basemap.load_map( 'spstere', self.centroid, height, width, max(self.bounds[0], self.bounds[2])) else: self.basemap = basemap.load_map('lcc', self.centroid, height, width) elif abs(self.centroid[1] - self.bounds[1]) > 90: height_bounds = [self.bounds[0], self.bounds[2]] width_bounds = [self.bounds[1], self.bounds[3]] height_buffer = (abs(height_bounds[1] - height_bounds[0])) * 0.1 width_buffer = (abs(width_bounds[0] - width_bounds[1])) * 0.1 if abs(width_bounds[1] - width_bounds[0]) > 360: raise ClientError( gettext( "You have requested an area that exceeds the width of the world. \ Thinking big is good but plots need to be less than 360 deg wide." )) if height_bounds[1] < 0: height_bounds[1] = height_bounds[1] + height_buffer else: height_bounds[1] = height_bounds[1] + height_buffer if height_bounds[0] < 0: height_bounds[0] = height_bounds[0] - height_buffer else: height_bounds[0] = height_bounds[0] - height_buffer new_width_bounds = [] new_width_bounds.append(width_bounds[0] - width_buffer) new_width_bounds.append(width_bounds[1] + width_buffer) if abs(new_width_bounds[1] - new_width_bounds[0]) > 360: width_buffer = np.floor( (360 - abs(width_bounds[1] - width_bounds[0])) / 2) new_width_bounds[0] = width_bounds[0] - width_buffer new_width_bounds[1] = width_bounds[1] + width_buffer if new_width_bounds[0] < -360: new_width_bounds[0] = -360 if new_width_bounds[1] > 720: new_width_bounds[1] = 720 self.basemap = basemap.load_map( 'merc', self.centroid, (height_bounds[0], height_bounds[1]), (new_width_bounds[0], new_width_bounds[1])) else: self.basemap = basemap.load_map('lcc', self.centroid, height, width) if self.basemap.aspect < 1: gridx = 500 gridy = int(500 * self.basemap.aspect) else: gridy = 500 gridx = int(500 / self.basemap.aspect) self.longitude, self.latitude = self.basemap.makegrid(gridx, gridy) with open_dataset(get_dataset_url(self.dataset_name)) as dataset: if self.time < 0: self.time += len(dataset.timestamps) self.time = np.clip(self.time, 0, len(dataset.timestamps) - 1) self.variable_unit = self.get_variable_units( dataset, self.variables)[0] self.variable_name = self.get_variable_names( dataset, self.variables)[0] scale_factor = self.get_variable_scale_factors( dataset, self.variables)[0] if self.cmap is None: if len(self.variables) == 1: self.cmap = colormap.find_colormap(self.variable_name) else: self.cmap = colormap.colormaps.get('speed') if len(self.variables) == 2: self.variable_name = self.vector_name(self.variable_name) if self.depth == 'bottom': depth_value = 'Bottom' else: self.depth = np.clip(int(self.depth), 0, len(dataset.depths) - 1) depth_value = dataset.depths[self.depth] data = [] allvars = [] for v in self.variables: var = dataset.variables[v] allvars.append(v) if self.filetype in ['csv', 'odv', 'txt']: d, depth_value = dataset.get_area(np.array( [self.latitude, self.longitude]), self.depth, self.time, v, self.interp, self.radius, self.neighbours, return_depth=True) else: d = dataset.get_area( np.array([self.latitude, self.longitude]), self.depth, self.time, v, self.interp, self.radius, self.neighbours) d = np.multiply(d, scale_factor) self.variable_unit, d = self.kelvin_to_celsius( self.variable_unit, d) data.append(d) if self.filetype not in ['csv', 'odv', 'txt']: if len(var.dimensions) == 3: self.depth_label = "" elif self.depth == 'bottom': self.depth_label = " at Bottom" else: self.depth_label = " at " + \ str(int(np.round(depth_value))) + " m" if len(data) == 2: data[0] = np.sqrt(data[0]**2 + data[1]**2) self.data = data[0] quiver_data = [] # Store the quiver data on the same grid as the main variable. This # will only be used for CSV export. quiver_data_fullgrid = [] if self.quiver is not None and \ self.quiver['variable'] != '' and \ self.quiver['variable'] != 'none': for v in self.quiver['variable'].split(','): allvars.append(v) var = dataset.variables[v] quiver_unit = get_variable_unit(self.dataset_name, var) quiver_name = get_variable_name(self.dataset_name, var) quiver_lon, quiver_lat = self.basemap.makegrid(50, 50) d = dataset.get_area( np.array([quiver_lat, quiver_lon]), self.depth, self.time, v, self.interp, self.radius, self.neighbours, ) quiver_data.append(d) # Get the quiver data on the same grid as the main # variable. d = dataset.get_area( np.array([self.latitude, self.longitude]), self.depth, self.time, v, self.interp, self.radius, self.neighbours, ) quiver_data_fullgrid.append(d) self.quiver_name = self.vector_name(quiver_name) self.quiver_longitude = quiver_lon self.quiver_latitude = quiver_lat self.quiver_unit = quiver_unit self.quiver_data = quiver_data self.quiver_data_fullgrid = quiver_data_fullgrid if all( [len(dataset.variables[v].dimensions) == 3 for v in allvars]): self.depth = 0 contour_data = [] if self.contour is not None and \ self.contour['variable'] != '' and \ self.contour['variable'] != 'none': d = dataset.get_area( np.array([self.latitude, self.longitude]), self.depth, self.time, self.contour['variable'], self.interp, self.radius, self.neighbours, ) contour_unit = get_variable_unit( self.dataset_name, dataset.variables[self.contour['variable']]) contour_name = get_variable_name( self.dataset_name, dataset.variables[self.contour['variable']]) contour_factor = get_variable_scale_factor( self.dataset_name, dataset.variables[self.contour['variable']]) contour_unit, d = self.kelvin_to_celsius(contour_unit, d) d = np.multiply(d, contour_factor) contour_data.append(d) self.contour_unit = contour_unit self.contour_name = contour_name self.contour_data = contour_data self.timestamp = dataset.timestamps[self.time] if self.compare: self.variable_name += " Difference" with open_dataset(get_dataset_url( self.compare['dataset'])) as dataset: data = [] for v in self.compare['variables']: var = dataset.variables[v] d = dataset.get_area( np.array([self.latitude, self.longitude]), self.compare['depth'], self.compare['time'], v, self.interp, self.radius, self.neighbours, ) data.append(d) if len(data) == 2: data = np.sqrt(data[0]**2 + data[1]**2) else: data = data[0] u, data = self.kelvin_to_celsius( dataset.variables[self.compare['variables'][0]].unit, data) self.data -= data # Load bathymetry data self.bathymetry = overlays.bathymetry(self.basemap, self.latitude, self.longitude, blur=2) if self.depth != 'bottom' and self.depth != 0: if len(quiver_data) > 0: quiver_bathymetry = overlays.bathymetry( self.basemap, quiver_lat, quiver_lon) self.data[np.where(self.bathymetry < depth_value)] = np.ma.masked for d in self.quiver_data: d[np.where(quiver_bathymetry < depth_value)] = np.ma.masked for d in self.contour_data: d[np.where(self.bathymetry < depth_value)] = np.ma.masked else: mask = maskoceans(self.longitude, self.latitude, self.data, True, 'h', 1.25).mask self.data[~mask] = np.ma.masked for d in self.quiver_data: mask = maskoceans(self.quiver_longitude, self.quiver_latitude, d).mask d[~mask] = np.ma.masked for d in contour_data: mask = maskoceans(self.longitude, self.latitude, d).mask d[~mask] = np.ma.masked if self.area and self.filetype in ['csv', 'odv', 'txt', 'geotiff']: area_polys = [] for a in self.area: rings = [LinearRing(p) for p in a['polygons']] innerrings = [LinearRing(p) for p in a['innerrings']] polygons = [] for r in rings: inners = [] for ir in innerrings: if r.contains(ir): inners.append(ir) polygons.append(Poly(r, inners)) area_polys.append(MultiPolygon(polygons)) points = [ Point(p) for p in zip(self.latitude.ravel(), self.longitude.ravel()) ] indicies = [] for a in area_polys: indicies.append( np.where( list(map(lambda p, poly=a: poly.contains(p), points)))[0]) indicies = np.unique(np.array(indicies).ravel()) newmask = np.ones(self.data.shape, dtype=bool) newmask[np.unravel_index(indicies, newmask.shape)] = False self.data.mask |= newmask self.depth_value = depth_value
def load_data(self): """ Calculates and returns the depth, depth-value, and depth unit from a given dataset Args: depth: Stored depth information (self.depth or self.compare['depth']) clip_length: How many depth values to clip (usually len(dataset.depths) - 1) dataset: Opened dataset Returns: (depth, depth_value, depth_unit) """ def find_depth(depth, clip_length, dataset): depth_value = 0 depth_unit = "m" if depth: if depth == 'bottom': depth_value = 'Bottom' depth_unit = '' return (depth, depth_value, depth_unit) else: depth = np.clip(int(depth), 0, clip_length) depth_value = np.round(dataset.depths[depth]) depth_unit = "m" return (depth, depth_value, depth_unit) return (depth, depth_value, depth_unit) # Load left/Main Map with open_dataset(self.dataset_config) as dataset: latvar, lonvar = utils.get_latlon_vars(dataset) self.depth, self.depth_value, self.depth_unit = find_depth( self.depth, len(dataset.depths) - 1, dataset) self.fix_startend_times(dataset, self.starttime, self.endtime) time = list(range(self.starttime, self.endtime + 1)) if len(self.variables) > 1: v = [] for name in self.variables: self.path_points, self.distance, t, value = dataset.get_path( self.points, self.depth, time, name) v.append(value**2) value = np.sqrt(np.ma.sum(v, axis=0)) self.variable_name = self.get_vector_variable_name( dataset, self.variables) else: self.path_points, self.distance, t, value = dataset.get_path( self.points, self.depth, time, self.variables[0]) self.variable_name = self.get_variable_names( dataset, self.variables)[0] variable_units = self.get_variable_units(dataset, self.variables) scale_factors = self.get_variable_scale_factors( dataset, self.variables) self.variable_unit = variable_units[0] self.data = value self.times = dataset.timestamps[self.starttime:self.endtime + 1] self.data = np.multiply(self.data, scale_factors[0]) self.data = self.data.transpose() # Get colourmap if self.cmap is None: self.cmap = colormap.find_colormap(self.variable_name) # Load data sent from Right Map (if in compare mode) if self.compare: compare_config = DatasetConfig(self.compare['dataset']) with open_dataset(compare_config) as dataset: latvar, lonvar = utils.get_latlon_vars(dataset) self.compare['depth'], self.compare[ 'depth_value'], self.compare['depth_unit'] = find_depth( self.compare['depth'], len(dataset.depths) - 1, dataset) self.fix_startend_times(dataset, self.compare['starttime'], self.compare['endtime']) time = list( range(self.compare['starttime'], self.compare['endtime'] + 1)) if len(self.compare['variables']) > 1: v = [] for name in self.compare['variables']: path, distance, t, value = dataset.get_path( self.points, self.compare['depth'], time, name) v.append(value**2) value = np.sqrt(np.ma.sum(v, axis=0)) self.compare['variable_name'] = \ self.get_vector_variable_name(dataset, self.compare['variables']) else: path, distance, t, value = dataset.get_path( self.points, self.compare['depth'], time, self.compare['variables'][0]) self.compare['variable_name'] = self.get_variable_names( dataset, self.compare['variables'])[0] # Colourmap if (self.compare['colormap'] == 'default'): self.compare['colormap'] = colormap.find_colormap( self.compare['variable_name']) else: self.compare['colormap'] = colormap.find_colormap( self.compare['colormap']) variable_units = self.get_variable_units( dataset, self.compare['variables']) scale_factors = self.get_variable_scale_factors( dataset, self.compare['variables']) self.compare['variable_unit'] = variable_units[0] self.compare['data'] = value self.compare['times'] = dataset.timestamps[ self.compare['starttime']:self.compare['endtime'] + 1] self.compare['data'] = np.multiply(self.compare['data'], scale_factors[0]) self.compare['data'] = self.compare['data'].transpose() # Comparison over different time ranges makes no sense if self.starttime != self.compare['starttime'] or\ self.endtime != self.compare['endtime']: raise ClientError( gettext( "Please ensure the Start Time and End Time for the Left and Right maps are identical." ))