Пример #1
0
    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']))
Пример #2
0
    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]
Пример #3
0
    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']))
Пример #4
0
    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']))
Пример #5
0
    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']))
Пример #6
0
    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']))
Пример #7
0
    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']))
Пример #8
0
    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']))
Пример #9
0
    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]
Пример #10
0
    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"]))