pub(crate) mod map_conversion {
pub const SOURCE_CRS: usize = 0;
pub const TARGET_CRS: usize = 1;
pub const EASTINGS: usize = 2;
pub const NORTHINGS: usize = 3;
pub const ORTHOGONAL_HEIGHT: usize = 4;
pub const X_AXIS_ABSCISSA: usize = 5;
pub const X_AXIS_ORDINATE: usize = 6;
pub const SCALE: usize = 7;
}
pub(crate) mod site {
pub const REF_LATITUDE: usize = 9;
pub const REF_LONGITUDE: usize = 10;
pub const REF_ELEVATION: usize = 11;
}
pub(crate) mod map_conversion_scaled {
pub const FACTOR_X: usize = 8;
pub const FACTOR_Y: usize = 9;
pub const FACTOR_Z: usize = 10;
}
pub(crate) mod rigid_operation {
pub const FIRST_COORDINATE: usize = 2;
pub const SECOND_COORDINATE: usize = 3;
pub const HEIGHT: usize = 4;
}
pub(crate) mod geographic_crs {
pub const PRIME_MERIDIAN: usize = 3;
pub const ANGLE_UNIT: usize = 4;
pub const HEIGHT_UNIT: usize = 5;
}
pub(crate) mod projected_crs {
pub const NAME: usize = 0;
pub const DESCRIPTION: usize = 1;
pub const GEODETIC_DATUM: usize = 2;
pub const VERTICAL_DATUM: usize = 3;
pub const MAP_PROJECTION: usize = 4;
pub const MAP_ZONE: usize = 5;
pub const MAP_UNIT: usize = 6;
}
pub(crate) mod well_known_text {
pub const WELL_KNOWN_TEXT: usize = 0;
pub const COORDINATE_REFERENCE_SYSTEM: usize = 1;
}
pub(crate) mod geometric_context {
pub const TRUE_NORTH: usize = 5;
}
pub(crate) mod direction {
pub const DIRECTION_RATIOS: usize = 0;
}
pub(crate) mod si_unit {
pub const UNIT_TYPE: usize = 1;
pub const PREFIX: usize = 2;
pub const NAME: usize = 3;
}
pub(crate) mod conversion_based_unit {
pub const UNIT_TYPE: usize = 1;
pub const NAME: usize = 2;
pub const CONVERSION_FACTOR: usize = 3;
}
pub(crate) mod measure_with_unit {
pub const VALUE_COMPONENT: usize = 0;
pub const UNIT_COMPONENT: usize = 1;
}
#[cfg(test)]
mod tests {
use ifc_schema::{ifc2x3, ifc4, ifc4x3, Schema};
use super::*;
type Layout = (&'static str, usize, &'static [(usize, &'static str)]);
const LAYOUTS: &[Layout] = &[
(
"IfcMapConversion",
8,
&[
(map_conversion::SOURCE_CRS, "SourceCRS"),
(map_conversion::TARGET_CRS, "TargetCRS"),
(map_conversion::EASTINGS, "Eastings"),
(map_conversion::NORTHINGS, "Northings"),
(map_conversion::ORTHOGONAL_HEIGHT, "OrthogonalHeight"),
(map_conversion::X_AXIS_ABSCISSA, "XAxisAbscissa"),
(map_conversion::X_AXIS_ORDINATE, "XAxisOrdinate"),
(map_conversion::SCALE, "Scale"),
],
),
(
"IfcProjectedCRS",
7,
&[
(projected_crs::NAME, "Name"),
(projected_crs::DESCRIPTION, "Description"),
(projected_crs::GEODETIC_DATUM, "GeodeticDatum"),
(projected_crs::VERTICAL_DATUM, "VerticalDatum"),
(projected_crs::MAP_PROJECTION, "MapProjection"),
(projected_crs::MAP_ZONE, "MapZone"),
(projected_crs::MAP_UNIT, "MapUnit"),
],
),
(
"IfcGeometricRepresentationContext",
6,
&[(geometric_context::TRUE_NORTH, "TrueNorth")],
),
(
"IfcDirection",
1,
&[(direction::DIRECTION_RATIOS, "DirectionRatios")],
),
(
"IfcSIUnit",
4,
&[
(si_unit::UNIT_TYPE, "UnitType"),
(si_unit::PREFIX, "Prefix"),
(si_unit::NAME, "Name"),
],
),
(
"IfcConversionBasedUnit",
4,
&[
(conversion_based_unit::UNIT_TYPE, "UnitType"),
(conversion_based_unit::NAME, "Name"),
(conversion_based_unit::CONVERSION_FACTOR, "ConversionFactor"),
],
),
(
"IfcMeasureWithUnit",
2,
&[
(measure_with_unit::VALUE_COMPONENT, "ValueComponent"),
(measure_with_unit::UNIT_COMPONENT, "UnitComponent"),
],
),
];
fn assert_layouts(schema: &Schema) {
let release = schema.name();
for (entity, arity, slots) in LAYOUTS {
assert!(
schema.entity(entity).is_some(),
"{release} does not declare {entity}"
);
let names = schema.attribute_names(entity);
assert_eq!(names.len(), *arity, "{release} {entity} arity: {names:?}");
for (slot, name) in *slots {
assert_eq!(
names.get(*slot).copied(),
Some(*name),
"{release} {entity} slot {slot}: {names:?}"
);
}
}
}
#[test]
fn slots_match_the_bundled_ifc4_schema() {
assert_layouts(ifc4());
}
#[test]
fn slots_match_the_bundled_ifc4x3_schema() {
assert_layouts(ifc4x3());
}
#[test]
fn vertical_datum_moves_between_versions_without_moving_its_slot() {
let crs = "IfcCoordinateReferenceSystem";
assert_eq!(
ifc4().attribute_names(crs),
["Name", "Description", "GeodeticDatum", "VerticalDatum"]
);
assert_eq!(
ifc4x3().attribute_names(crs),
["Name", "Description", "GeodeticDatum"]
);
}
#[test]
fn ifc4x3_only_operations_are_absent_from_ifc4_and_laid_out_in_ifc4x3() {
for entity in [
"IfcMapConversionScaled",
"IfcRigidOperation",
"IfcGeographicCRS",
] {
assert!(ifc4().entity(entity).is_none(), "IFC4 declares {entity}");
assert!(ifc4x3().entity(entity).is_some(), "IFC4X3 lacks {entity}");
}
let scaled = ifc4x3().attribute_names("IfcMapConversionScaled");
assert_eq!(
scaled[..8],
ifc4x3().attribute_names("IfcMapConversion")[..],
"IfcMapConversionScaled must extend IfcMapConversion's layout"
);
assert_eq!(scaled[8..], ["FactorX", "FactorY", "FactorZ"]);
assert_eq!(scaled[map_conversion_scaled::FACTOR_X], "FactorX");
assert_eq!(scaled[map_conversion_scaled::FACTOR_Y], "FactorY");
assert_eq!(scaled[map_conversion_scaled::FACTOR_Z], "FactorZ");
let rigid = ifc4x3().attribute_names("IfcRigidOperation");
assert_eq!(rigid[map_conversion::SOURCE_CRS], "SourceCRS");
assert_eq!(rigid[map_conversion::TARGET_CRS], "TargetCRS");
assert_eq!(rigid[rigid_operation::FIRST_COORDINATE], "FirstCoordinate");
assert_eq!(
rigid[rigid_operation::SECOND_COORDINATE],
"SecondCoordinate"
);
assert_eq!(rigid[rigid_operation::HEIGHT], "Height");
let geographic = ifc4x3().attribute_names("IfcGeographicCRS");
assert_eq!(geographic[projected_crs::NAME], "Name");
assert_eq!(geographic[projected_crs::DESCRIPTION], "Description");
assert_eq!(geographic[projected_crs::GEODETIC_DATUM], "GeodeticDatum");
assert_eq!(geographic[geographic_crs::PRIME_MERIDIAN], "PrimeMeridian");
assert_eq!(geographic[geographic_crs::ANGLE_UNIT], "AngleUnit");
assert_eq!(geographic[geographic_crs::HEIGHT_UNIT], "HeightUnit");
assert!(ifc4().entity("IfcWellKnownText").is_none());
let wkt = ifc4x3().attribute_names("IfcWellKnownText");
assert_eq!(wkt.len(), 2);
assert_eq!(
wkt[well_known_text::WELL_KNOWN_TEXT],
"WellKnownText",
"{wkt:?}"
);
assert_eq!(
wkt[well_known_text::COORDINATE_REFERENCE_SYSTEM],
"CoordinateReferenceSystem",
"{wkt:?}"
);
assert_eq!(
ifc4x3().attribute_names("IfcRigidOperation"),
[
"SourceCRS",
"TargetCRS",
"FirstCoordinate",
"SecondCoordinate",
"Height"
]
);
assert_eq!(
ifc4x3().attribute_names("IfcGeographicCRS"),
[
"Name",
"Description",
"GeodeticDatum",
"PrimeMeridian",
"AngleUnit",
"HeightUnit"
]
);
}
#[test]
fn site_reference_slots_match_ifc2x3_ifc4_and_ifc4x3() {
for schema in [ifc2x3(), ifc4(), ifc4x3()] {
let names = schema.attribute_names("IfcSite");
assert_eq!(
names.len(),
14,
"{} IfcSite arity: {names:?}",
schema.name()
);
assert_eq!(names[site::REF_LATITUDE], "RefLatitude", "{names:?}");
assert_eq!(names[site::REF_LONGITUDE], "RefLongitude", "{names:?}");
assert_eq!(names[site::REF_ELEVATION], "RefElevation", "{names:?}");
}
}
#[test]
fn ifc2x3_declares_no_coordinate_operation() {
for entity in [
"IfcCoordinateOperation",
"IfcMapConversion",
"IfcCoordinateReferenceSystem",
"IfcProjectedCRS",
] {
assert!(
ifc2x3().entity(entity).is_none(),
"IFC2X3 declares {entity}"
);
}
}
}