Geometry and frames
Positions, frames, terrain and the geomagnetic field: grand.geo.
Terrestrial coordinate systems and the conversions between them. |
|
Topography wrapper for GRAND packages. |
|
Geomagnetic field wrapper for GRAND packages |
grand.geo.coordinates
Terrestrial coordinate systems and the conversions between them.
The frames are Geodetic, ECEF, LTP and
GRANDCS; every conversion between a local frame and geodetic passes
through ECEF. See Coordinates and conventions for the conventions, including the two
that most often catch people out: every angle is in degrees, and GRANDCS
\(x\) runs north where an ENU \(x\) runs east.
units:
energy: GeV
time : nanosecond
length: m
voltage: microVolt
Efield: microVolt/m
Bfield: nanoTesla
angles: degree
grammage: g/cm2
Density: g/cm3
frequency: Hz
resistance: Ohm.
- class grand.geo.coordinates.Coordinates(n: int | None = None)[source]
Bases:
ndarrayGeneric container for a coordinates object.
This object created is a standard np.ndarray of size (3, n) where 3 is for 3D coordinates and n is the number of entries.
- class grand.geo.coordinates.CartesianRepresentation(arg: Any = None, x: float | ndarray = None, y: float | ndarray = None, z: float | ndarray = None)[source]
Bases:
CoordinatesGeneric container for cartesian coordinates.
- info()[source]
Prints a short description of the object, for interactive use.
Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.- Returns:
A short description of shape and range, for interactive use.
- Return type:
- property x
Cartesian \(x\) component, in meters.
- property y
Cartesian \(y\) component, in meters.
- property z
Cartesian \(z\) component, in meters.
- cartesian_to_spherical()[source]
Returns this vector as a
SphericalRepresentation.Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.- Returns:
Polar angle, azimuth and radius.
- Return type:
- cartesian_to_horizontal()[source]
Returns this vector as a
HorizontalRepresentation.Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.- Returns:
Azimuth, elevation and norm.
- Return type:
- norm()[source]
Returns the Euclidean length of the vector, in meters.
Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.- Returns:
Euclidean length of each vector, in meters.
- Return type:
ndarray
- class grand.geo.coordinates.SphericalRepresentation(arg: Any = None, theta: float | int | ndarray = None, phi: float | int | ndarray = None, r: float | int | ndarray = None)[source]
Bases:
CoordinatesGeneric container for spherical coordinates.
- property theta
Polar angle from the \(+z\) axis, in degrees.
- property phi
Azimuth from the \(+x\) axis, in degrees.
- property r
Radius, in meters.
- spherical_to_cartesian()[source]
Returns this vector as a
CartesianRepresentation.Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.- Returns:
Components x, y and z.
- Return type:
- spherical_to_horizontal()[source]
Returns this vector as a
HorizontalRepresentation.Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.- Returns:
Azimuth, elevation and norm.
- Return type:
- class grand.geo.coordinates.HorizontalRepresentation(azimuth: float | int | ndarray = None, elevation: float | int | ndarray = None, norm: float | int | ndarray = 1.0)[source]
Bases:
CoordinatesGeneric container for horizontal coordinates.
- property azimuth
Azimuth from geographic north toward east, in degrees.
- property elevation
Elevation above the horizon, in degrees.
- property norm
Length of the vector, in meters.
- horizontal_to_cartesian()[source]
Returns this vector as a
CartesianRepresentation.Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.- Returns:
Components x, y and z.
- Return type:
- horizontal_to_spherical()[source]
Returns this vector as a
SphericalRepresentation.Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.- Returns:
Polar angle, azimuth and radius.
- Return type:
- class grand.geo.coordinates.Horizontal(arg: Any = None, azimuth: float | int | ndarray = None, elevation: float | int | ndarray = None, norm: float | int | ndarray = 1.0, location: Any = Geodetic([[38.88849], [92.28605], [2920.522]]), vector: bool = False)[source]
Bases:
HorizontalRepresentationGeneric container for horizontal coordinates.
azimuth : angle (deg) starting from true North toward East. elevation: angle (deg) from horizontal plane toward zenith.
- horizontal_to_ecef()[source]
Returns this direction in the
ECEFframe.Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.- Returns:
The same direction, Earth-centred.
- Return type:
- horizontal_to_geodetic()[source]
Returns this direction as a geodetic position.
Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.- Returns:
The same direction, as a geodetic position.
- Return type:
- class grand.geo.coordinates.HorizontalVector(*args, **kwargs)[source]
Bases:
HorizontalRepresentationDeprecated alias, merged into
Horizontal.by adding ‘vector’ attribute to reduce code duplication. It takes what
Horizontaltakes:locationraised TypeError here.
- class grand.geo.coordinates.ECEF(arg: Any = None, x: float | int | ndarray = None, y: float | int | ndarray = None, z: float | int | ndarray = None, *args, **kwargs)[source]
Bases:
CartesianRepresentationEarth-Centred, Earth-Fixed Cartesian coordinates, in meters.
A right-handed frame whose origin is the center of the Earth and which rotates with it. Every conversion between a local frame and geodetic coordinates passes through ECEF; it is the common pivot, so that the ellipsoid constants live in one place.
The axes are:
\(x\), through the equator at the prime meridian (latitude 0, longitude 0);
\(y\), through the equator 90 degrees east (latitude 0, longitude 90);
\(z\), through the north pole (latitude 90).
Examples
import numpy as np from grand import Geodetic, ECEF site = Geodetic(latitude=40.98, longitude=93.95, height=1200.0) print(np.round(np.asarray(ECEF(site)).ravel(), 1))
[-332230.4 4811451.1 4161493.6]
- ecef_to_geodetic(reference='GEOID')[source]
Returns this position as latitude, longitude and height.
Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.
- ecef_to_grandcs(location=None)[source]
Returns this position in the
GRANDCSarray frame.Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.
- ecef_to_ltp(ltp)[source]
Returns this position in a local tangent plane,
LTP.Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.
- class grand.geo.coordinates.Geodetic(arg: Any = None, latitude: float | int | ndarray = None, longitude: float | int | ndarray = None, height: float | int | ndarray = None, *args, **kwargs)[source]
Bases:
GeodeticRepresentationA position as latitude, longitude and height.
- Latitude
Degrees north of the equator, from -90 (South Pole) to +90 (North Pole); negative in the southern hemisphere.
- Longitude
Degrees east of the prime meridian (Greenwich), between -360 and 360 (beyond that it is refused). A negative value is stored plus 360, so -10 becomes 350.
- Height
Metres above the WGS-84 ellipsoid, the reference surface of these coordinates; not below -6400 km, the center of the Earth. The ellipsoid is not sea level: the geoid (mean sea level) lies up to about 100 m above or below it, 61 m below at Dunhuang. Heights from other sources – topography, site tables – may be measured from the geoid; see
Referenceandgeoid_undulation().
Conversions to and from the local frames (
LTP,GRANDCS) pass throughECEF, whose origin is the center of the Earth.See also
grand.geo.coordinates.GRANDCSThe array frame.
grand.geo.topography.geoid_undulationBetween the ellipsoid and sea level.
Examples
Conversion to any local frame passes through
ECEF, and the round trip is exact.import numpy as np from grand import Geodetic, ECEF site = Geodetic(latitude=40.98, longitude=93.95, height=1200.0) print("ECEF (m):", np.round(np.asarray(ECEF(site)).ravel(), 1)) print("round trip:", np.round(np.asarray(Geodetic(ECEF(site))).ravel(), 6))
ECEF (m): [-332230.4 4811451.1 4161493.6] round trip: [ 40.98 93.95 1200. ]
- geodetic_to_horizontal(location=None)[source]
Returns this position in horizontal coordinates, as seen from location.
Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.
- geodetic_to_ecef()[source]
Returns this position in the
ECEFframe.Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.- Returns:
The same position, Earth-centred.
- Return type:
- geodetic_to_grandcs(location=None)[source]
Returns this position in the
GRANDCSarray frame.Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.
- geodetic_to_ltp(ltp)[source]
Returns this position in a local tangent plane,
LTP.Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.
- class grand.geo.coordinates.LTP(arg: Any = None, x: float | int | ndarray = None, y: float | int | ndarray = None, z: float | int | ndarray = None, *args, **kwargs)[source]
Bases:
CartesianRepresentationCalculates basis and orgin at a given latitude and longitude.
Basis and origin is calculated in ECEF frame. ‘location’ and ‘orientation’ are required.
See also
grand.geo.coordinates.GRANDCSThe array frame: north, west, up, from magnetic north.
Examples
The same three numbers name different places in different orientations: 1 km along x is east in
'ENU'and north in'NWU':import numpy as np from grand import Geodetic, LTP site = Geodetic(latitude=40.98, longitude=93.95, height=1200.0) for orientation in ("ENU", "NWU"): point = LTP(x=1000.0, y=0.0, z=0.0, location=site, orientation=orientation, magnetic=False) print(orientation, np.round(np.asarray(Geodetic(point)).ravel()[:2], 5))
ENU [40.98 93.96188] NWU [40.989 93.95 ]
- ltp_to_ltp(ltp)[source]
Returns this vector in another local tangent plane.
Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.
- ltp_to_grandcs(location=None)[source]
Returns this vector in the
GRANDCSarray frame.Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.
- ltp_to_ecef()[source]
Returns this vector in the
ECEFframe.Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.- Returns:
The vector, Earth-centred.
- Return type:
- ltp_to_geodetic(reference='GEOID')[source]
Returns this vector as a geodetic position.
Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.
- class grand.geo.coordinates.GRANDCS(arg: Any = None, x: float | int | ndarray = None, y: float | int | ndarray = None, z: float | int | ndarray = None, *args, **kwargs)[source]
Bases:
LTPClass for the GRANDCS coordinate system (cs). This class instantiate coordinates.
in GRANDCS’s coordinate frame. Input can be either x, y, z coordinates value in GRANDCS’s cs or coordinates in ECEF or Geodetic system.
If the input coordinates are in ECEF or Geodetic (or any coordinate system other than GRANDCS’s cs) following procedure is performed. Convert everything to ECEF frame to do all initial calculations and then finally project the positional vector from GRANDCS’s cs origin to it’s basis vectors. Basis for NWU GRANDCS’s cs is calculated based on GRANDCS’s origin. Basis forms a rotational matrix. Transpose of the rotational matrix is it’s inverse. Basis in this case are unit vectors in ECEF frame that describes the NWU direction from GRANDCS’s origin (also in ECEF frame). To convert other cs to GRANDCS’s cs, first transform the coordinates in ECEF frame, then shift the origin of coordinates from ECEF’s origin to GRANDCS’s origin. Now you get a positional vector from GRANDCS’s origin in ECEF frame. Take a dot product of this positional vector with the NWU basis unit vectors to get the coordinates in GRANDCS coordinate system.
Use inverse (transpose) of rotational matrix to convert from GRANDCS cs to ECEF. Then convert from ECEF to Geodetic.
See also
grand.geo.coordinates.LTPA local frame with a chosen orientation.
grand.geo.coordinates.GeodeticLatitude, longitude and height.
Examples
GRANDCS \(x\) runs north, while an ENU \(x\) runs east, so the same three numbers name different places. Neither call raises.
import numpy as np from grand import Geodetic, GRANDCS, LTP, ECEF site = Geodetic(latitude=40.98, longitude=93.95, height=1200.0) a = ECEF(GRANDCS(x=1000.0, y=0.0, z=0.0, location=site)) b = ECEF(LTP(x=1000.0, y=0.0, z=0.0, location=site, orientation="ENU", magnetic=False)) print("%.1f m apart" % np.linalg.norm(np.asarray(a).ravel() - np.asarray(b).ravel()))
1410.6 m apart
- grandcs_to_ecef()[source]
Returns this vector in the
ECEFframe.Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.- Returns:
The vector, Earth-centred.
- Return type:
- grandcs_to_geodetic(reference='GEOID')[source]
Returns this vector as a geodetic position.
Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.
- grandcs_to_ltp(ltp)[source]
Returns this vector in a local tangent plane,
LTP.Every conversion between a local frame and geodetic passes through
ECEF; see Coordinates and conventions.
- class grand.geo.coordinates.Reference(*values)[source]
Bases:
IntEnumReference surface for a height in the geodetic system.
A geodetic height is meaningless without saying what it is measured from.
ELLIPSOIDmeasures from the WGS-84 ellipsoid, the smooth mathematical figure;GEOIDmeasures from mean sea level, which departs from the ellipsoid by up to about 100 m. Confusing the two is a common source of vertical errors at the array.Added in version 0.1.0.
Notes
The same enumeration is defined in
grand.geo.topography. It is repeated here because importing it would make the two modules circular.Examples
A height of 1200 m means two different places depending on which surface it is measured from.
from grand.geo.coordinates import Reference, geoid_undulation print(list(Reference)) print("geoid - ellipsoid at Dunhuang: %.2f m" % geoid_undulation(latitude=40.98, longitude=93.95))
[<Reference.ELLIPSOID: 1>, <Reference.GEOID: 2>] geoid - ellipsoid at Dunhuang: -61.04 m
- ELLIPSOID = 1
- GEOID = 2
- grand.geo.coordinates.geoid_undulation(coordinates=None, latitude=None, longitude=None)[source]
Returns the height of the geoid above the ellipsoid, in meters.
The geoid undulation is what converts between the two references of
Reference: a height above mean sea level plus the undulation at that location is the height above the ellipsoid. Values are read from the EGM96 model shipped indata/egm96.png.- Parameters:
coordinates (Geodetic, ECEF, LTP, GRANDCS or float, optional) – The position; or, as a number, the latitude, with the longitude as the second argument:
geoid_undulation(40.98, 93.95).latitude (float or ndarray, optional) – Geodetic latitude, in degrees.
longitude (float or ndarray, optional) – Geodetic longitude, in degrees.
- Returns:
float or ndarray – Undulation in meters, positive where the geoid lies above the ellipsoid.
A missing angle gives NaN, with a warning.
Examples
from grand.geo.coordinates import geoid_undulation # The GRANDProto300 site at Dunhuang. print("%.2f m" % geoid_undulation(latitude=40.98, longitude=93.95)) print("%.2f m" % geoid_undulation(40.98, 93.95))
-61.04 m -61.04 m
Notes
grand.geo.topography.geoid_undulation()has the same signature and gives the same values; it is repeated here to avoid a circular import.
grand.geo.topography
Topography wrapper for GRAND packages.
- grand.geo.topography.elevation(coordinates, reference: str | None = 'GEOID')[source]
Get the topography elevation, w.r.t. sea level or w.r.t. the ellipsoid.
- grand.geo.topography.distance(position: Any, direction: CartesianRepresentation, maximum_distance: float = None, frame: Any = None)[source]
Get the signed intersection distance with the topography.
- Parameters:
direction (CartesianRepresentation or ECEF) – Direction to travel in, in ECEF unless frame says otherwise.
maximum_distance (float, optional) – Give up beyond this distance, in meters.
frame (LTP, GRANDCS or "ENU", optional) – Frame direction is given in: the axes of an LTP or GRANDCS, or
"ENU"for east, north and up at the (single) starting point. By default it is ECEF.
- Returns:
Distance to the ground, in meters, or NaN if it was not reached.
- Return type:
float or ndarray
- grand.geo.topography.geoid_undulation(coordinates=None, latitude=None, longitude=None)[source]
Get the geoid undulation: the height of the geoid above the ellipsoid.
Same signature and values as
grand.geo.coordinates.geoid_undulation().- Parameters:
coordinates (Geodetic, ECEF, LTP, GRANDCS or float, optional) – Position or positions to evaluate at; or, as a number, the latitude, with the longitude as the second argument.
latitude (float or ndarray, optional) – Degrees north, instead of coordinates.
longitude (float or ndarray, optional) – Degrees east, instead of coordinates.
- Returns:
float or ndarray – Height of the geoid above the ellipsoid, in meters.
A missing angle gives NaN, with a warning.
Examples
Negative here: at Dunhuang the geoid lies below the ellipsoid, so a point 1200 m above the ellipsoid is about 1261 m above sea level.
from grand.geo.topography import geoid_undulation print("%.2f m" % geoid_undulation(latitude=40.98, longitude=93.95))
-61.04 m
- grand.geo.topography.update_data(coordinates=None, clear: bool = False, radius: float = None)[source]
Updates the cache of topography data.
Tiles are published at https://github.com/grand-mother/store/releases and saved locally as
grand/data/topography/*.hgt, one file per one-degree square. They are not in version control.
- grand.geo.topography.cachedir() Path[source]
Get the location of the topography data cache.
- Returns:
Directory the downloaded elevation tiles are cached in.
- Return type:
Path
- grand.geo.topography.model() str[source]
Get the default model for topographic data.
- Returns:
Name of the elevation model in use, such as SRTM.
- Return type:
- class grand.geo.topography.Reference(*values)[source]
Bases:
IntEnumReference level for topography data
- ELLIPSOID = 1
- GEOID = 2
- LOCAL = 3
- class grand.geo.topography.Topography(path: Path | str = PosixPath('/home/runner/work/grand/grand/data/topography'))[source]
Bases:
objectProxy to topography data.
- elevation(coordinates, reference: str | None = 'GEOID')[source]
Get the topography elevation, w.r.t. sea level, w.r.t the ellipsoid or in local coordinates. The default reference is w.r.t sea level (GEOID).
- distance(position: Any, direction: CartesianRepresentation, maximum_distance: float = None, frame: Any = None)[source]
Get the signed intersection distance with the topography.
- Parameters:
direction (CartesianRepresentation or ECEF) – Direction to travel in, in ECEF unless frame says otherwise. A local (east, north, up) vector passed without frame is read as ECEF and gives a wrong distance.
maximum_distance (float, optional) – Give up beyond this distance, in meters.
frame (LTP, GRANDCS or "ENU", optional) – Frame direction is given in: the axes of an LTP or GRANDCS, or
"ENU"for east, north and up at the (single) starting point. By default it is ECEF.
- Returns:
Distance to the ground, in meters.
- Return type:
float or ndarray
grand.geo.geomagnet
Geomagnetic field wrapper for GRAND packages
- grand.geo.geomagnet.field(coordinates: ECEF | Geodetic | GRANDCS | LTP) CartesianRepresentation[source]
Get the default geo-magnetic field at the given coordinates.
- Parameters:
coordinates (Geodetic, ECEF, LTP or GRANDCS) – Where to evaluate the field.
- Returns:
The geomagnetic field vector, in tesla, in the local east-north-up frame at the location, whatever frame coordinates is given in. GRAND data use north-west-up: there, x is this y and y is minus this x. About 5.6e-5 T at Dunhuang.
- Return type:
Examples
The field drives the radio emission, so its direction at the site enters every simulated shower.
import numpy as np from grand import Geodetic from grand.geo.geomagnet import field site = Geodetic(latitude=40.98, longitude=93.95, height=1200.0) b = np.asarray(field(site)).ravel() print("B at Dunhuang (T):", np.round(b, 7)) print("magnitude: %.1f microtesla" % (np.linalg.norm(b) * 1e6))
B at Dunhuang (T): [ 1.00e-07 2.67e-05 -4.97e-05] magnitude: 56.4 microtesla
- class grand.geo.geomagnet.Geomagnet(model: str = None, latitude: float | ndarray = None, longitude: float | ndarray = None, height: float | ndarray = None, location: ECEF | Geodetic | LTP | GRANDCS = None, obstime: str | date = None)[source]
Bases:
objectThe geomagnetic field of a model (‘IGRF13’ by default) at a place and time.
Give the location as latitude (deg), longitude (deg) and height (m), or as an ECEF, Geodetic, LTP or GRANDCS position; a missing location raises TypeError. Give the time as an ISO date (‘2020-01-19’) or a
datetime.date; the default is ‘2020-01-01’.- field
The field in tesla, in the local east-north-up frame at the location, whatever frame the location was given in. GRAND data use north-west-up: there, x is this y and y is minus this x.
- Type:
- declination
Angle of the horizontal field from geographic north, in degrees, positive toward east.
- Type:
float or ndarray