energyml_export/lib.rs
1//! Writing EnergyML geometry out to formats other programs read.
2//!
3//! # A registry, because the list is never finished
4//!
5//! OBJ, GeoJSON and VTK - in three flavours - are here. glTF, PLY, shapefiles
6//! and whatever a particular site needs are not, and hard-coding a `match` over
7//! format names would mean every one of them is a change to this crate. So an exporter is a
8//! [`Exporter`] implementation in a [`Exporters`] registry, exactly as an array
9//! backend and a representation reader are:
10//!
11//! ```no_run
12//! # use energyml_export::{Exporter, Exporters};
13//! # fn example(mine: Box<dyn Exporter>) {
14//! let mut exporters = Exporters::builtin();
15//! exporters.push(mine); // found before the built-ins
16//! # }
17//! ```
18//!
19//! An application adds a format without touching this crate, and a format added
20//! here reaches every caller - including the `energyml export` command, which
21//! lists whatever the registry holds rather than a list of its own.
22//!
23//! # Metadata is configuration, not code
24//!
25//! What each format carries beyond the geometry is an
26//! [`energyml_domain::metadata::Spec`] - a JSON file of key → dotted path,
27//! where a path may cross references into other objects:
28//!
29//! ```json
30//! {
31//! "uuid": "uuid",
32//! "title": "Citation.Title",
33//! "type": "contentType",
34//! "feature": "RepresentedInterpretation.InterpretedFeature.Citation.Title"
35//! }
36//! ```
37//!
38//! One spec, every format: the same keys come out as GeoJSON `properties`, VTK
39//! `FieldData` arrays and OBJ comments. Adding a key is editing a file, not
40//! editing this crate - which is the same argument as the registry, one level
41//! down.
42//!
43//! # What an exporter is given
44//!
45//! A [`SceneObject`]: the shape, the coordinate system, and the identity -
46//! uuid, ETP URI, title, and the interpretation and feature above it. The last
47//! part matters more than it looks. An exported surface that is only triangles
48//! cannot be traced back to what it represents, and tracing back is usually the
49//! reason for exporting.
50//!
51//! # Where the coordinates end up
52//!
53//! Each exporter states the [`Placement`] it wants, and gets it. OBJ asks for
54//! metres, because a mesh viewer wants numbers near the origin in a single
55//! unit. GeoJSON asks for metres and then needs *degrees*, which is a datum
56//! transformation and not arithmetic - so it goes through
57//! [`energyml_domain::reproject`], and **refuses** rather than writing
58//! projected metres into a field the format says is longitude. That refusal is
59//! the honest answer: a GeoJSON with eastings in the longitude column loads
60//! fine and puts the model in the Gulf of Guinea.
61//!
62//! [`SceneObject`]: energyml_geometry::SceneObject
63//! [`Placement`]: energyml_geometry::Placement
64
65#![forbid(unsafe_code)]
66#![warn(missing_docs)]
67
68pub mod csv;
69pub mod error;
70pub mod geojson;
71pub mod obj;
72pub mod registry;
73pub mod shapes;
74pub mod vtk;
75
76pub use error::{Error, Result};
77pub use registry::{Exporter, Exporters, Options};
78pub use shapes::{Selector, Shapes};