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}