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