1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
//! Structured failures while resolving an IFC coordinate operation.
use ifc_model::EntityId;
/// Why a project-to-map operation could not be resolved losslessly.
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq)]
pub enum GeorefError {
/// An entity referenced another that the model does not contain.
MissingEntity {
/// The entity holding the dangling reference.
referrer: EntityId,
/// The id that resolved to nothing.
missing: EntityId,
},
/// An entity was not the IFC type the referencing slot requires.
WrongType {
/// The entity that was read.
entity: EntityId,
/// The IFC type the slot requires.
expected: &'static str,
/// The IFC type actually declared.
actual: String,
},
/// A mandatory attribute was absent, so the value cannot be inferred.
MissingAttribute {
/// The entity that was read.
entity: EntityId,
/// Zero-based slot index of the absent attribute.
index: usize,
/// Schema name of the attribute.
name: &'static str,
},
/// A value supplied to an authoring helper was not acceptable.
///
/// Distinct from [`GeorefError::InvalidAttribute`], which names an
/// entity already in the model: at authoring time there is no id
/// yet, so the entity is named by type.
AuthoringInvalid {
/// The entity being authored.
entity: &'static str,
/// The attribute at fault.
attribute: &'static str,
/// What was supplied.
value: String,
},
/// An attribute was present but held the wrong kind of value.
InvalidAttribute {
/// The entity that was read.
entity: EntityId,
/// Zero-based slot index of the offending attribute.
index: usize,
/// Schema name of the attribute.
name: &'static str,
},
/// The coordinate operation is a subtype this crate does not resolve.
///
/// Refused rather than approximated: a non-projected operation would
/// silently produce wrong map coordinates.
UnsupportedOperation {
/// The coordinate operation entity.
entity: EntityId,
/// The IFC type actually declared.
actual: String,
},
/// The map x axis is zero-length or non-finite, so no rotation exists.
DegenerateAxis {
/// The map conversion entity.
entity: EntityId,
},
/// The map scale is zero, negative, or non-finite.
InvalidScale {
/// The map conversion entity.
entity: EntityId,
/// The rejected scale.
value: f64,
},
/// The map unit is not a length unit this crate can reduce to metres.
InvalidUnit {
/// The unit entity.
entity: EntityId,
/// Which expectation failed.
detail: &'static str,
},
/// A unit conversion chain revisited an entity or ran deeper than 16.
///
/// Both guards exist: a repeated id catches a true loop, the depth cap
/// catches an unbounded chain of distinct units.
UnitCycle {
/// The unit entity where the walk was abandoned.
entity: EntityId,
},
/// The model header declares no `FILE_SCHEMA` token.
MissingSchema,
/// The header declares more than one schema, so the profile is ambiguous.
///
/// Refused rather than picking one: the choice changes which coordinate
/// operations are legal.
AmbiguousSchema {
/// Every schema token found in the header.
tokens: Vec<String>,
},
/// The declared schema has no projected georeferencing profile here.
///
/// IFC2X3 declares no georeferencing entities at all; other tokens may be
/// unrecognised.
UnsupportedSchema {
/// The rejected schema token.
token: String,
},
/// Chaining onto a project frame requires that frame to be supplied.
MissingProjectFrame,
/// The supplied project frame is not an invertible rigid transform.
DegenerateProjectFrame,
/// A direction was zero-length or non-finite, so it cannot be normalised.
NonFiniteDirection {
/// The direction entity.
entity: EntityId,
},
}
impl std::fmt::Display for GeorefError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::MissingEntity { referrer, missing } => {
write!(f, "{referrer} references missing {missing}")
}
Self::WrongType {
entity,
expected,
actual,
} => write!(f, "{entity} is {actual}, expected {expected}"),
Self::MissingAttribute {
entity,
index,
name,
} => write!(f, "{entity} is missing {name} at slot {index}"),
Self::InvalidAttribute {
entity,
index,
name,
} => write!(f, "{entity} has invalid {name} at slot {index}"),
Self::AuthoringInvalid {
entity,
attribute,
value,
} => write!(f, "{entity}.{attribute}: {value}"),
Self::UnsupportedOperation { entity, actual } => {
write!(f, "{entity} uses unsupported coordinate operation {actual}")
}
Self::DegenerateAxis { entity } => {
write!(f, "{entity} has a zero-length or non-finite map x axis")
}
Self::InvalidScale { entity, value } => {
write!(f, "{entity} has invalid map scale {value}")
}
Self::InvalidUnit { entity, detail } => {
write!(f, "{entity} has unsupported or invalid map unit: {detail}")
}
Self::UnitCycle { entity } => {
write!(f, "unit conversion chain at {entity} is cyclic or too deep")
}
Self::MissingSchema => {
write!(f, "model header declares no FILE_SCHEMA token")
}
Self::AmbiguousSchema { tokens } => {
write!(f, "model header declares multiple schemas: {tokens:?}")
}
Self::UnsupportedSchema { token } => {
write!(
f,
"schema {token} does not declare IFC georeferencing entities \
or its coordinate-operation profile is not projected here"
)
}
Self::MissingProjectFrame => {
write!(
f,
"chaining a project-to-map operation onto a project frame \
requires that frame to be supplied explicitly"
)
}
Self::DegenerateProjectFrame => {
write!(
f,
"supplied project frame is not an invertible rigid transform \
(zero or non-finite determinant)"
)
}
Self::NonFiniteDirection { entity } => {
write!(f, "{entity} has a non-finite or zero-length direction")
}
}
}
}
impl std::error::Error for GeorefError {}
/// Result of a georeferencing read, carrying [`GeorefError`] on failure.
pub type GeorefResult<T> = Result<T, GeorefError>;