pub struct Crs {Show 13 fields
pub uuid: String,
pub kind: &'static str,
pub x_offset: f64,
pub y_offset: f64,
pub z_offset: f64,
pub areal_rotation: f64,
pub vertical_direction: VerticalDirection,
pub vertical_quantity: VerticalQuantity,
pub horizontal_uom: Option<String>,
pub vertical_uom: Option<String>,
pub time_uom: Option<String>,
pub projected: CrsKind,
pub vertical: CrsKind,
}Expand description
A coordinate reference system, read the same way in every version.
Fields§
§uuid: StringThe uuid of the object this was read from.
kind: &'static strIts type name, for a caller that wants to label it.
x_offset: f64Offset of the local origin along the first projected axis, in
horizontal_uom.
y_offset: f64Offset along the second projected axis.
z_offset: f64Offset of the local origin along the local vertical axis - counted
the way vertical_direction counts, in
vertical_uom - from the zero of the vertical
datum.
RESQML 2.0.1’s ZOffset is exactly that. eml 2.3’s
OriginVerticalCoordinate is not: it is written in the base vertical
CRS, its direction and its unit, and read_with converts it - see
the module doc.
areal_rotation: f64Rotation of the local axes, anticlockwise from the first projected axis, in radians - this view’s own convention, which neither release writes.
Both releases state a clockwise angle measured from the projected Y
axis: RESQML 2.0.1’s ArealRotation (“A positive value indicates a
clockwise rotation from the projected Y axis”, RESQML TRG 2.0.1 §2.1
p. 10) and eml 2.3’s Azimuth from grid north. read_with negates
both on the way in.
vertical_direction: VerticalDirectionWhich way the third, local coordinate counts.
eml 2.3’s VerticalAxis.Direction, never the referenced
VerticalCrs’s: that one says which way the datum counts, which is a
different fact (common TRG 2.3 §12.34 p. 338, §12.35 p. 339).
vertical_quantity: VerticalQuantityWhether the third coordinate is a length or a time.
horizontal_uom: Option<String>The unit horizontal coordinates are written in, as the file spells it.
None when the CRS does not say. Not an invitation to assume
metres - see Crs::to_metres.
vertical_uom: Option<String>The unit the third coordinate is written in.
On an eml 2.3 length system, VerticalAxis.Uom - the unit the stored z
is in - and never the VerticalCrs’s uom, which is its datum’s.
On a time system this is not the time unit: RESQML 2.0.1’s
LocalTime3dCrs carries VerticalUom for the vertical reference -
the ZOffset, a length - and states the axis unit separately in
TimeUom. Ask Crs::vertical_axis_uom rather than this field.
time_uom: Option<String>The unit of the time axis, when the system has one.
RESQML 2.0.1 puts it in TimeUom, beside a VerticalUom that is a
length. eml 2.3 has one VerticalAxis.Uom that is the time unit when
IsTime is set, so this stays None there and
Crs::vertical_axis_uom falls back correctly.
projected: CrsKindThe projected system the local one sits in.
vertical: CrsKindThe vertical system.
Implementations§
Source§impl Crs
impl Crs
Sourcepub fn is_component(&self) -> bool
pub fn is_component(&self) -> bool
Whether this is one half of a system rather than a whole one.
eml 2.3 promoted the parts of a coordinate system to top-level objects:
a package contains LocalEngineering2dCrs and VerticalCrs objects
alongside the LocalEngineeringCompoundCrs that joins them. Reading a
part succeeds and gives a perfectly valid half - horizontal placement
with no vertical, or the reverse - which is exactly the sort of thing
that looks like a complete answer.
A caller iterating over every CRS-shaped object in a package wants this; a caller following a representation’s reference never sees one.
Sourcepub fn is_identity(&self) -> bool
pub fn is_identity(&self) -> bool
Whether this CRS moves a point at all.
The common case in practice, and worth short-circuiting: most local CRSs in real files are the identity with a projected system attached.
Sourcepub fn to_projected(&self, point: [f64; 3]) -> [f64; 3]
pub fn to_projected(&self, point: [f64; 3]) -> [f64; 3]
Place a local point in the projected system.
Rotation, then offset, and the vertical flip when the CRS counts
downward - so the result is an elevation whichever way the file was
written. Units are not touched: the result is in the CRS’s own
units, which to_metres normalises.
§The third coordinate
z_offset + z is where the point sits counted along the local axis
from the datum’s zero - both terms count the same way, because
read_with has put the eml 2.3 origin into the axis’s sense. Flipping
a downward count gives the elevation above the vertical datum, the
one convention every caller draws in. With Lutetia’s depth compound -
axis down, NGF-IGN69 counting up, OriginVerticalCoordinate 35
(CRS.xsd l. 517-520: “in the base vertical CRS”) - the offset along
the axis is −35 and a local depth z lands at 35 − z.
This is the local placement only. Going from here to another EPSG
system is crate::reproject.
Sourcepub fn from_projected(&self, point: [f64; 3]) -> [f64; 3]
pub fn from_projected(&self, point: [f64; 3]) -> [f64; 3]
The exact inverse of Crs::to_projected: a placed point back
into this CRS’s local frame.
What a point must go through before being stored in an object that
references this CRS: RESQML geometry is written in the local frame, and
every reader - this workspace’s included - applies the placement on the
way out. Storing already-placed coordinates applies the offsets, the
rotation and the vertical flip a second time at every read - invisible
on a CRS whose origin is (0, 0) and axis is Up, which is every corpus
package, and wrong on a real one. The round trip
from_projected(to_projected(p)) == p is pinned by a test with all
three of an offset, a rotation and a Down axis, because that identity
is the whole reason this function exists - and again on a compound
whose axis and datum count in opposite directions, the case the
conversion of OriginVerticalCoordinate in read_with is about.
Sourcepub fn to_metres(&self, point: [f64; 3]) -> Result<[f64; 3], Error>
pub fn to_metres(&self, point: [f64; 3]) -> Result<[f64; 3], Error>
Place a point and convert it to metres.
The step that is easy to forget and impossible to notice: a file whose
ProjectedUom is ft is a perfectly ordinary file, and reading it as
metres puts the model 3.28 times too far from the origin with every
coordinate still looking plausible.
Errors rather than assuming when a unit is outside the table. A CRS that states no unit at all is taken at its word and left alone - that is the schema’s own default, not a guess.
Sourcepub fn vertical_axis_uom(&self) -> Option<&str>
pub fn vertical_axis_uom(&self) -> Option<&str>
The unit the third coordinate is actually written in.
The one that has to be asked, because the two releases put it in
different fields and getting it wrong is a factor of a thousand: ALWYN’s
seismic model is a 2.0.1 LocalTime3dCrs whose VerticalUom reads m -
perfectly correct, it describes the ZOffset - while the axis itself
is in TimeUom. Reading vertical_uom on a time system and handing it
to a time conversion is how a millisecond becomes a metre.
Sourcepub fn is_time(&self) -> bool
pub fn is_time(&self) -> bool
Whether the third coordinate is a travel time rather than a length.
§Why a viewer has to ask
A time-domain surface has no depth, and nothing in the file gives it one:
the conversion needs a velocity model the package does not carry, and
RESQML deliberately puts the time kind - two-way against one-way -
outside the CRS (TRG §12.9 leaves it to the acquisition activity). So
Crs::to_metres refuses, which is right.
Crs::to_projected does not refuse, and that is the trap. It
applies the offsets and the flip like any other axis and hands back a
number, so a seismic horizon at 2 713 milliseconds goes into a scene
as z = −2 713 and lands among depth objects at −2 713 metres. Nothing
errors, and the two are within about ten percent of each other for real
velocities - which is exactly the range in which a wrong picture is
indistinguishable from a right one.
A caller drawing several objects together therefore has to know which vertical domain each is in, and say so. This is that question.