curryer.compute.spatial¶
Spatial computations.
Notes
- Terms:
ECEF - Earth Centered Earth Fixed, called ITRF93 in SPICE.
ECI - Earth Centered Inertial, called J2000 in SPICE.
Rectangular coordinates - X/Y/Z offset from center.
Geodetic coordinates - Lon/Lat/Alt from a reference ellipsoid.
WGS84 - Standard ellipsoidal representation of the Earth.
Geoid height - Modeled height above the ellipsoid that’s typically called
sea-level, based on gravitational equipotential surface. - Orthometric height - Surface elevation above (not including) the geoid. - Terrain correct - Process of accounting for elevated surfaces (terrain) that may have been intersected before reaching the ellipsoid, resulting in a “horizontal” (lon/lat) offset, increasing based on off-nadir angle.
@author: Brandon Stone
Attributes¶
Classes¶
High-level class to manage the geolocation processing steps. |
Functions¶
|
Load the pixel or boresight vector(s) for a given instrument from the instrument kernel. |
|
Load the pixel or boresight vector(s) for a given instrument. |
|
Compute the boresight pointing and state (ECEF or ECI). |
|
Compute the intersection points of an instrument's pointing vectors (boresight or pixels) with the Earth's surface, |
Geolocate the boresight on the Earth's surface (WGS84 ellipsoid). |
|
|
Intersect a pointing vector to an ellipsoid (vectorized). |
|
Convert Earth Centered Earth Fixed rectangular coordinates to geodetic |
|
Convert geodetic latitude longitude and altitude (WGS84 ellipsoid) to |
|
Perform terrain correction on ellipsoidal intersections (vectorized). |
|
Compute the azimuth angle (vectorized). |
|
Compute the zenith angle (vectorized). |
|
Compute the azimuth and zenith surface angles (vectorized). |
|
Compute min/max longitude, accounting for the international dateline. |
|
Compute azimuth and zenith angles using SPICE. |
|
Apply terrain correction to a single point. |
|
Geolocate the boresight on the Earth's surface (WGS84 ellipsoid). |
Module Contents¶
- curryer.compute.spatial.logger¶
- curryer.compute.spatial.EARTH_FRAME = 'ITRF93'¶
- class curryer.compute.spatial.SpatialQueries¶
- static _query_rotation_and_position_raw(sample_et, instrument, perspective_correction, observer_id, fixed_frame_name)¶
Raw SPICE query without error interception.
- _query_rotation_and_position_safe¶
- classmethod query_rotation_and_position(sample_et, instrument, perspective_correction, observer_id: int = spicierpy.obj.Body('EARTH').id, allow_nans=False, fixed_frame_name=EARTH_FRAME)¶
Query SPICE for rotation and position, optionally suppressing errors.
- Parameters:
sample_et (float) – Ephemeris time to query.
instrument (spicierpy.obj.Body) – Instrument to query.
perspective_correction (str) – SPICE perspective correction to use.
observer_id (int, optional) – Observer NAIF ID (default is Earth).
allow_nans (bool, optional) – If True, suppress SPICE errors and return NaNs instead. Default is False.
fixed_frame_name (str, optional) – Fixed reference frame name (default is ECEF/ITRF93).
- Returns:
tuple – Rotation matrix (3x3) and position vector (3,) in fixed frame.
SpatialQualityFlags – Quality flag indicating results.
- curryer.compute.spatial.get_instrument_kernel_pointing_vectors(instrument: int | str | curryer.spicierpy.obj.Body) tuple[int, numpy.ndarray]¶
Load the pixel or boresight vector(s) for a given instrument from the instrument kernel.
Boresight vector is queried from the instrument kernel, but superseded by pixel vectors if they are available.
Pixel vectors are queried from the SPICE kernel variable pool using the non-standard (but established) variables:
INS{instrument_id}_PIXEL_COUNT INS{instrument_id}_PIXEL_VECTORS
where the former is the pixel index and the latter is three doubles.
- Parameters:
instrument (spicierpy.obj.Body or int or str) – Instrument to query details about.
- Returns:
int – Number of boresight (1) or pixel vectors (1+).
np.ndarray – 2D array of boresight (1x3) or pixel vectors (1+,3).
- curryer.compute.spatial.pixel_vectors(instrument: int | str | curryer.spicierpy.obj.Body) tuple[int, numpy.ndarray]¶
Load the pixel or boresight vector(s) for a given instrument.
Boresight vector is queried from the instrument kernel, but superseded by pixel vectors if they are available.
Pixel vectors are queried from the SPICE kernel variable pool using the non-standard (but established) variables:
INS{instrument_id}_PIXEL_COUNT INS{instrument_id}_PIXEL_VECTORS
where the former is the pixel index and the latter is three doubles.
- Parameters:
instrument (spicierpy.obj.Body or int or str) – Instrument to query details about.
- Returns:
int – Number of boresight (1) or pixel vectors (1+).
np.ndarray – 2D array of boresight (1x3) or pixel vectors (1+,3).
- curryer.compute.spatial.instrument_pointing_state(ugps_times: numpy.ndarray, instrument: int | str | curryer.spicierpy.obj.Body, correction: str = None, allow_nans=True, boresight_vector=None, pointing_frame='J2000') tuple[pandas.DataFrame, pandas.DataFrame, pandas.Series]¶
Compute the boresight pointing and state (ECEF or ECI).
- Parameters:
ugps_times (np.ndarray) – One or more times in GPS microseconds to compute the pointing. Relevant SPICE kernels must be loaded for these times.
instrument (spicierpy.obj.Body or int or str) – Instrument name or ID containing the boresight to process. If boresight_vector is None (default), the SPICE instrument kernel and/or SPICE variables must be loaded; see pixel_vectors.
correction (str, optional) – Type of SPICE perspective correction to use. Default is “None”.
allow_nans (bool, optional) – Convert invalid returns (e.g., data gaps) into NaNs (default), otherwise throws SPICE errors.
boresight_vector (np.ndarray, optional) – Array of one or more boresight vectors in the instrument frame. Default is None, instead loading them from SPICE kernels using pixel_vectors.
pointing_frame (str, optional) – Pointing frame. Use “ITRF93” for ECEF and “J2000” for ECI (default).
- Returns:
pd.DataFrame – Pointing in rectangular coordinates in kilometers. Invalid points are set to NaN unless allow_nans was False. The index is the product of ugps_times and boresight_vector.
pd.DataFrame – Instrument position, velocity, and attitude in pointing_frame as rectangular x/y/z coordinates and Euler angles in degrees. The index is the ugps_times.
pd.Series – Quality flags indicating why one or both of the other returns were set to NaNs (e.g., missing kernel data). The index is the product of ugps_times and boresight_vector.
- curryer.compute.spatial.compute_ellipsoid_intersection(ugps_times: numpy.ndarray, instrument: int | str | curryer.spicierpy.obj.Body, perspective_correction: str = None, allow_nans: bool = True, custom_pointing_vectors: numpy.ndarray | None = None, give_geodetic_output: bool = False, give_lat_lon_in_degrees: bool = True, observer_id: int = spicierpy.obj.Body('EARTH').id, fixed_frame_name: str = EARTH_FRAME) tuple[pandas.DataFrame, pandas.DataFrame, pandas.Series]¶
Compute the intersection points of an instrument’s pointing vectors (boresight or pixels) with the Earth’s surface, modeled as the WGS84 reference ellipsoid. This function geolocates where the instrument (e.g., a satellite sensor) is “looking” on the ellipsoid at given times, optionally applying perspective corrections and custom pointing vectors.
This is the recommended API replacing instrument_intersect_ellipsoid.
- Parameters:
ugps_times (np.ndarray) – Array of times (in UNIX GPS seconds) at which to compute the intersection points.
instrument (int, str, or spicierpy.obj.Body) – The instrument or spacecraft identifier. Can be a NAIF integer code, a string name, or a spicierpy.obj.Body object.
perspective_correction (str, optional) – If specified, applies a perspective correction model to the pointing vectors. Default is None (no correction).
allow_nans (bool, optional) – If True, allows NaN values in the output for cases where intersection cannot be computed. Default is True.
custom_pointing_vectors (np.ndarray or None, optional) – If provided, overrides the instrument kernel pointing vectors with a custom array of shape (N, 3) or (3,). If None, uses the instrument’s default boresight or pixel vectors. Default is None.
give_geodetic_output (bool, optional) – If True, output geodetic coordinates (longitude, latitude, altitude) instead of ECEF XYZ. Default is False.
give_lat_lon_in_degrees (bool, optional) – If True, longitude and latitude are returned in degrees (if geodetic output is requested). Default is True.
- Returns:
surface_points (pd.DataFrame) – DataFrame of intersection points on the ellipsoid, either in ECEF XYZ (columns: x, y, z) or geodetic coordinates (columns: lon, lat, alt), depending on give_geodetic_output.
spacecraft_positions (pd.DataFrame) – DataFrame of spacecraft positions at each time and pixel, in ECEF XYZ coordinates (columns: x, y, z).
quality_flags (pd.Series) – Series of integer quality flags for each intersection, indicating success or failure modes.
Examples
>>> times = np.array([1_600_000_000.0, 1_600_000_100.0]) >>> surface, sc_pos, flags = compute_ellipsoid_intersection( ... times, ... instrument="MRO_HIRISE", ... custom_pointing_vectors=np.array([0, 0, 1]), ... give_geodetic_output=True ... ) >>> print(surface.head())
- curryer.compute.spatial.instrument_intersect_ellipsoid(ugps_times: numpy.ndarray, instrument: int | str | curryer.spicierpy.obj.Body, correction: str = None, allow_nans=True, boresight_vector=None, geodetic=False, degrees=False) tuple[pandas.DataFrame, pandas.DataFrame, pandas.Series]¶
Geolocate the boresight on the Earth’s surface (WGS84 ellipsoid).
- Parameters:
ugps_times (np.ndarray) – One or more times in GPS microseconds to compute the boresight to WGS84 ellipsoid intersection. Relevant SPICE kernels must be loaded for these times.
instrument (spicierpy.obj.Body or int or str) – Instrument name or ID containing the boresight to process. If boresight_vector is None (default), the SPICE instrument kernel and/or SPICE variables must be loaded; see pixel_vectors.
correction (str, optional) – Type of SPICE perspective correction to use. Default is “None”.
allow_nans (bool, optional) – Convert invalid returns (e.g., data gaps) into NaNs (default), otherwise throws SPICE errors.
boresight_vector (np.ndarray, optional) – Array of one or more boresight vectors in the instrument frame. Default is None, instead loading them from SPICE kernels using pixel_vectors.
geodetic (bool, optional) – If True, intersections are in geodetic lon/lat/alt coordinates, otherwise (default) they are in rectangular x/y/z coordinates.
degrees (bool, optional) – Returns geodetic coordinates in degrees instead of radians (default). Ignored if geodetic is False (default).
- Returns:
pd.DataFrame – Ellipsoidal intersection in rectangular coordinates, or geodetic lon/lat/alt if geodetic was True. The latter defaults to radians unless degrees was True. The former and “alt” are in kilometers. Invalid intersections are set to NaN unless allow_nans was False. The index is the product of ugps_times and boresight_vector.
pd.DataFrame – Spacecraft position in ECEF as rectangular x/y/z coordinates. The index is the product of ugps_times and boresight_vector.
pd.Series – Quality flags indicating why one or both of the other returns were set to NaNs (e.g., missing kernel data, failed to intersect ellipsoid). The index is the product of ugps_times and boresight_vector.
- curryer.compute.spatial.ray_intersect_ellipsoid(vector: numpy.ndarray, position: numpy.ndarray, geodetic=False, degrees=False, a: float = None, b: float = None, e2: float = None) numpy.ndarray¶
Intersect a pointing vector to an ellipsoid (vectorized).
- Parameters:
vector (np.ndarray) – Array of pointing vectors in an ellipsoid-centered-fixed reference frame (i.e., ECEF for EGS84). Units must match a; default is kilometers.
position (np.ndarray) – Array of position vectors in an ellipsoid-centered-fixed reference frame. Must either be a single vector or the same shape as vector. Units must match vector.
geodetic (bool, optional) – If True, intersections are in geodetic lon/lat/alt coordinates, otherwise (default) they are in rectangular x/y/z coordinates.
degrees (bool, optional) – Returns geodetic coordinates in degrees instead of radians (default). Ignored if geodetic is False (default).
a (float, optional) – Ellipsoid’s major axis. Default is WGS84 in kilometers.
b (float, optional) – Ellipsoid’s minor axis. Default is WGS84 in kilometers.
e2 (float, optional) – Ellipsoid’s squared eccentricity. Default is WGS84 in kilometers.
- Returns:
Ellipsoidal intersection in rectangular coordinates, or geodetic lon/lat/alt if geodetic was True. The latter defaults to radians unless degrees was True. The former and “alt” are in the same units as the vector input (default is kilometer). Non-intersections are set to NaNs.
- Return type:
np.ndarray
References
- curryer.compute.spatial.ecef_to_geodetic(xyz: numpy.ndarray, meters=False, degrees=True) numpy.ndarray¶
Convert Earth Centered Earth Fixed rectangular coordinates to geodetic latitude longitude and altitude (WGS84 ellipsoid).
Vectorized implementation (e.g., 1k points takes ~0.2 ms, vs. SPICE ~20ms).
- Parameters:
xyz (np.ndarray) – Rectangular coordinates in ECEF.
meters (bool, optional) – If True, inputs are in meters, otherwise (default) kilometers.
degrees (bool, optional) – If True (default), outputs are in degrees, otherwise radians.
- Returns:
Geodetic latitude longitude and altitude (WGS84 ellipsoid).
- Return type:
np.ndarray
References
https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#The_application_of_Ferrari’s_solution
- curryer.compute.spatial.geodetic_to_ecef(lon_lat_alt: numpy.ndarray, meters=False, degrees=True) numpy.ndarray¶
Convert geodetic latitude longitude and altitude (WGS84 ellipsoid) to Earth Centered Earth Fixed rectangular coordinates (vectorized).
- Parameters:
lon_lat_alt (np.ndarray) – Geodetic latitude longitude and altitude (WGS84 ellipsoid).
meters (bool, optional) – If True, outputs are in meters, otherwise (default) kilometers.
degrees (bool, optional) – If True (default), inputs are in degrees, otherwise radians.
- Returns:
Rectangular coordinates in ECEF.
- Return type:
np.ndarray
References
https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#From_geodetic_to_ECEF_coordinates
- curryer.compute.spatial.terrain_correct(elev: curryer.compute.elevation.Elevation, ec_srf_pos: numpy.ndarray, ec_sat_pos: numpy.ndarray, local_minmax: tuple[float, float] = None) tuple[numpy.ndarray, numpy.ndarray]¶
Perform terrain correction on ellipsoidal intersections (vectorized).
- Parameters:
elev (elevation.Elevation) – Source of geoid and orthometric heights in kilometers and degrees.
ec_srf_pos (np.ndarray) – Surface position in Earth Centered Earth Fixed rectangular coordinates in kilometers.
ec_sat_pos (np.ndarray) – Spacecraft position in Earth Centered Earth Fixed rectangular coordinates in kilometers.
local_minmax ((float, float), optional) – Assumed local min and max elevation in kilometers, otherwise (default) query the min/max from elev.
- Returns:
np.ndarray – Corrected surface position in geodetic latitude longitude and altitude. Latitude and latitude are in degrees. Altitude is kilometers above the WGS84 ellipsoid (geoid height + orthometric (DEM) height). Points that can not be corrected (e.g., >85 degree off-nadir) are set to NaNs.
np.ndarray – Quality flags indicating why the correction failed.
References
- curryer.compute.spatial.calc_azimuth(obs_position: numpy.ndarray, trg_position: numpy.ndarray, degrees=False, signed=False) numpy.ndarray¶
Compute the azimuth angle (vectorized).
- Parameters:
obs_position (np.ndarray) – Observer (surface) positions in rectangular coordinates.
trg_position (np.ndarray) – Target positions in rectangular coordinates. Must be a single point or the same length as obs_position.
degrees (bool, optional) – If True, returns are in degrees, otherwise (default) radians.
signed (bool, optional) – Return azimuth as a signed value ranging from 0 (North), 90 (East), 180/-180 (South), and -90 (West). Default is 0 to 360 clockwise.
- Returns:
Azimuth angle between the observer, target and +Z-axis.
- Return type:
np.ndarray
- curryer.compute.spatial.calc_zenith(obs_position: numpy.ndarray, trg_position: numpy.ndarray, degrees=False, geocentric=False) numpy.ndarray¶
Compute the zenith angle (vectorized).
- Parameters:
obs_position (np.ndarray) – Observer (surface) positions in rectangular coordinates.
trg_position (np.ndarray) – Target positions in rectangular coordinates. Must be a single point or the same length as obs_position.
degrees (bool, optional) – If True, returns are in degrees, otherwise (default) radians.
geocentric (bool, optional) – Compute geocentric zenith (angle between target and body center), or geodetic zenith (angle between target and surface normal).
- Returns:
Zenith angle between the observer, target and reference frame center.
- Return type:
np.ndarray
- curryer.compute.spatial.surface_angles(surface_positions: pandas.DataFrame, target_positions: pandas.DataFrame = None, target_obj: int | str | curryer.spicierpy.obj.Body = None, degrees=False, allow_nans=False, signed=False, geocentric=False) pandas.DataFrame¶
Compute the azimuth and zenith surface angles (vectorized).
- Parameters:
surface_positions (pd.DataFrame) – Surface positions in rectangular coordinates. Index must be time in GPS microseconds if target_positions is not supplied.
target_positions (pd.DataFrame, optional) – Target positions in rectangular coordinates. Required unless target_obj is supplied. Must be a single point or the same length as surface_positions.
target_obj (spicierpy.obj.Body or int or str, optional) – Target to query positions for at surface_position times. Typically, a spacecraft or the Sun.
degrees (bool, optional) – If True, returns are in degrees, otherwise (default) radians.
allow_nans (bool, optional) – Convert invalid returns (e.g., data gaps) into NaNs (default), otherwise throws SPICE errors.
signed (bool, optional) – Return azimuth as a signed value ranging from 0 (North), 90 (East), 180/-180 (South), and -90 (West). Default is 0 to 360 clockwise.
geocentric (bool, optional) – Compute geocentric zenith (angle between target and body center), or geodetic zenith (angle between target and surface normal).
- Returns:
Azimuth and zenith angles. Invalid values are set to NaNs unless allow_nans was False.
- Return type:
pd.DataFrame
- curryer.compute.spatial.minmax_lon(lons: numpy.ndarray, degrees=False)¶
Compute min/max longitude, accounting for the international dateline.
- Parameters:
lons (np.ndarray)
degrees (bool, optional) – If True, inputs and returns are in degrees, otherwise (default) radians. Must range -180 to 180, otherwise -pi, pi.
- Returns:
Min and max longitude. Max may be numerically larger than min if it was determined to wrap the international dateline.
- Return type:
float, float
- class curryer.compute.spatial.Geolocate(instrument: str | int | curryer.spicierpy.obj.Body, dem_data_dir=None)¶
High-level class to manage the geolocation processing steps.
- instrument¶
- sc_state_frame¶
- elevation¶
- _step_time = None¶
- _log_step(msg: str, qf_ds: pandas.Series = None)¶
Log an individual processing step, summarizing quality flag results.
- __call__(ugps_times: numpy.ndarray) xarray.Dataset¶
Perform geolocation processing.
- Parameters:
ugps_times (np.ndarray) – One or more times in GPS microseconds to geolocate. Relevant SPICE kernels must already be loaded into memory.
- Returns:
Geolocation results and ancillary statistics.
- Return type:
xr.Dataset
- intersect_ellipsoid(ugps_times: numpy.ndarray) tuple[pandas.DataFrame, pandas.DataFrame, pandas.Series]¶
Intersect the pixels to the WGS84 ellipsoid.
- Parameters:
ugps_times (np.ndarray) – One or more times in GPS microseconds.
- Returns:
pd.DataFrame – Ellipsoidal intersection in geodetic lon/lat/alt coordinates (degrees, kilometers). Invalid intersections are set to NaN. The index is the product of ugps_times and pixels across-track.
pd.DataFrame – Spacecraft position in ECEF as rectangular x/y/z coordinates. The index is the product of ugps_times and pixels across-track.
pd.Series – Quality flags indicating why one or both of the other returns were NaNs (e.g., missing kernel data, failed to intersect ellipsoid). The index is the product of ugps_times and pixels across-track.
- correct_terrain(ellips_lla_df: pandas.DataFrame, sc_xyz_df: pandas.DataFrame, ellips_qf_ds: pandas.Series, pad_degrees: float = 1.0) tuple[pandas.DataFrame, pandas.Series]¶
Perform terrain correction on ellipsoidal intersections.
- Parameters:
ellips_lla_df (pd.DataFrame) – Ellipsoidal intersection in geodetic lon/lat/alt coordinates (degrees, kilometers). Invalid intersections are set to NaN. The index is the product of ugps_times and pixels across-track.
sc_xyz_df (pd.DataFrame) – Spacecraft position in ECEF as rectangular x/y/z coordinates. The index is the product of ugps_times and pixels across-track.
ellips_qf_ds (pd.Series) – Quality flags indicating why one or both of the other returns were NaNs (e.g., missing kernel data, failed to intersect ellipsoid). The index is the product of ugps_times and pixels across-track.
pad_degrees (float, optional) – Number of lon/lat degrees to pad the intersections when loading the regional extent of the surface elevation.
- Returns:
pd.DataFrame – Terrain intersection in geodetic lon/lat/alt coordinates (degrees, kilometers). Invalid intersections are set to NaN. The index matches the input index from ellips_lla_df.
pd.Series – Quality flags indicating why the returns were NaNs. The index matches the input index from ellips_lla_df.
- calc_ancillary(terrain_lla_df: pandas.DataFrame, sc_xyz_df: pandas.DataFrame) tuple[pandas.DataFrame, pandas.DataFrame, pandas.DataFrame, pandas.DataFrame, pandas.Series]¶
Compute ancillary data fields.
- Parameters:
terrain_lla_df (pd.DataFrame) – Terrain intersection in geodetic lon/lat/alt coordinates (degrees, kilometers). Invalid intersections are set to NaN. The index is the product of ugps_times and pixels across-track.
sc_xyz_df (pd.DataFrame) – Spacecraft position in ECEF as rectangular x/y/z coordinates. The index is the product of ugps_times and pixels across-track.
- Returns:
pd.DataFrame – Instrument pointing in frame sc_state_frame (e.g. ECI) in KM. The index matches the input index from terrain_lla_df.
pd.DataFrame – Solar azimuth and zenith angles (degrees). The index matches the input index from terrain_lla_df.
pd.DataFrame – View azimuth and zenith angles (degrees). The index matches the input index from terrain_lla_df.
pd.DataFrame – Spacecraft position (x/y/z), velocity (vx/vy/vz) and attitude (ex/ey/ez). The latter is from the instrument perspective. The index is the UGPS times from terrain_lla_df.
pd.Series – Quality flags indicating why any of the returns were NaNs. The index matches the input index from terrain_lla_df.
- curryer.compute.spatial.spice_angles(ugps_times, surface_positions, target_obj, degrees=False)¶
Compute azimuth and zenith angles using SPICE.
- curryer.compute.spatial.terrain_correct_single(elev: curryer.compute.elevation.Elevation, ec_srf_pos, ec_sat_pos, local_minmax=None)¶
Apply terrain correction to a single point.
- curryer.compute.spatial.legacy_intersect_ellipsoid(ugps_times, instrument, correction=None, allow_nans=True, boresight_vector=None, geodetic=False)¶
Geolocate the boresight on the Earth’s surface (WGS84 ellipsoid).