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}