def inside_outline(fault): def inside(tri): return rotated_outline.contains(Polygon(rotated_xyz[tri])) # Iterate through triangles in internal coords and select those inside # outline the non-convex outline of the fault... x, y, z = fault._internal_xyz.T rotated_tri = Triangulation(x, y) rotated_xyz = fault._internal_xyz rotated_outline = Polygon(fault._rotated_outline) return np.array([inside(tri) for tri in rotated_tri.triangle_nodes])
def tri(self): try: from matplotlib.delaunay import Triangulation except ImportError: raise ImportError('Maplotlib is required for geoprobe.swfault ' 'triangulation') try: return self._tri except AttributeError: x, y, z = self._internal_xyz.T self._tri = Triangulation(x, y) self._tri.x = self.x self._tri.y = self.y return self._tri
def calc_geodesic_distances(xyz, res=.03): """ Calculates pairwise geodesic distances. Note that we generate the graph by projecting to 2D """ x,y = xyz[:,:2].T tri = Triangulation(x,y) G = nx.Graph() #G.add_nodes_from(xrange(len(xyz))) for i0 in xrange(len(xyz)): G.add_node(i0) for (i0, i1) in tri.edge_db: dist = np.linalg.norm(xyz[i1] - xyz[i0]) if dist < res: G.add_edge(i0, i1, weight = np.linalg.norm(xyz[i1] - xyz[i0])) distmat = np.asarray(nx.floyd_warshall_numpy(G)) finitevals = distmat[np.isfinite(distmat)] distmat[~np.isfinite(distmat)] = finitevals.max() * 3 return distmat
xi, yi, zi = xxr.flat[ij], yyr.flat[ij], zzr.flat[ij] ij2 = N.unique((N.random.rand(50)*zzr.size).astype('i')) xo2, yo2 = xxr.flat[ij2], yyr.flat[ij2] # CDAT natgrid from nat import Natgrid I = Natgrid(xi, yi, xr, yr) I.ext = 1 I.igr = 1 zzoc = I.rgrd(zi).T # Matplotlib / R. Kern from matplotlib.delaunay import Triangulation tri = Triangulation(xi,yi) I = tri.nn_extrapolator(zi) zzok = I(xxr,yyr) # Plot from matplotlib import pyplot as P P.figure(figsize=(4, 11)) P.subplot(311) P.pcolormesh(zzr, **vminmax) P.title('Original') stdref = zzr.std() P.subplot(312) P.pcolormesh(zzoc, **vminmax) P.title('CDAT Natgrid: err=%i%%'%((zzr-zzoc).std()*100/stdref)) P.subplot(313)
ij2 = N.unique((N.random.rand(50) * zzr.size).astype('i')) xo2, yo2 = xxr.flat[ij2], yyr.flat[ij2] # CDAT natgrid from nat import Natgrid I = Natgrid(xi, yi, xr, yr) I.ext = 1 I.igr = 1 zzoc = I.rgrd(zi).T # Matplotlib / R. Kern from matplotlib.delaunay import Triangulation tri = Triangulation(xi, yi) I = tri.nn_extrapolator(zi) zzok = I(xxr, yyr) # Plot from matplotlib import pyplot as P P.figure(figsize=(4, 11)) P.subplot(311) P.pcolormesh(zzr, **vminmax) P.title('Original') stdref = zzr.std() P.subplot(312) P.pcolormesh(zzoc, **vminmax) P.title('CDAT Natgrid: err=%i%%' % ((zzr - zzoc).std() * 100 / stdref)) P.subplot(313)
ij2 = N.unique((N.random.rand(100)*zzr.size).astype('i')) xo, yo = xxr.flat[ij2], yyr.flat[ij2] # Sans VACUMM # - CDAT natgrid from nat import Natgrid I = Natgrid(xi, yi, xr, yr) I.ext = 1 # Changer I.igr = 1 # les parametres zzoc = I.rgrd(zi).T # - Matplotlib / R. Kern from matplotlib.delaunay import Triangulation tri = Triangulation(xi,yi) I = tri.nn_extrapolator(zi) # Tester les autres methodes zzork = I(xxr,yyr) # vers grille zork = I(xo,yo) # vers points # Avec VACUMM from vacumm.misc.grid.regridding import GridData, griddata, xy2xy zzov = griddata(xi, yi, zi, (xr, yr), method='nat', ext=True, sub=10) # -> Tester la methode "carg" # -> Testez parametre sub=... # -> Essayer avec GridData zov2 = xy2xy(xi, yi, zi, xo, yo) # Krigeage from vacumm.misc.grid.kriging import krig
def AngleResolved2Polar(data, output_size, hole=True, dtype=None): """ Converts an angle resolved image to polar (aka azymuthal) projection data (model.DataArray): The image that was projected on the CCD after being relfected on the parabolic mirror. The flat line of the D shape is expected to be horizontal, at the top. It needs PIXEL_SIZE and AR_POLE metadata. Pixel size is the sensor pixel size * binning / magnification. output_size (int): The size of the output DataArray (assumed to be square) hole (boolean): Crop the pole if True dtype (numpy dtype): intermediary dtype for computing the theta/phi data returns (model.DataArray): converted image in polar view """ assert(len(data.shape) == 2) # => 2D with greyscale # TODO: separate raw projection to another function, named AngleResolved2Rectangular() # Get the metadata try: pixel_size = data.metadata[model.MD_PIXEL_SIZE] mirror_x, mirror_y = data.metadata[model.MD_AR_POLE] parabola_f = data.metadata.get(model.MD_AR_PARABOLA_F, AR_PARABOLA_F) except KeyError: raise ValueError("Metadata required: MD_PIXEL_SIZE, MD_AR_POLE.") if dtype is None: dtype = numpy.float64 # Crop the input image to half circle cropped_image = _CropHalfCircle(data, pixel_size, (mirror_x, mirror_y), hole) theta_data = numpy.empty(shape=cropped_image.shape, dtype=dtype) phi_data = numpy.empty(shape=cropped_image.shape, dtype=dtype) omega_data = numpy.empty(shape=cropped_image.shape) # For each pixel of the input ndarray, input metadata is used to # calculate the corresponding theta, phi and radiant intensity image_x, image_y = cropped_image.shape jj = numpy.linspace(0, image_y - 1, image_y) xpix = mirror_x - jj for i in xrange(image_x): ypix = (i - mirror_y) + (2 * parabola_f) / pixel_size[1] theta, phi, omega = _FindAngle(data, xpix, ypix, pixel_size) theta_data[i, :] = theta phi_data[i, :] = phi omega_data[i, :] = cropped_image[i] / omega # Convert into polar coordinates h_output_size = output_size / 2 theta = theta_data * (h_output_size / math.pi * 2) phi = phi_data theta_data = numpy.cos(phi) * theta phi_data = numpy.sin(phi) * theta # Interpolation into 2d array # xi = numpy.linspace(-h_output_size, h_output_size, 2 * h_output_size + 1) # yi = numpy.linspace(-h_output_size, h_output_size, 2 * h_output_size + 1) # qz = mlab.griddata(phi_data.flat, theta_data.flat, omega_data.flat, xi, yi, interp="linear") # FIXME: need rotation (=swap axes), but swapping theta/phi slows down the # interpolation by 3 ?! with warnings.catch_warnings(): # Some points might be so close that they are identical (within float # precision). It's fine, no need to generate a warning. warnings.simplefilter("ignore", DuplicatePointWarning) triang = Triangulation(theta_data.flat, phi_data.flat) # TODO use the standard matplotlib.tri.Triangulation # + matplotlib.tri.LinearTriInterpolator interp = triang.linear_interpolator(omega_data.flat, default_value=0) qz = interp[-h_output_size:h_output_size:complex(0, output_size), # Y - h_output_size:h_output_size:complex(0, output_size)] # X qz = qz.swapaxes(0, 1)[:, ::-1] # rotate by 90° result = model.DataArray(qz, data.metadata) return result
def AngleResolved2Rectangular(data, output_size, hole=True, dtype=None): """ Converts an angle resolved image to equirectangular (aka cylindrical) projection (ie, phi/theta axes) data (model.DataArray): The image that was projected on the CCD after being relfected on the parabolic mirror. The flat line of the D shape is expected to be horizontal, at the top. It needs PIXEL_SIZE and AR_POLE metadata. Pixel size is the sensor pixel size * binning / magnification. output_size (int, int): The size of the output DataArray (theta, phi), not including the theta/phi angles at the first row/column hole (boolean): Crop the pole if True dtype (numpy dtype): intermediary dtype for computing the theta/phi data returns (model.DataArray): converted image in equirectangular view """ assert(len(data.shape) == 2) # => 2D with greyscale # Get the metadata try: pixel_size = data.metadata[model.MD_PIXEL_SIZE] mirror_x, mirror_y = data.metadata[model.MD_AR_POLE] parabola_f = data.metadata.get(model.MD_AR_PARABOLA_F, AR_PARABOLA_F) except KeyError: raise ValueError("Metadata required: MD_PIXEL_SIZE, MD_AR_POLE.") if dtype is None: dtype = numpy.float64 # Crop the input image to half circle cropped_image = _CropHalfCircle(data, pixel_size, (mirror_x, mirror_y), hole) theta_data = numpy.empty(shape=cropped_image.shape, dtype=dtype) phi_data = numpy.empty(shape=cropped_image.shape, dtype=dtype) omega_data = numpy.empty(shape=cropped_image.shape) # For each pixel of the input ndarray, input metadata is used to # calculate the corresponding theta, phi and radiant intensity image_x, image_y = cropped_image.shape jj = numpy.linspace(0, image_y - 1, image_y) xpix = mirror_x - jj for i in xrange(image_x): ypix = (i - mirror_y) + (2 * parabola_f) / pixel_size[1] theta, phi, omega = _FindAngle(data, xpix, ypix, pixel_size) theta_data[i, :] = theta phi_data[i, :] = phi omega_data[i, :] = cropped_image[i] / omega # compute new mask phi_lin = numpy.linspace(0, 2 * math.pi, output_size[1]) theta_lin = numpy.linspace(0, math.pi / 2, output_size[0]) phi_grid, theta_grid = numpy.meshgrid(phi_lin, theta_lin) a = (1 / (4 * parabola_f)) xcut = AR_XMAX - AR_PARABOLA_F # length vector c = (2 * (a * numpy.cos(phi_grid) * numpy.sin(theta_grid) + a)) ** -1 x = -numpy.sin(theta_grid) * numpy.cos(phi_grid) * c z = numpy.cos(theta_grid) * c mask = numpy.ones(output_size) mask[(x > xcut) | (theta_grid < (4 * numpy.pi / 180)) | (z < AR_FOCUS_DISTANCE)] = 0 # TODO: can probably choose a selection here to speed up interpolation. # This is a silly fix but it works. Prevents extrapolation which leads to errors theta_data = numpy.tile(theta_data, (1, 3)) phi_data = numpy.append(numpy.append(phi_data - 2 * math.pi, phi_data, axis=1), phi_data + 2 * math.pi, axis=1) ARdata = numpy.tile(omega_data, (1, 3)) with warnings.catch_warnings(): # Some points might be so close that they are identical (within float # precision). It's fine, no need to generate a warning. warnings.simplefilter("ignore", DuplicatePointWarning) triang = Triangulation(phi_data.flat, theta_data.flat) interp = triang.linear_interpolator(ARdata.flat, default_value=0) qz = interp[0:numpy.pi / 2:complex(0, output_size[0]), 0:2 * numpy.pi:complex(0, output_size[1])] qz = numpy.roll(qz, qz.shape[1] // 2, axis=1) qz_masked = qz * mask # TODO: put theta/phi angles in metadata? # attach theta as first column qz_masked = numpy.append(theta_lin.reshape(theta_lin.shape[0], 1), qz_masked, axis=1) # attach phi as first row phi_lin = numpy.append([[0]], phi_lin.reshape(1, phi_lin.shape[0]), axis=1) qz_masked = numpy.append(phi_lin, qz_masked, axis=0) result = model.DataArray(qz_masked, data.metadata) return result
def AngleResolved2Polar(data, output_size, hole=True, dtype=None): """ Converts an angle resolved image to polar (aka azymuthal) projection data (model.DataArray): The image that was projected on the CCD after being relfected on the parabolic mirror. The flat line of the D shape is expected to be horizontal, at the top. It needs PIXEL_SIZE and AR_POLE metadata. Pixel size is the sensor pixel size * binning / magnification. output_size (int): The size of the output DataArray (assumed to be square) hole (boolean): Crop the pole if True dtype (numpy dtype): intermediary dtype for computing the theta/phi data returns (model.DataArray): converted image in polar view """ assert(len(data.shape) == 2) # => 2D with greyscale # TODO: separate raw projection to another function, named AngleResolved2Rectangular() # Get the metadata try: pixel_size = data.metadata[model.MD_PIXEL_SIZE] mirror_x, mirror_y = data.metadata[model.MD_AR_POLE] parabola_f = data.metadata.get(model.MD_AR_PARABOLA_F, AR_PARABOLA_F) except KeyError: raise ValueError("Metadata required: MD_PIXEL_SIZE, MD_AR_POLE.") if dtype is None: dtype = numpy.float64 # Crop the input image to half circle cropped_image = _CropHalfCircle(data, pixel_size, (mirror_x, mirror_y), hole) theta_data = numpy.empty(shape=cropped_image.shape, dtype=dtype) phi_data = numpy.empty(shape=cropped_image.shape, dtype=dtype) omega_data = numpy.empty(shape=cropped_image.shape) # For each pixel of the input ndarray, input metadata is used to # calculate the corresponding theta, phi and radiant intensity image_x, image_y = cropped_image.shape jj = numpy.linspace(0, image_y - 1, image_y) xpix = mirror_x - jj for i in xrange(image_x): ypix = (i - mirror_y) + (2 * parabola_f) / pixel_size[1] theta, phi, omega = _FindAngle(data, xpix, ypix, pixel_size) theta_data[i, :] = theta phi_data[i, :] = phi omega_data[i, :] = cropped_image[i] / omega # Convert into polar coordinates h_output_size = output_size / 2 theta = theta_data * (h_output_size / math.pi * 2) phi = phi_data theta_data = numpy.cos(phi) * theta phi_data = numpy.sin(phi) * theta # Interpolation into 2d array # FIXME: griddata() uses a 3x less memory (and especially, doesn't leak), # but it's 5x slower # => create a cpython function calling libqhull directly? # grid_x, grid_y = numpy.mgrid[-h_output_size:h_output_size:output_size * 1j, # - h_output_size:h_output_size:output_size * 1j] # # #One way possible to increase the speed is to discard points too close from # #each other. Everything < 1 px apart is pretty useless. As they are # #spatially ordered, it's shouldn't be too costly to check # #See kmeans clustering maybe? # #nt, np, no = [theta_data[0, 0]], [phi_data[0, 0]], [omega_data[0, 0]] # #for t, p, o in zip(theta_data.flat, phi_data.flat, omega_data.flat): # # if math.hypot(t - nt[-1], p - np[-1]) > 0.9: # # nt.append(t) # # np.append(p) # # no.append(o) # #logging.warning("Reduce size to %d", len(no)) # #qz = griddata((nt, np), no, (grid_x, grid_y), method='linear', fill_value=0) # qz = griddata((theta_data.flat, phi_data.flat), omega_data.flat, (grid_x, grid_y), # method='linear', fill_value=0) # qz = qz[:, ::-1] # Warning: Uses a lot of memory, which is not recovered until the thread is # ended. Every thread will use a different memory pool. # FIXME: need rotation (=swap axes), but swapping theta/phi slows down the # interpolation by 3 ?! with warnings.catch_warnings(): # Some points might be so close that they are identical (within float # precision). It's fine, no need to generate a warning. warnings.simplefilter("ignore", DuplicatePointWarning) triang = Triangulation(theta_data.flat, phi_data.flat) # FIXME: Leaks memory when run in a separate thread interp = triang.linear_interpolator(omega_data.flat, default_value=0) qz = interp[-h_output_size:h_output_size:complex(0, output_size), # Y - h_output_size:h_output_size:complex(0, output_size)] # X qz = qz.swapaxes(0, 1)[:, ::-1] # rotate by 90° # TODO use the standard matplotlib.tri.Triangulation, but it uses 3x more memory # also leaks, and is slower! # triang = Triangulation(phi_data.flat, theta_data.flat) # interp = LinearTriInterpolator(triang, omega_data.flat) # xi, yi = numpy.meshgrid(numpy.linspace(-h_output_size, h_output_size, output_size), # numpy.linspace(-h_output_size, h_output_size, output_size)) # qz = interp(xi, yi) # qz = qz[:, ::-1] result = model.DataArray(qz, data.metadata) return result
def AngleResolved2Polar(data, output_size, hole=True, dtype=None): """ Converts an angle resolved image to polar (aka azymuthal) projection data (model.DataArray): The image that was projected on the CCD after being relfected on the parabolic mirror. The flat line of the D shape is expected to be horizontal, at the top. It needs PIXEL_SIZE and AR_POLE metadata. Pixel size is the sensor pixel size * binning / magnification. output_size (int): The size of the output DataArray (assumed to be square) hole (boolean): Crop the pole if True dtype (numpy dtype): intermediary dtype for computing the theta/phi data returns (model.DataArray): converted image in polar view """ assert (len(data.shape) == 2) # => 2D with greyscale # TODO: separate raw projection to another function, named AngleResolved2Rectangular() # Get the metadata try: pixel_size = data.metadata[model.MD_PIXEL_SIZE] mirror_x, mirror_y = data.metadata[model.MD_AR_POLE] parabola_f = data.metadata.get(model.MD_AR_PARABOLA_F, AR_PARABOLA_F) except KeyError: raise ValueError("Metadata required: MD_PIXEL_SIZE, MD_AR_POLE.") if dtype is None: dtype = numpy.float64 # Crop the input image to half circle cropped_image = _CropHalfCircle(data, pixel_size, (mirror_x, mirror_y), hole) theta_data = numpy.empty(shape=cropped_image.shape, dtype=dtype) phi_data = numpy.empty(shape=cropped_image.shape, dtype=dtype) omega_data = numpy.empty(shape=cropped_image.shape) # For each pixel of the input ndarray, input metadata is used to # calculate the corresponding theta, phi and radiant intensity image_x, image_y = cropped_image.shape jj = numpy.linspace(0, image_y - 1, image_y) xpix = mirror_x - jj for i in xrange(image_x): ypix = (i - mirror_y) + (2 * parabola_f) / pixel_size[1] theta, phi, omega = _FindAngle(data, xpix, ypix, pixel_size) theta_data[i, :] = theta phi_data[i, :] = phi omega_data[i, :] = cropped_image[i] / omega # Convert into polar coordinates h_output_size = output_size / 2 theta = theta_data * (h_output_size / math.pi * 2) phi = phi_data theta_data = numpy.cos(phi) * theta phi_data = numpy.sin(phi) * theta # Interpolation into 2d array # FIXME: griddata() uses a 3x less memory (and especially, doesn't leak), # but it's 5x slower # => create a cpython function calling libqhull directly? # grid_x, grid_y = numpy.mgrid[-h_output_size:h_output_size:output_size * 1j, # - h_output_size:h_output_size:output_size * 1j] # # #One way possible to increase the speed is to discard points too close from # #each other. Everything < 1 px apart is pretty useless. As they are # #spatially ordered, it's shouldn't be too costly to check # #See kmeans clustering maybe? # #nt, np, no = [theta_data[0, 0]], [phi_data[0, 0]], [omega_data[0, 0]] # #for t, p, o in zip(theta_data.flat, phi_data.flat, omega_data.flat): # # if math.hypot(t - nt[-1], p - np[-1]) > 0.9: # # nt.append(t) # # np.append(p) # # no.append(o) # #logging.warning("Reduce size to %d", len(no)) # #qz = griddata((nt, np), no, (grid_x, grid_y), method='linear', fill_value=0) # qz = griddata((theta_data.flat, phi_data.flat), omega_data.flat, (grid_x, grid_y), # method='linear', fill_value=0) # qz = qz[:, ::-1] # Warning: Uses a lot of memory, which is not recovered until the thread is # ended. Every thread will use a different memory pool. # FIXME: need rotation (=swap axes), but swapping theta/phi slows down the # interpolation by 3 ?! with warnings.catch_warnings(): # Some points might be so close that they are identical (within float # precision). It's fine, no need to generate a warning. warnings.simplefilter("ignore", DuplicatePointWarning) triang = Triangulation( theta_data.flat, phi_data.flat) # FIXME: Leaks memory when run in a separate thread interp = triang.linear_interpolator(omega_data.flat, default_value=0) qz = interp[-h_output_size:h_output_size:complex(0, output_size), # Y -h_output_size:h_output_size:complex(0, output_size)] # X qz = qz.swapaxes(0, 1)[:, ::-1] # rotate by 90° # TODO use the standard matplotlib.tri.Triangulation, but it uses 3x more memory # also leaks, and is slower! # triang = Triangulation(phi_data.flat, theta_data.flat) # interp = LinearTriInterpolator(triang, omega_data.flat) # xi, yi = numpy.meshgrid(numpy.linspace(-h_output_size, h_output_size, output_size), # numpy.linspace(-h_output_size, h_output_size, output_size)) # qz = interp(xi, yi) # qz = qz[:, ::-1] result = model.DataArray(qz, data.metadata) return result
def AngleResolved2Rectangular(data, output_size, hole=True, dtype=None): """ Converts an angle resolved image to equirectangular (aka cylindrical) projection (ie, phi/theta axes) data (model.DataArray): The image that was projected on the CCD after being relfected on the parabolic mirror. The flat line of the D shape is expected to be horizontal, at the top. It needs PIXEL_SIZE and AR_POLE metadata. Pixel size is the sensor pixel size * binning / magnification. output_size (int, int): The size of the output DataArray (theta, phi), not including the theta/phi angles at the first row/column hole (boolean): Crop the pole if True dtype (numpy dtype): intermediary dtype for computing the theta/phi data returns (model.DataArray): converted image in equirectangular view """ assert (len(data.shape) == 2) # => 2D with greyscale # Get the metadata try: pixel_size = data.metadata[model.MD_PIXEL_SIZE] mirror_x, mirror_y = data.metadata[model.MD_AR_POLE] parabola_f = data.metadata.get(model.MD_AR_PARABOLA_F, AR_PARABOLA_F) except KeyError: raise ValueError("Metadata required: MD_PIXEL_SIZE, MD_AR_POLE.") if dtype is None: dtype = numpy.float64 # Crop the input image to half circle cropped_image = _CropHalfCircle(data, pixel_size, (mirror_x, mirror_y), hole) theta_data = numpy.empty(shape=cropped_image.shape, dtype=dtype) phi_data = numpy.empty(shape=cropped_image.shape, dtype=dtype) omega_data = numpy.empty(shape=cropped_image.shape) # For each pixel of the input ndarray, input metadata is used to # calculate the corresponding theta, phi and radiant intensity image_x, image_y = cropped_image.shape jj = numpy.linspace(0, image_y - 1, image_y) xpix = mirror_x - jj for i in xrange(image_x): ypix = (i - mirror_y) + (2 * parabola_f) / pixel_size[1] theta, phi, omega = _FindAngle(data, xpix, ypix, pixel_size) theta_data[i, :] = theta phi_data[i, :] = phi omega_data[i, :] = cropped_image[i] / omega # compute new mask phi_lin = numpy.linspace(0, 2 * math.pi, output_size[1]) theta_lin = numpy.linspace(0, math.pi / 2, output_size[0]) phi_grid, theta_grid = numpy.meshgrid(phi_lin, theta_lin) a = (1 / (4 * parabola_f)) xcut = AR_XMAX - AR_PARABOLA_F # length vector c = (2 * (a * numpy.cos(phi_grid) * numpy.sin(theta_grid) + a))**-1 x = -numpy.sin(theta_grid) * numpy.cos(phi_grid) * c z = numpy.cos(theta_grid) * c mask = numpy.ones(output_size) mask[(x > xcut) | (theta_grid < (4 * numpy.pi / 180)) | (z < AR_FOCUS_DISTANCE)] = 0 # TODO: can probably choose a selection here to speed up interpolation. # This is a silly fix but it works. Prevents extrapolation which leads to errors theta_data = numpy.tile(theta_data, (1, 3)) phi_data = numpy.append(numpy.append(phi_data - 2 * math.pi, phi_data, axis=1), phi_data + 2 * math.pi, axis=1) ARdata = numpy.tile(omega_data, (1, 3)) with warnings.catch_warnings(): # Some points might be so close that they are identical (within float # precision). It's fine, no need to generate a warning. warnings.simplefilter("ignore", DuplicatePointWarning) triang = Triangulation(phi_data.flat, theta_data.flat) interp = triang.linear_interpolator(ARdata.flat, default_value=0) qz = interp[0:numpy.pi / 2:complex(0, output_size[0]), 0:2 * numpy.pi:complex(0, output_size[1])] qz = numpy.roll(qz, qz.shape[1] // 2, axis=1) qz_masked = qz * mask # TODO: put theta/phi angles in metadata? # attach theta as first column qz_masked = numpy.append(theta_lin.reshape(theta_lin.shape[0], 1), qz_masked, axis=1) # attach phi as first row phi_lin = numpy.append([[0]], phi_lin.reshape(1, phi_lin.shape[0]), axis=1) qz_masked = numpy.append(phi_lin, qz_masked, axis=0) result = model.DataArray(qz_masked, data.metadata) return result