Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implementation of OGC Features and Geometries JSON (JSON-FG)
5 : * Author: Even Rouault <even.rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2023, Even Rouault <even.rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "ogr_jsonfg.h"
14 : #include "cpl_time.h"
15 : #include "ogrlibjsonutils.h" // OGRJSonParse()
16 :
17 : #include <algorithm>
18 :
19 : /************************************************************************/
20 : /* OGRJSONFGWriteLayer() */
21 : /************************************************************************/
22 :
23 123 : OGRJSONFGWriteLayer::OGRJSONFGWriteLayer(
24 : const char *pszName, const OGRSpatialReference *poSRS,
25 : std::unique_ptr<OGRCoordinateTransformation> &&poCTToWGS84,
26 : const std::string &osCoordRefSys, OGRwkbGeometryType eGType,
27 123 : CSLConstList papszOptions, OGRJSONFGDataset *poDS)
28 123 : : poDS_(poDS), poFeatureDefn_(new OGRFeatureDefn(pszName)),
29 246 : poCTToWGS84_(std::move(poCTToWGS84)), osCoordRefSys_(osCoordRefSys)
30 : {
31 123 : poFeatureDefn_->Reference();
32 123 : poFeatureDefn_->SetGeomType(eGType);
33 123 : if (eGType != wkbNone && poSRS)
34 : {
35 66 : auto poSRSClone = poSRS->Clone();
36 66 : poFeatureDefn_->GetGeomFieldDefn(0)->SetSpatialRef(poSRSClone);
37 66 : poSRSClone->Release();
38 66 : m_bMustSwapForPlace = OGRJSONFGMustSwapXY(poSRS);
39 : }
40 123 : SetDescription(poFeatureDefn_->GetName());
41 :
42 123 : bIsWGS84CRS_ =
43 123 : osCoordRefSys_.find("\"http://www.opengis.net/def/crs/OGC/0/CRS84\"") !=
44 123 : std::string::npos ||
45 123 : osCoordRefSys_.find(
46 : "\"http://www.opengis.net/def/crs/OGC/0/CRS84h\"") !=
47 122 : std::string::npos ||
48 122 : osCoordRefSys_.find("\"http://www.opengis.net/def/crs/EPSG/0/4326\"") !=
49 246 : std::string::npos ||
50 106 : osCoordRefSys_.find("\"http://www.opengis.net/def/crs/EPSG/0/4979\"") !=
51 : std::string::npos;
52 :
53 123 : oWriteOptions_.nXYCoordPrecision = atoi(CSLFetchNameValueDef(
54 : papszOptions, "XY_COORD_PRECISION_GEOMETRY", "-1"));
55 123 : oWriteOptions_.nZCoordPrecision = atoi(
56 : CSLFetchNameValueDef(papszOptions, "Z_COORD_PRECISION_GEOMETRY", "-1"));
57 123 : oWriteOptions_.nSignificantFigures =
58 123 : atoi(CSLFetchNameValueDef(papszOptions, "SIGNIFICANT_FIGURES", "-1"));
59 123 : oWriteOptions_.SetRFC7946Settings();
60 123 : oWriteOptions_.SetIDOptions(papszOptions);
61 :
62 123 : oWriteOptionsPlace_.nXYCoordPrecision = atoi(
63 : CSLFetchNameValueDef(papszOptions, "XY_COORD_PRECISION_PLACE", "-1"));
64 123 : oWriteOptionsPlace_.nZCoordPrecision = atoi(
65 : CSLFetchNameValueDef(papszOptions, "Z_COORD_PRECISION_PLACE", "-1"));
66 123 : oWriteOptionsPlace_.nSignificantFigures =
67 123 : atoi(CSLFetchNameValueDef(papszOptions, "SIGNIFICANT_FIGURES", "-1"));
68 123 : oWriteOptionsPlace_.bAllowCurve = true;
69 123 : oWriteOptionsPlace_.bAllowMeasure = true;
70 :
71 123 : bWriteFallbackGeometry_ = CPLTestBool(
72 : CSLFetchNameValueDef(papszOptions, "WRITE_GEOMETRY", "TRUE"));
73 :
74 123 : osMeasureUnit_ = CSLFetchNameValueDef(papszOptions, "MEASURE_UNIT", "");
75 : osMeasureDescription_ =
76 123 : CSLFetchNameValueDef(papszOptions, "MEASURE_DESCRIPTION", "");
77 :
78 123 : VSILFILE *fp = poDS_->GetOutputFile();
79 123 : if (poDS_->IsSingleOutputLayer())
80 : {
81 15 : auto poFeatureType = json_object_new_string(pszName);
82 15 : VSIFPrintfL(fp, "\"featureType\" : %s,\n",
83 : json_object_to_json_string_ext(poFeatureType,
84 : JSON_C_TO_STRING_SPACED));
85 15 : json_object_put(poFeatureType);
86 15 : if (!osCoordRefSys.empty())
87 15 : VSIFPrintfL(fp, "\"coordRefSys\" : %s,\n", osCoordRefSys.c_str());
88 :
89 15 : if (!osMeasureUnit_.empty() || !osMeasureDescription_.empty())
90 : {
91 3 : m_bMeasureWritten = true;
92 3 : bLayerLevelMeasuresWritten_ = true;
93 3 : VSIFPrintfL(fp, "\"measures\": {\n");
94 3 : VSIFPrintfL(fp, " \"enabled\": true");
95 3 : if (!osMeasureUnit_.empty())
96 : {
97 3 : auto poUnit = json_object_new_string(osMeasureUnit_.c_str());
98 3 : VSIFPrintfL(fp, ",\n \"unit\": %s",
99 : json_object_to_json_string_ext(
100 : poUnit, JSON_C_TO_STRING_SPACED));
101 3 : json_object_put(poUnit);
102 : }
103 3 : if (!osMeasureDescription_.empty())
104 : {
105 : auto poDescription =
106 3 : json_object_new_string(osMeasureDescription_.c_str());
107 3 : VSIFPrintfL(fp, ",\n \"description\": %s",
108 : json_object_to_json_string_ext(
109 : poDescription, JSON_C_TO_STRING_SPACED));
110 3 : json_object_put(poDescription);
111 : }
112 3 : VSIFPrintfL(fp, "\n},\n");
113 : }
114 : }
115 123 : }
116 :
117 : /************************************************************************/
118 : /* ~OGRJSONFGWriteLayer() */
119 : /************************************************************************/
120 :
121 246 : OGRJSONFGWriteLayer::~OGRJSONFGWriteLayer()
122 : {
123 123 : poFeatureDefn_->Release();
124 246 : }
125 :
126 : /************************************************************************/
127 : /* SyncToDisk() */
128 : /************************************************************************/
129 :
130 6 : OGRErr OGRJSONFGWriteLayer::SyncToDisk()
131 : {
132 6 : return poDS_->SyncToDiskInternal();
133 : }
134 :
135 : /************************************************************************/
136 : /* GetValueAsDateOrDateTime() */
137 : /************************************************************************/
138 :
139 16 : static const char *GetValueAsDateOrDateTime(const OGRField *psRawValue,
140 : OGRFieldType eType)
141 : {
142 16 : if (eType == OFTDate)
143 : {
144 22 : return CPLSPrintf("%04d-%02d-%02d", psRawValue->Date.Year,
145 11 : psRawValue->Date.Month, psRawValue->Date.Day);
146 : }
147 : else
148 : {
149 : struct tm brokenDown;
150 5 : memset(&brokenDown, 0, sizeof(brokenDown));
151 5 : brokenDown.tm_year = psRawValue->Date.Year - 1900;
152 5 : brokenDown.tm_mon = psRawValue->Date.Month - 1;
153 5 : brokenDown.tm_mday = psRawValue->Date.Day;
154 5 : brokenDown.tm_hour = psRawValue->Date.Hour;
155 5 : brokenDown.tm_min = psRawValue->Date.Minute;
156 5 : brokenDown.tm_sec = 0;
157 5 : if (psRawValue->Date.TZFlag > 0)
158 : {
159 : // Force to UTC
160 5 : GIntBig nVal = CPLYMDHMSToUnixTime(&brokenDown);
161 5 : nVal -= (psRawValue->Date.TZFlag - 100) * 15 * 60;
162 5 : CPLUnixTimeToYMDHMS(nVal, &brokenDown);
163 : }
164 5 : if (std::fabs(std::round(psRawValue->Date.Second) -
165 5 : psRawValue->Date.Second) < 1e-3)
166 : {
167 8 : return CPLSPrintf(
168 4 : "%04d-%02d-%02dT%02d:%02d:%02dZ", brokenDown.tm_year + 1900,
169 4 : brokenDown.tm_mon + 1, brokenDown.tm_mday, brokenDown.tm_hour,
170 : brokenDown.tm_min,
171 8 : static_cast<int>(std::round(psRawValue->Date.Second)));
172 : }
173 : else
174 : {
175 2 : return CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%06.3fZ",
176 1 : brokenDown.tm_year + 1900, brokenDown.tm_mon + 1,
177 : brokenDown.tm_mday, brokenDown.tm_hour,
178 1 : brokenDown.tm_min, psRawValue->Date.Second);
179 : }
180 : }
181 : }
182 :
183 : /************************************************************************/
184 : /* OGRJSONFGWriteGeometry() */
185 : /************************************************************************/
186 :
187 : static json_object *
188 3 : OGRJSONFGWriteGeometry(const OGRGeometry *poGeometry,
189 : const OGRGeoJSONWriteOptions &oOptions)
190 : {
191 3 : if (wkbFlatten(poGeometry->getGeometryType()) == wkbPolyhedralSurface)
192 : {
193 3 : const auto poPS = poGeometry->toPolyhedralSurface();
194 3 : json_object *poObj = json_object_new_object();
195 3 : json_object_object_add(poObj, "type",
196 : json_object_new_string("Polyhedron"));
197 3 : json_object *poCoordinates = json_object_new_array();
198 3 : json_object_object_add(poObj, "coordinates", poCoordinates);
199 3 : json_object *poOuterShell = json_object_new_array();
200 3 : json_object_array_add(poCoordinates, poOuterShell);
201 8 : for (const auto *poPoly : *poPS)
202 : {
203 5 : json_object_array_add(poOuterShell,
204 : OGRGeoJSONWritePolygon(poPoly, oOptions));
205 : }
206 3 : return poObj;
207 : }
208 : else
209 : {
210 0 : return nullptr;
211 : }
212 : }
213 :
214 : /************************************************************************/
215 : /* ICreateFeature() */
216 : /************************************************************************/
217 :
218 173 : OGRErr OGRJSONFGWriteLayer::ICreateFeature(OGRFeature *poFeature)
219 : {
220 173 : VSILFILE *fp = poDS_->GetOutputFile();
221 173 : poDS_->BeforeCreateFeature();
222 :
223 173 : if (oWriteOptions_.bGenerateID && poFeature->GetFID() == OGRNullFID)
224 : {
225 0 : poFeature->SetFID(nOutCounter_);
226 : }
227 :
228 173 : json_object *poObj = json_object_new_object();
229 :
230 173 : json_object_object_add(poObj, "type", json_object_new_string("Feature"));
231 :
232 : /* -------------------------------------------------------------------- */
233 : /* Write FID if available */
234 : /* -------------------------------------------------------------------- */
235 173 : OGRGeoJSONWriteId(poFeature, poObj, /* bIdAlreadyWritten = */ false,
236 173 : oWriteOptions_);
237 :
238 173 : if (!poDS_->IsSingleOutputLayer())
239 : {
240 149 : json_object_object_add(poObj, "featureType",
241 149 : json_object_new_string(GetDescription()));
242 149 : if (!osCoordRefSys_.empty() && !bIsWGS84CRS_)
243 : {
244 102 : json_object *poCoordRefSys = nullptr;
245 102 : CPL_IGNORE_RET_VAL(
246 102 : OGRJSonParse(osCoordRefSys_.c_str(), &poCoordRefSys));
247 102 : json_object_object_add(poObj, "coordRefSys", poCoordRefSys);
248 : }
249 : }
250 :
251 : /* -------------------------------------------------------------------- */
252 : /* Write feature attributes to "properties" object. */
253 : /* -------------------------------------------------------------------- */
254 346 : json_object *poObjProps = OGRGeoJSONWriteAttributes(
255 173 : poFeature, /* bWriteIdIfFoundInAttributes = */ true, oWriteOptions_);
256 :
257 : /* -------------------------------------------------------------------- */
258 : /* Deal with time properties. */
259 : /* -------------------------------------------------------------------- */
260 173 : json_object *poTime = nullptr;
261 173 : int nFieldTimeIdx = poFeatureDefn_->GetFieldIndex("jsonfg_time");
262 173 : if (nFieldTimeIdx < 0)
263 171 : nFieldTimeIdx = poFeatureDefn_->GetFieldIndex("time");
264 173 : if (nFieldTimeIdx >= 0 && poFeature->IsFieldSetAndNotNull(nFieldTimeIdx))
265 : {
266 6 : const auto poFieldDefn = poFeatureDefn_->GetFieldDefn(nFieldTimeIdx);
267 6 : const auto eType = poFieldDefn->GetType();
268 6 : if (eType == OFTDate || eType == OFTDateTime)
269 : {
270 6 : json_object_object_del(poObjProps, poFieldDefn->GetNameRef());
271 6 : poTime = json_object_new_object();
272 6 : json_object_object_add(
273 : poTime, eType == OFTDate ? "date" : "timestamp",
274 : json_object_new_string(GetValueAsDateOrDateTime(
275 6 : poFeature->GetRawFieldRef(nFieldTimeIdx), eType)));
276 : }
277 : }
278 : else
279 : {
280 167 : bool bHasStartOrStop = false;
281 167 : json_object *poTimeStart = nullptr;
282 : int nFieldTimeStartIdx =
283 167 : poFeatureDefn_->GetFieldIndex("jsonfg_time_start");
284 167 : if (nFieldTimeStartIdx < 0)
285 166 : nFieldTimeStartIdx = poFeatureDefn_->GetFieldIndex("time_start");
286 174 : if (nFieldTimeStartIdx >= 0 &&
287 7 : poFeature->IsFieldSetAndNotNull(nFieldTimeStartIdx))
288 : {
289 : const auto poFieldDefnStart =
290 5 : poFeatureDefn_->GetFieldDefn(nFieldTimeStartIdx);
291 5 : const auto eType = poFieldDefnStart->GetType();
292 5 : if (eType == OFTDate || eType == OFTDateTime)
293 : {
294 5 : json_object_object_del(poObjProps,
295 : poFieldDefnStart->GetNameRef());
296 5 : poTimeStart = json_object_new_string(GetValueAsDateOrDateTime(
297 5 : poFeature->GetRawFieldRef(nFieldTimeStartIdx), eType));
298 5 : bHasStartOrStop = true;
299 : }
300 : }
301 :
302 167 : json_object *poTimeEnd = nullptr;
303 167 : int nFieldTimeEndIdx = poFeatureDefn_->GetFieldIndex("jsonfg_time_end");
304 167 : if (nFieldTimeEndIdx < 0)
305 166 : nFieldTimeEndIdx = poFeatureDefn_->GetFieldIndex("time_end");
306 174 : if (nFieldTimeEndIdx >= 0 &&
307 7 : poFeature->IsFieldSetAndNotNull(nFieldTimeEndIdx))
308 : {
309 : const auto poFieldDefnEnd =
310 5 : poFeatureDefn_->GetFieldDefn(nFieldTimeEndIdx);
311 5 : const auto eType = poFieldDefnEnd->GetType();
312 5 : if (eType == OFTDate || eType == OFTDateTime)
313 : {
314 5 : json_object_object_del(poObjProps,
315 : poFieldDefnEnd->GetNameRef());
316 5 : poTimeEnd = json_object_new_string(GetValueAsDateOrDateTime(
317 5 : poFeature->GetRawFieldRef(nFieldTimeEndIdx), eType));
318 5 : bHasStartOrStop = true;
319 : }
320 : }
321 :
322 167 : if (bHasStartOrStop)
323 : {
324 7 : poTime = json_object_new_object();
325 7 : json_object *poInterval = json_object_new_array();
326 7 : json_object_object_add(poTime, "interval", poInterval);
327 9 : json_object_array_add(poInterval,
328 : poTimeStart ? poTimeStart
329 2 : : json_object_new_string(".."));
330 9 : json_object_array_add(poInterval,
331 : poTimeEnd ? poTimeEnd
332 2 : : json_object_new_string(".."));
333 : }
334 : }
335 :
336 173 : json_object_object_add(poObj, "properties", poObjProps);
337 :
338 : /* -------------------------------------------------------------------- */
339 : /* Write place and/or geometry */
340 : /* -------------------------------------------------------------------- */
341 173 : json_object *poJSONGeometry = nullptr;
342 173 : json_object *poPlace = nullptr;
343 173 : if (const OGRGeometry *poGeom = poFeature->GetGeometryRef())
344 : {
345 122 : const bool bHasCurve = poGeom->hasCurveGeometry(true);
346 122 : if (bHasCurve)
347 22 : m_bCurveWritten = true;
348 122 : const bool bHasMeasure = CPL_TO_BOOL(poGeom->IsMeasured());
349 122 : if (bHasMeasure)
350 17 : m_bMeasureWritten = true;
351 122 : bool bWritePlace = false;
352 122 : if (wkbFlatten(poGeom->getGeometryType()) == wkbPolyhedralSurface)
353 : {
354 3 : m_bPolyhedraWritten = true;
355 3 : bWritePlace = true;
356 : }
357 119 : else if (bIsWGS84CRS_)
358 : {
359 29 : bWritePlace = bHasCurve || bHasMeasure;
360 29 : poJSONGeometry = OGRGeoJSONWriteGeometry(poGeom, oWriteOptions_);
361 : }
362 : else
363 : {
364 90 : if (bWriteFallbackGeometry_ && poCTToWGS84_)
365 : {
366 : auto poGeomClone =
367 88 : std::unique_ptr<OGRGeometry>(poGeom->clone());
368 44 : if (poGeomClone->transform(poCTToWGS84_.get()) == OGRERR_NONE)
369 : {
370 44 : poJSONGeometry = OGRGeoJSONWriteGeometry(poGeomClone.get(),
371 44 : oWriteOptions_);
372 : }
373 : }
374 :
375 90 : bWritePlace = true;
376 : }
377 :
378 122 : std::unique_ptr<OGRGeometry> poGeomClone; // keep in that scope
379 122 : if (bWritePlace)
380 : {
381 117 : if (m_bMustSwapForPlace)
382 : {
383 3 : poGeomClone.reset(poGeom->clone());
384 3 : poGeomClone->swapXY();
385 3 : poGeom = poGeomClone.get();
386 : }
387 117 : if (wkbFlatten(poGeom->getGeometryType()) == wkbPolyhedralSurface)
388 : {
389 3 : poPlace = OGRJSONFGWriteGeometry(poGeom, oWriteOptionsPlace_);
390 : }
391 : else
392 : {
393 114 : poPlace = OGRGeoJSONWriteGeometry(poGeom, oWriteOptionsPlace_);
394 : }
395 : }
396 :
397 122 : if (poGeom->IsMeasured())
398 : {
399 17 : if (!bLayerLevelMeasuresWritten_)
400 : {
401 14 : json_object *poMeasures = json_object_new_object();
402 14 : json_object_object_add(poMeasures, "enabled",
403 : json_object_new_boolean(true));
404 14 : if (!poDS_->IsSingleOutputLayer())
405 : {
406 13 : if (!osMeasureUnit_.empty())
407 : {
408 1 : json_object_object_add(
409 : poMeasures, "unit",
410 : json_object_new_string(osMeasureUnit_.c_str()));
411 : }
412 13 : if (!osMeasureDescription_.empty())
413 : {
414 1 : json_object_object_add(
415 : poMeasures, "description",
416 : json_object_new_string(
417 : osMeasureDescription_.c_str()));
418 : }
419 : }
420 14 : json_object_object_add(poObj, "measures", poMeasures);
421 : }
422 : }
423 105 : else if (bLayerLevelMeasuresWritten_)
424 : {
425 0 : json_object *poMeasures = json_object_new_object();
426 0 : json_object_object_add(poMeasures, "enabled",
427 : json_object_new_boolean(false));
428 0 : json_object_object_add(poObj, "measures", poMeasures);
429 : }
430 : }
431 :
432 173 : json_object_object_add(poObj, "geometry", poJSONGeometry);
433 173 : json_object_object_add(poObj, "place", poPlace);
434 :
435 173 : json_object_object_add(poObj, "time", poTime);
436 :
437 173 : VSIFPrintfL(fp, "%s",
438 : json_object_to_json_string_ext(
439 : poObj, JSON_C_TO_STRING_SPACED
440 : #ifdef JSON_C_TO_STRING_NOSLASHESCAPE
441 : | JSON_C_TO_STRING_NOSLASHESCAPE
442 : #endif
443 : ));
444 :
445 173 : json_object_put(poObj);
446 :
447 173 : ++nOutCounter_;
448 :
449 173 : return OGRERR_NONE;
450 : }
451 :
452 : /************************************************************************/
453 : /* CreateField() */
454 : /************************************************************************/
455 :
456 122 : OGRErr OGRJSONFGWriteLayer::CreateField(const OGRFieldDefn *poField,
457 : int /* bApproxOK */)
458 : {
459 122 : if (poFeatureDefn_->GetFieldIndexCaseSensitive(poField->GetNameRef()) >= 0)
460 : {
461 0 : CPLDebug("JSONFG", "Field '%s' already present in schema",
462 : poField->GetNameRef());
463 :
464 0 : return OGRERR_NONE;
465 : }
466 :
467 122 : poFeatureDefn_->AddFieldDefn(poField);
468 :
469 122 : return OGRERR_NONE;
470 : }
471 :
472 : /************************************************************************/
473 : /* TestCapability() */
474 : /************************************************************************/
475 :
476 251 : int OGRJSONFGWriteLayer::TestCapability(const char *pszCap) const
477 : {
478 251 : if (EQUAL(pszCap, OLCCreateField))
479 16 : return TRUE;
480 235 : else if (EQUAL(pszCap, OLCSequentialWrite))
481 16 : return TRUE;
482 219 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
483 0 : return TRUE;
484 219 : else if (EQUAL(pszCap, OLCMeasuredGeometries) ||
485 112 : EQUAL(pszCap, OLCZGeometries) || EQUAL(pszCap, OLCCurveGeometries))
486 219 : return TRUE;
487 0 : return FALSE;
488 : }
489 :
490 : /************************************************************************/
491 : /* GetDataset() */
492 : /************************************************************************/
493 :
494 24 : GDALDataset *OGRJSONFGWriteLayer::GetDataset()
495 : {
496 24 : return poDS_;
497 : }
|