Exemplo n.º 1
0
def test_tabular1d_inverse():
    """Test that the Tabular1D inverse is defined"""
    points = np.arange(5)
    values = np.array([1.5, 3.4, 6.7, 7, 32])
    t = models.Tabular1D(points, values)
    result = t.inverse((3.4, 6.7))
    assert_allclose(result, np.array((1., 2.)))

    # Check that it works for descending values in lookup_table
    t2 = models.Tabular1D(points, values[::-1])
    assert_allclose(t2.inverse.points[0], t2.lookup_table[::-1])

    result2 = t2.inverse((7, 6.7))
    assert_allclose(result2, np.array((1., 2.)))

    # Check that it errors on double-valued lookup_table
    points = np.arange(5)
    values = np.array([1.5, 3.4, 3.4, 32, 25])
    t = models.Tabular1D(points, values)
    with pytest.raises(NotImplementedError):
        t.inverse((3.4, 7.))

    # Check that Tabular2D.inverse raises an error
    table = np.arange(5*5).reshape(5, 5)
    points = np.arange(0, 5)
    points = (points, points)
    t3 = models.Tabular2D(points=points, lookup_table=table)
    with pytest.raises(NotImplementedError):
        t3.inverse((3, 3))
Exemplo n.º 2
0
def test_tabular_str():
    points = np.arange(5)
    lt = np.arange(5)
    t = models.Tabular1D(points, lt)
    assert str(t) == ("Model: Tabular1D\n"
                      "N_inputs: 1\n"
                      "N_outputs: 1\n"
                      "Parameters: \n"
                      "  points: (array([0, 1, 2, 3, 4]),)\n"
                      "  lookup_table: [0 1 2 3 4]\n"
                      "  method: linear\n"
                      "  fill_value: nan\n"
                      "  bounds_error: True")

    table = np.arange(5 * 5).reshape(5, 5)
    points = np.arange(0, 5)
    points = (points, points)
    t = models.Tabular2D(points=points, lookup_table=table)
    assert str(t) == (
        "Model: Tabular2D\n"
        "N_inputs: 2\n"
        "N_outputs: 1\n"
        "Parameters: \n"
        "  points: (array([0, 1, 2, 3, 4]), array([0, 1, 2, 3, 4]))\n"
        "  lookup_table: [[ 0  1  2  3  4]\n"
        " [ 5  6  7  8  9]\n"
        " [10 11 12 13 14]\n"
        " [15 16 17 18 19]\n"
        " [20 21 22 23 24]]\n"
        "  method: linear\n"
        "  fill_value: nan\n"
        "  bounds_error: True")
Exemplo n.º 3
0
def fitswcs_transform_from_model(wcsinfo, wavetable=None):
    """
    Create a WCS object using from datamodel.meta.wcsinfo.
    Transforms assume 0-based coordinates.

    Parameters
    ----------
    wcsinfo : dict-like
        ``~jwst.meta.wcsinfo`` structure.

    Return
    ------
    transform : `~astropy.modeling.core.Model`
        WCS forward transform - from pixel to world coordinates.

    """
    spatial_axes, spectral_axes, unknown = gwutils.get_axes(wcsinfo)

    transform = gwutils.make_fitswcs_transform(wcsinfo)
    if spectral_axes:
        sp_axis = spectral_axes[0]
        if wavetable is None:
            # Subtract one from CRPIX which is 1-based.
            spectral_transform = astmodels.Shift(-(wcsinfo['CRPIX'][sp_axis] - 1)) | \
                astmodels.Scale(wcsinfo['CDELT'][sp_axis]) | \
                astmodels.Shift(wcsinfo['CRVAL'][sp_axis])
        else:
            # Wave dimension is an array that needs to be converted to a table
            waves = wavetable['wavelength'].flatten()
            spectral_transform = astmodels.Tabular1D(lookup_table=waves)

        transform = transform & spectral_transform

    return transform
Exemplo n.º 4
0
def test_tabular_with_bounding_box():
    points = np.arange(5)
    values = np.array([1.5, 3.4, 6.7, 7, 32])
    t = models.Tabular1D(points, values)
    result = t(1, with_bounding_box=True)

    assert result == 3.4
    assert t.inverse(result, with_bounding_box=True) == 1.
Exemplo n.º 5
0
def test_tabular_bounding_box_with_units():
    points = np.arange(5) * u.pix
    lt = np.arange(5) * u.AA
    t = models.Tabular1D(points, lt)
    result = t(1 * u.pix, with_bounding_box=True)

    assert result == 1. * u.AA
    assert t.inverse(result, with_bounding_box=True) == 1 * u.pix
Exemplo n.º 6
0
def test_format_output():
    points = np.arange(5)
    values = np.array([1.5, 3.4, 6.7, 7, 32])
    t = models.Tabular1D(points, values)
    pipe = [('detector', t), ('world', None)]
    w = wcs.WCS(pipe)
    assert_allclose(w(1), 3.4)
    assert_allclose(w([1, 2]), [3.4, 6.7])
    assert np.isscalar(w(1))
Exemplo n.º 7
0
def test_units_with_bounding_box():
    points = np.arange(10, 20)
    table = np.arange(10) * u.Angstrom
    t = models.Tabular1D(points, lookup_table=table)

    assert isinstance(t(10), u.Quantity)
    assert isinstance(t(10, with_bounding_box=True), u.Quantity)

    assert_quantity_allclose(t(10), t(10, with_bounding_box=True))
Exemplo n.º 8
0
def test_bare_baseframe():
    # This is a regression test for the following call:
    frame = cf.CoordinateFrame(1, "SPATIAL", (0,), unit=(u.km,))
    assert u.allclose(frame.coordinate_to_quantity((1*u.m,)), 1*u.m)

    # Now also setup the same situation through the whole call stack to be safe.
    w = WCS(forward_transform=m.Tabular1D(points=np.arange(10)*u.pix,
                                          lookup_table=np.arange(10)*u.km),
            output_frame=frame,
            input_frame=cf.CoordinateFrame(1, "PIXEL", (0,), unit=(u.pix,), name="detector_frame")
            )
    assert u.allclose(w.world_to_pixel(0*u.km), 0)
Exemplo n.º 9
0
def test_tabular_evaluate():
    points = np.arange(5)
    lt = np.arange(5)[::-1]
    t = models.Tabular1D(points, lt)
    assert (t.evaluate([1, 2, 3]) == [3, 2, 1]).all()
    assert (t.evaluate(np.array([1, 2, 3]) * u.m) == [3, 2, 1]).all()

    t.n_outputs = 2
    value = [np.array([3, 2, 1]), np.array([1, 2, 3])]
    with mk.patch.object(tabular_models, 'interpn', autospec=True, return_value=value) as mkInterpn:
        outputs = t.evaluate([1, 2, 3])
        for index, output in enumerate(outputs):
            assert np.all(value[index] == output)
        assert mkInterpn.call_count == 1
Exemplo n.º 10
0
def test_tabular_model(tmpdir):
    points = np.arange(0, 5)
    values = [1., 10, 2, 45, -3]
    model = astmodels.Tabular1D(points=points, lookup_table=values)
    tree = {'model': model}
    helpers.assert_roundtrip_tree(tree, tmpdir)
    table = np.array([[ 3.,  0.,  0.],
                      [ 0.,  2.,  0.],
                      [ 0.,  0.,  0.]])
    points = ([1, 2, 3], [1, 2, 3])
    model2 = astmodels.Tabular2D(points, lookup_table=table, bounds_error=False,
                                 fill_value=None, method='nearest')
    tree = {'model': model2}
    helpers.assert_roundtrip_tree(tree, tmpdir)
Exemplo n.º 11
0
def gwcs_stokes_lookup():
    transform = models.Tabular1D([0, 1, 2, 3] * u.pix, [0, 1, 2, 3] * u.one,
                                 method="nearest",
                                 fill_value=np.nan,
                                 bounds_error=False)
    frame = cf.StokesFrame()

    detector_frame = cf.CoordinateFrame(name="detector",
                                        naxes=1,
                                        axes_order=(0, ),
                                        axes_type=("pixel", ),
                                        axes_names=("x", ),
                                        unit=(u.pix, ))

    return wcs.WCS(forward_transform=transform,
                   output_frame=frame,
                   input_frame=detector_frame)
Exemplo n.º 12
0
def generate_lookup_table(lookup_table,
                          interpolation='linear',
                          points_unit=u.pix,
                          **kwargs):
    if not isinstance(lookup_table, u.Quantity):
        raise TypeError("lookup_table must be a Quantity.")

    # The integer location is at the centre of the pixel.
    points = (np.arange(lookup_table.size) - 0) * points_unit

    kwargs = {
        'bounds_error': False,
        'fill_value': np.nan,
        'method': interpolation,
        **kwargs
    }

    return m.Tabular1D(points, lookup_table, **kwargs)
Exemplo n.º 13
0
def test_tabular_repr():
    points = np.arange(5)
    lt = np.arange(5)
    t = models.Tabular1D(points, lt)
    assert repr(t) ==\
        "<Tabular1D(points=(array([0, 1, 2, 3, 4]),), lookup_table=[0 1 2 3 4])>"

    table = np.arange(5 * 5).reshape(5, 5)
    points = np.arange(0, 5)
    points = (points, points)
    t = models.Tabular2D(points=points, lookup_table=table)
    assert repr(t) ==\
        "<Tabular2D(points=(array([0, 1, 2, 3, 4]), array([0, 1, 2, 3, 4])), " +\
        "lookup_table=[[ 0  1  2  3  4]\n" +\
        " [ 5  6  7  8  9]\n" +\
        " [10 11 12 13 14]\n" +\
        " [15 16 17 18 19]\n" +\
        " [20 21 22 23 24]])>"
Exemplo n.º 14
0
    assert_allclose(positional, model(1, 2))

    with pytest.raises(ValueError):
        model(1, 2, 3)

    with pytest.raises(ValueError):
        model(1)

    with pytest.raises(ValueError):
        model(1, 2, t=12, r=3)


@pytest.mark.parametrize('model', [
    models.Gaussian1D(),
    models.Polynomial1D(1, ),
    models.Tabular1D(lookup_table=np.ones((5, ))),
    models.Rotation2D(),
    models.Pix2Sky_TAN()
])
@pytest.mark.skipif('not HAS_SCIPY')
def test_call_keyword_args_1(model):
    """
    Test calling a model with positional, keywrd and a mixture of both arguments.
    """
    positional = model(1)
    assert_allclose(positional, model(x=1))

    model.inputs = ('r', )
    assert_allclose(positional, model(r=1))

    with pytest.raises(ValueError):
Exemplo n.º 15
0
def test_tabular_with_bounding_box():
    points = np.arange(5)
    values = np.array([1.5, 3.4, 6.7, 7, 32])
    t = models.Tabular1D(points, values)
    result = t(1, with_bounding_box=True)
Exemplo n.º 16
0
def test_bounding_box_with_units():
    points = np.arange(5)*u.pix
    lt = np.arange(5)*u.AA
    t = models.Tabular1D(points, lt)

    assert(t(1*u.pix, with_bounding_box=True) == 1.*u.AA)
Exemplo n.º 17
0
def v2v3lamtoxy(v2in, v3in, lamin, stype):
    # Open relevant distortion file
    specfile = fits.open(get_fitsreffile())

    # Convert input vectors to numpy arrays
    v2 = np.array(v2in)
    v3 = np.array(v3in)
    lam = np.array(lamin)

    # Global header
    hdr = specfile[0].header
    # File data
    lrsdata = np.array([l for l in specfile[1].data])
    # Define zero points (in subarray frame), subarray location, subarray size
    if (stype.lower() == 'slit'):
        xsub0, ysub0 = 0, 0
        xsub1, ysub1 = 1031, 1023
        zero_point = hdr['imx'] - 1, hdr['imy'] - 1
    elif (stype.lower() == 'slitless'):
        xsub0, ysub0 = 0, 528
        xsub1, ysub1 = 71, 415
        zero_point = hdr['imxsltl'] - 1 - xsub0, hdr['imysltl'] - 1 - ysub0
    else:
        print('Invalid operation type: specify either slit or slitless')

    # In the lrsdata reference table, X_center,y_center,wavelength describe the location of the
    # centroid trace along the detector in pixels relative to nominal location.
    # The box corners for this wavelength are x0,y0(ul) x1,y1 (ur) x2,y2(lr) x3,y3(ll)
    # Use these to create the bounding box for all valid detector locations in units of subarray pixels
    xcen = lrsdata[:, 0]
    ycen = lrsdata[:, 1]
    wavetab = lrsdata[:, 2]
    x0 = lrsdata[:, 3]
    y0 = lrsdata[:, 4]
    x1 = lrsdata[:, 5]
    y2 = lrsdata[:, 8]
    bb = ((x0.min() - 0.5 + zero_point[0], x1.max() + 0.5 + zero_point[0]),
          (y2.min() - 0.5 + zero_point[1], y0.max() + 0.5 + zero_point[1]))

    # Now deal with the fact that the spectral trace isn't perfectly up and down along detector.
    # This information is contained in the xcenter/ycenter values in the CDP table, but we'll handle it
    # as a simple rotation using a linear fit to this relation provided by the CDP.
    z = np.polyfit(xcen, ycen, 1)
    slope = 1. / z[0]
    traceangle = np.arctan(slope) * 180. / np.pi  # trace angle in degrees
    rot = models.Rotation2D(traceangle)  # Rotation model

    # Now include this rotation in our overall transform
    # First shift to a frame relative to the trace zeropoint, then apply the rotation
    # to correct for the curved trace.  End in a rotated frame relative to zero at the reference point
    # and where yrot is aligned with the spectral trace)
    xysubtoxyrot = models.Shift(-zero_point[0]) & models.Shift(
        -zero_point[1]) | rot

    # Work out the spectral component of the transform
    # First compute the reference trace in the rotated-Y frame
    xcenrot, ycenrot = rot(xcen, ycen)
    # The input table of wavelengths isn't perfect, and the delta-wavelength steps show some unphysical behaviour
    # Therefore fit with a spline for the ycenrot->wavelength transform
    # Reverse vectors so that yinv is increasing (needed for spline fitting function)
    wavetab = lrsdata[:, 2]
    yrev = ycenrot[::-1]
    wrev = wavetab[::-1]
    # Spline fit with enforced smoothness
    spl = UnivariateSpline(yrev, wrev, s=0.002)
    # Evaluate the fit at the rotated-y reference points
    wavereference = spl(yrev)
    # wavereference now contains the wavelengths corresponding to regularly-sampled ycenrot, create the model
    wavemodel = models.Tabular1D(lookup_table=wavereference,
                                 points=yrev,
                                 name='waveref',
                                 bounds_error=False,
                                 fill_value=np.nan)

    # Now construct the inverse spectral transform.
    # First we need to create a spline going from wavereference -> ycenrot
    spl2 = UnivariateSpline(wavereference[::-1], ycenrot, s=0.002)

    # Compute the nominal x,y pixels in subarray frame for this v2,v3
    xnom, ynom = mt.v2v3toxy(v2, v3, 'F770W')
    xnom = xnom - xsub0
    ynom = ynom - ysub0
    # Compute this in the rotated frame
    xrot, _ = xysubtoxyrot(xnom, ynom)
    # Get the real yrot from the wavelength
    yrot = spl2(lam)

    # Convert rotated x,y to subarray x,y
    xsub, ysub = xysubtoxyrot.inverse(xrot, yrot)

    return xsub, ysub
Exemplo n.º 18
0
def lrs_distortion(input_model, reference_files):
    """
    The LRS-FIXEDSLIT and LRS-SLITLESS WCS pipeline.

    Transform from subarray (x, y) to (v2, v3, lambda) using
    the "specwcs" and "distortion" reference files.

    """

    # subarray to full array transform
    subarray2full = subarray_transform(input_model)

    # full array to v2v3 transform for the ordinary imager
    with DistortionModel(reference_files['distortion']) as dist:
        distortion = dist.model

    # Combine models to create subarray to v2v3 distortion
    if subarray2full is not None:
        subarray_dist = subarray2full | distortion
    else:
        subarray_dist = distortion

    # Read in the reference table data and get the zero point (SIAF reference point)
    # of the LRS in the subarray ref frame
    with fits.open(reference_files['specwcs']) as ref:
        lrsdata = np.array([l for l in ref[1].data])
        # Get the zero point from the reference data.
        # The zero_point is X, Y  (which should be COLUMN, ROW)
        # These are 1-indexed in CDP-7 (i.e., SIAF convention) so must be converted to 0-indexed
        if input_model.meta.exposure.type.lower() == 'mir_lrs-fixedslit':
            zero_point = ref[0].header['imx'] - 1, ref[0].header['imy'] - 1
        elif input_model.meta.exposure.type.lower() == 'mir_lrs-slitless':
            zero_point = ref[0].header['imxsltl'] - 1, ref[0].header[
                'imysltl'] - 1
            # Transform to slitless subarray from full array
            zero_point = subarray2full.inverse(zero_point[0], zero_point[1])

    # In the lrsdata reference table, X_center,y_center,wavelength describe the location of the
    # centroid trace along the detector in pixels relative to nominal location.
    # x0,y0(ul) x1,y1 (ur) x2,y2(lr) x3,y3(ll) define corners of the box within which the distortion
    # and wavelength calibration was derived
    xcen = lrsdata[:, 0]
    ycen = lrsdata[:, 1]
    wavetab = lrsdata[:, 2]
    x0 = lrsdata[:, 3]
    y0 = lrsdata[:, 4]
    x1 = lrsdata[:, 5]
    y2 = lrsdata[:, 8]

    # If in fixed slit mode, define the bounding box using the corner locations provided in
    # the CDP reference file.
    if input_model.meta.exposure.type.lower() == 'mir_lrs-fixedslit':

        bb_sub = ((np.floor(x0.min() + zero_point[0]) - 0.5,
                   np.ceil(x1.max() + zero_point[0]) + 0.5),
                  (np.floor(y2.min() + zero_point[1]) - 0.5,
                   np.ceil(y0.max() + zero_point[1]) + 0.5))

    # If in slitless mode, define the bounding box X locations using the subarray x boundaries
    # and the y locations using the corner locations in the CDP reference file.  Make sure to
    # omit the 4 reference pixels on the left edge of slitless subarray.
    if input_model.meta.exposure.type.lower() == 'mir_lrs-slitless':
        bb_sub = ((input_model.meta.subarray.xstart - 1 + 4 - 0.5,
                   input_model.meta.subarray.xsize - 1 + 0.5),
                  (np.floor(y2.min() + zero_point[1]) - 0.5,
                   np.ceil(y0.max() + zero_point[1]) + 0.5))

    # Find the ROW of the zero point
    row_zero_point = zero_point[1]

    # The inputs to the "detector_to_v2v3" transform are
    # - the indices in x spanning the entire image row
    # - y is the y-value of the zero point
    # This is equivalent of making a vector of x, y locations for
    # every pixel in the reference row
    const1d = models.Const1D(row_zero_point)
    const1d.inverse = models.Const1D(row_zero_point)
    det_to_v2v3 = models.Identity(1) & const1d | subarray_dist

    # Now deal with the fact that the spectral trace isn't perfectly up and down along detector.
    # This information is contained in the xcenter/ycenter values in the CDP table, but we'll handle it
    # as a simple rotation using a linear fit to this relation provided by the CDP.

    z = np.polyfit(xcen, ycen, 1)
    slope = 1. / z[0]
    traceangle = np.arctan(slope) * 180. / np.pi  # trace angle in degrees
    rot = models.Rotation2D(traceangle)  # Rotation model

    # Now include this rotation in our overall transform
    # First shift to a frame relative to the trace zeropoint, then apply the rotation
    # to correct for the curved trace.  End in a rotated frame relative to zero at the reference point
    # and where yrot is aligned with the spectral trace)
    xysubtoxyrot = models.Shift(-zero_point[0]) & models.Shift(
        -zero_point[1]) | rot

    # Next shift back to the subarray frame, and then map to v2v3
    xyrottov2v3 = models.Shift(zero_point[0]) & models.Shift(
        zero_point[1]) | det_to_v2v3

    # The two models together
    xysubtov2v3 = xysubtoxyrot | xyrottov2v3

    # Work out the spectral component of the transform
    # First compute the reference trace in the rotated-Y frame
    xcenrot, ycenrot = rot(xcen, ycen)
    # The input table of wavelengths isn't perfect, and the delta-wavelength
    # steps show some unphysical behaviour
    # Therefore fit with a spline for the ycenrot->wavelength transform
    # Reverse vectors so that yinv is increasing (needed for spline fitting function)
    yrev = ycenrot[::-1]
    wrev = wavetab[::-1]
    # Spline fit with enforced smoothness
    spl = UnivariateSpline(yrev, wrev, s=0.002)
    # Evaluate the fit at the rotated-y reference points
    wavereference = spl(yrev)
    # wavereference now contains the wavelengths corresponding to regularly-sampled ycenrot, create the model
    wavemodel = models.Tabular1D(lookup_table=wavereference,
                                 points=yrev,
                                 name='waveref',
                                 bounds_error=False,
                                 fill_value=np.nan)

    # Now construct the inverse spectral transform.
    # First we need to create a spline going from wavereference -> ycenrot
    spl2 = UnivariateSpline(wavereference[::-1], ycenrot, s=0.002)
    # Make a uniform grid of wavelength points from min to max, sampled according
    # to the minimum delta in the input table
    dw = np.amin(np.absolute(np.diff(wavereference)))
    wmin = np.amin(wavereference)
    wmax = np.amax(wavereference)
    wgrid = np.arange(wmin, wmax, dw)
    # Evaluate the rotated y locations of the grid
    ygrid = spl2(wgrid)
    # ygrid now contains the rotated y pixel locations corresponding to
    # regularly-sampled wavelengths, create the model
    wavemodel.inverse = models.Tabular1D(lookup_table=ygrid,
                                         points=wgrid,
                                         name='waverefinv',
                                         bounds_error=False,
                                         fill_value=np.nan)

    # Wavelength barycentric correction
    try:
        velosys = input_model.meta.wcsinfo.velosys
    except AttributeError:
        pass
    else:
        if velosys is not None:
            velocity_corr = velocity_correction(
                input_model.meta.wcsinfo.velosys)
            wavemodel = wavemodel | velocity_corr
            log.info("Applied Barycentric velocity correction : {}".format(
                velocity_corr[1].amplitude.value))

    # Construct the full distortion model (xsub,ysub -> v2,v3,wavelength)
    lrs_wav_model = xysubtoxyrot | models.Mapping([1], n_inputs=2) | wavemodel
    dettotel = models.Mapping((0, 1, 0, 1)) | xysubtov2v3 & lrs_wav_model

    # Construct the inverse distortion model (v2,v3,wavelength -> xsub,ysub)
    # Transform to get xrot from v2,v3
    v2v3toxrot = subarray_dist.inverse | xysubtoxyrot | models.Mapping(
        [0], n_inputs=2)
    # wavemodel.inverse gives yrot from wavelength
    # v2,v3,lambda -> xrot,yrot
    xform1 = v2v3toxrot & wavemodel.inverse
    dettotel.inverse = xform1 | xysubtoxyrot.inverse

    # Bounding box is the subarray bounding box, because we're assuming subarray coordinates passed in
    dettotel.bounding_box = bb_sub[::-1]

    return dettotel
Exemplo n.º 19
0
    astropy_models.Sky2Pix_ZenithalPerspective(mu=1.5, gamma=15.0),

    # astropy.modeling.rotations
    astropy_models.EulerAngleRotation(23, 14, 2.3, axes_order="xzx"),
    astropy_models.RotateCelestial2Native(5.63, -72.5, 180),
    astropy_models.RotateCelestial2Native(5.63 * u.deg, -72.5 * u.deg,
                                          180 * u.deg),
    astropy_models.RotateNative2Celestial(5.63, -72.5, 180),
    astropy_models.RotateNative2Celestial(5.63 * u.deg, -72.5 * u.deg,
                                          180 * u.deg),
    astropy_models.Rotation2D(angle=1.51),
    astropy_models.RotationSequence3D([1.2, 2.3, 3.4, .3], "xyzx"),
    astropy_models.SphericalRotationSequence([1.2, 2.3, 3.4, .3], "xyzy"),

    # astropy.modeling.tabular
    astropy_models.Tabular1D(points=np.arange(0, 5),
                             lookup_table=[1., 10, 2, 45, -3]),
    astropy_models.Tabular1D(points=np.arange(0, 5) * u.pix,
                             lookup_table=[1., 10, 2, 45, -3] * u.nm),
    astropy_models.Tabular2D(
        points=([1, 2, 3], [1, 2, 3]),
        lookup_table=np.arange(0, 9).reshape(3, 3),
        bounds_error=False,
        fill_value=None,
        method="nearest",
    ),
    astropy_models.Tabular2D(
        points=([1, 2, 3], [1, 2, 3]) * u.pix,
        lookup_table=np.arange(0, 9).reshape(3, 3) * u.nm,
        bounds_error=False,
        fill_value=None,
        method="nearest",