Skip to main content

cityjson_lib/
json.rs

1use std::io::{BufRead, Write};
2use std::path::Path;
3
4pub use cityjson_json::CityJsonSeqWriteReport;
5pub use cityjson_json::RootKind;
6
7use crate::{CityJSONVersion, CityModel, Error, Result};
8
9pub use cityjson_json::v2_0::{
10    CityJsonSeqReader, CityJsonSeqWriteOptions, FeatureStreamTransform,
11    ReadOptions as JsonReadOptions, WriteOptions as JsonWriteOptions, read_feature,
12    read_feature_stream as read_feature_stream_raw,
13    read_feature_with_base as read_feature_with_base_raw, read_model, to_vec as to_vec_raw,
14    write_feature_stream as write_feature_stream_raw, write_model,
15};
16
17pub mod staged {
18    use std::io::Write;
19    use std::path::Path;
20
21    pub use cityjson_json::staged::{FeatureAssembly, FeatureObjectFragment};
22
23    use crate::{CityModel, Error, Result};
24
25    pub fn from_feature_slice_with_base(
26        feature_bytes: &[u8],
27        base_document_bytes: &[u8],
28    ) -> Result<CityModel> {
29        cityjson_json::staged::from_feature_slice_with_base(feature_bytes, base_document_bytes)
30            .map(CityModel::from)
31            .map_err(Error::from)
32    }
33
34    pub fn from_feature_slice_with_base_assume_cityjson_feature_v2_0(
35        feature_bytes: &[u8],
36        base_document_bytes: &[u8],
37    ) -> Result<CityModel> {
38        from_feature_slice_with_base(feature_bytes, base_document_bytes)
39    }
40
41    pub fn from_feature_assembly_with_base(
42        assembly: FeatureAssembly<'_>,
43        base_document_bytes: &[u8],
44    ) -> Result<CityModel> {
45        cityjson_json::staged::from_feature_assembly_with_base(assembly, base_document_bytes)
46            .map(CityModel::from)
47            .map_err(Error::from)
48    }
49
50    pub fn from_feature_file_with_base<P: AsRef<Path>>(
51        path: P,
52        base_document_bytes: &[u8],
53    ) -> Result<CityModel> {
54        cityjson_json::staged::from_feature_file_with_base(path, base_document_bytes)
55            .map(CityModel::from)
56            .map_err(Error::from)
57    }
58
59    pub fn to_feature_writer(writer: &mut impl Write, model: &CityModel) -> Result<()> {
60        cityjson_json::staged::to_feature_writer(writer, model).map_err(Error::from)
61    }
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub struct Probe {
66    kind: RootKind,
67    version: Option<CityJSONVersion>,
68}
69
70impl Probe {
71    pub fn kind(&self) -> RootKind {
72        self.kind
73    }
74
75    pub fn version(&self) -> Option<CityJSONVersion> {
76        self.version
77    }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
81pub struct WriteOptions {
82    pub pretty: bool,
83    pub validate_default_themes: bool,
84}
85
86fn write_options(options: WriteOptions) -> cityjson_json::WriteOptions {
87    cityjson_json::WriteOptions {
88        pretty: options.pretty,
89        validate_default_themes: options.validate_default_themes,
90        trailing_newline: false,
91    }
92}
93
94pub fn probe(bytes: &[u8]) -> Result<Probe> {
95    let probe = cityjson_json::probe(bytes).map_err(Error::from)?;
96    let version = probe.version().map(CityJSONVersion::try_from).transpose()?;
97    Ok(Probe {
98        kind: probe.kind(),
99        version,
100    })
101}
102
103pub fn from_slice_assume_cityjson_v2_0(bytes: &[u8]) -> Result<CityModel> {
104    cityjson_json::read_model(bytes, &cityjson_json::ReadOptions::default())
105        .map(CityModel::from)
106        .map_err(Error::from)
107}
108
109pub fn from_slice(bytes: &[u8]) -> Result<CityModel> {
110    let probe = probe(bytes)?;
111    match probe.kind() {
112        RootKind::CityJSON => match probe.version() {
113            Some(CityJSONVersion::V2_0) => from_slice_assume_cityjson_v2_0(bytes),
114            None => Err(Error::MissingVersion),
115            Some(other) => Err(Error::UnsupportedVersion {
116                found: other.to_string(),
117                supported: CityJSONVersion::V2_0.to_string(),
118            }),
119        },
120        RootKind::CityJSONFeature => Err(Error::ExpectedCityJSON(probe.kind().to_string())),
121    }
122}
123
124pub fn from_file<P: AsRef<Path>>(path: P) -> Result<CityModel> {
125    let path = path.as_ref();
126    match path.extension().and_then(|ext| ext.to_str()) {
127        Some("jsonl") => Err(Error::UnsupportedFeature(
128            "CityJSONFeature streams must be read with json::read_feature_stream".into(),
129        )),
130        _ => from_slice(&std::fs::read(path)?),
131    }
132}
133
134pub fn from_feature_slice(bytes: &[u8]) -> Result<CityModel> {
135    let probe = probe(bytes)?;
136    match probe.kind() {
137        RootKind::CityJSON => Err(Error::ExpectedCityJSONFeature(probe.kind().to_string())),
138        RootKind::CityJSONFeature => from_feature_slice_assume_cityjson_feature_v2_0(bytes),
139    }
140}
141
142pub fn from_feature_file<P: AsRef<Path>>(path: P) -> Result<CityModel> {
143    from_feature_slice(&std::fs::read(path)?)
144}
145
146pub fn from_feature_slice_assume_cityjson_feature_v2_0(bytes: &[u8]) -> Result<CityModel> {
147    cityjson_json::read_feature(bytes, &cityjson_json::ReadOptions::default())
148        .map(CityModel::from)
149        .map_err(Error::from)
150}
151
152pub fn read_feature_stream<R>(reader: R) -> Result<impl Iterator<Item = Result<CityModel>>>
153where
154    R: BufRead,
155{
156    let iter = cityjson_json::read_feature_stream(reader, &cityjson_json::ReadOptions::default())?;
157    Ok(iter.map(|item| item.map(CityModel::from).map_err(Error::from)))
158}
159
160pub fn read_cityjsonseq<R>(reader: R) -> Result<impl Iterator<Item = Result<CityModel>>>
161where
162    R: BufRead,
163{
164    read_feature_stream(reader)
165}
166
167pub fn write_feature_stream<I, W>(mut writer: W, models: I) -> Result<()>
168where
169    I: IntoIterator<Item = CityModel>,
170    W: Write,
171{
172    for model in models {
173        staged::to_feature_writer(&mut writer, &model).map_err(Error::from)?;
174        writer.write_all(b"\n")?;
175    }
176    Ok(())
177}
178
179pub fn write_feature_stream_refs<'a, I, W>(mut writer: W, models: I) -> Result<()>
180where
181    I: IntoIterator<Item = &'a CityModel>,
182    W: Write,
183{
184    for model in models {
185        staged::to_feature_writer(&mut writer, model).map_err(Error::from)?;
186        writer.write_all(b"\n")?;
187    }
188    Ok(())
189}
190
191pub fn write_cityjsonseq<I, W>(
192    writer: W,
193    base_root: &CityModel,
194    features: I,
195    transform: &cityjson::v2_0::Transform,
196) -> Result<CityJsonSeqWriteReport>
197where
198    I: IntoIterator<Item = CityModel>,
199    W: Write,
200{
201    let features = features.into_iter().collect::<Vec<_>>();
202    write_cityjsonseq_refs(writer, base_root, features.iter(), transform)
203}
204
205pub fn write_cityjsonseq_refs<'a, I, W>(
206    writer: W,
207    _base_root: &CityModel,
208    features: I,
209    transform: &cityjson::v2_0::Transform,
210) -> Result<CityJsonSeqWriteReport>
211where
212    I: IntoIterator<Item = &'a CityModel>,
213    W: Write,
214{
215    let options = cityjson_json::CityJsonSeqWriteOptions {
216        transform: cityjson_json::FeatureStreamTransform::Explicit(transform.clone()),
217        ..cityjson_json::CityJsonSeqWriteOptions::default()
218    };
219    cityjson_json::write_feature_stream(writer, features.into_iter().cloned(), &options)
220        .map_err(Error::from)
221}
222
223pub fn write_cityjsonseq_auto_transform<I, W>(
224    writer: W,
225    base_root: &CityModel,
226    features: I,
227    scale: [f64; 3],
228) -> Result<CityJsonSeqWriteReport>
229where
230    I: IntoIterator<Item = CityModel>,
231    W: Write,
232{
233    let features = features.into_iter().collect::<Vec<_>>();
234    write_cityjsonseq_auto_transform_refs(writer, base_root, features.iter(), scale)
235}
236
237pub fn write_cityjsonseq_auto_transform_refs<'a, I, W>(
238    writer: W,
239    _base_root: &CityModel,
240    features: I,
241    scale: [f64; 3],
242) -> Result<CityJsonSeqWriteReport>
243where
244    I: IntoIterator<Item = &'a CityModel>,
245    W: Write,
246{
247    let options = cityjson_json::CityJsonSeqWriteOptions {
248        transform: cityjson_json::FeatureStreamTransform::Auto { scale },
249        ..cityjson_json::CityJsonSeqWriteOptions::default()
250    };
251    cityjson_json::write_feature_stream(writer, features.into_iter().cloned(), &options)
252        .map_err(Error::from)
253}
254
255pub fn to_vec(model: &CityModel) -> Result<Vec<u8>> {
256    cityjson_json::to_vec(model, &write_options(WriteOptions::default())).map_err(Error::from)
257}
258
259pub fn to_vec_with_options(model: &CityModel, options: WriteOptions) -> Result<Vec<u8>> {
260    cityjson_json::to_vec(model, &write_options(options)).map_err(Error::from)
261}
262
263pub fn to_string(model: &CityModel) -> Result<String> {
264    String::from_utf8(to_vec(model)?).map_err(|error| Error::Import(error.to_string()))
265}
266
267pub fn to_string_with_options(model: &CityModel, options: WriteOptions) -> Result<String> {
268    String::from_utf8(to_vec_with_options(model, options)?)
269        .map_err(|error| Error::Import(error.to_string()))
270}
271
272pub fn to_writer(writer: &mut impl Write, model: &CityModel) -> Result<()> {
273    cityjson_json::write_model(writer, model, &write_options(WriteOptions::default()))
274        .map_err(Error::from)
275}
276
277pub fn to_writer_with_options(
278    writer: &mut impl Write,
279    model: &CityModel,
280    options: WriteOptions,
281) -> Result<()> {
282    cityjson_json::write_model(writer, model, &write_options(options)).map_err(Error::from)
283}
284
285pub fn to_feature_string(model: &CityModel) -> Result<String> {
286    to_feature_string_with_options(model, WriteOptions::default())
287}
288
289pub fn to_feature_vec_with_options(model: &CityModel, options: WriteOptions) -> Result<Vec<u8>> {
290    Ok(to_feature_string_with_options(model, options)?.into_bytes())
291}
292
293pub fn to_feature_string_with_options(model: &CityModel, options: WriteOptions) -> Result<String> {
294    match model.type_citymodel() {
295        cityjson::CityModelType::CityJSONFeature => to_string_with_options(model, options),
296        other => Err(Error::UnsupportedType(other.to_string())),
297    }
298}
299
300pub fn to_feature_writer(writer: &mut impl Write, model: &CityModel) -> Result<()> {
301    staged::to_feature_writer(writer, model).map_err(Error::from)
302}
303
304pub fn merge_feature_stream_slice(bytes: &[u8]) -> Result<CityModel> {
305    cityjson_json::merge_feature_stream_slice(bytes).map_err(Error::from)
306}
307
308pub fn merge_cityjsonseq_slice(bytes: &[u8]) -> Result<CityModel> {
309    cityjson_json::merge_cityjsonseq_slice(bytes).map_err(Error::from)
310}