Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implementation of OGRGeoJSONWriteLayer class (OGR GeoJSON Driver).
5 : * Author: Mateusz Loskot, mateusz@loskot.net
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2011, Even Rouault <even dot rouault at spatialys.com>
9 : * Copyright (c) 2007, Mateusz Loskot
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "ogr_geojson.h"
15 : #include "ogrgeojsonwriter.h"
16 :
17 : #include "cpl_vsi_virtual.h"
18 :
19 : #include <algorithm>
20 :
21 : /************************************************************************/
22 : /* OGRGeoJSONWriteLayer() */
23 : /************************************************************************/
24 :
25 156 : OGRGeoJSONWriteLayer::OGRGeoJSONWriteLayer(const char *pszName,
26 : OGRwkbGeometryType eGType,
27 : CSLConstList papszOptions,
28 : bool bWriteFC_BBOXIn,
29 : OGRCoordinateTransformation *poCT,
30 156 : OGRGeoJSONDataSource *poDS)
31 156 : : poDS_(poDS), poFeatureDefn_(new OGRFeatureDefn(pszName)), nOutCounter_(0),
32 156 : bWriteBBOX(CPLTestBool(
33 : CSLFetchNameValueDef(papszOptions, "WRITE_BBOX", "FALSE"))),
34 : bBBOX3D(false), bWriteFC_BBOX(bWriteFC_BBOXIn),
35 156 : nSignificantFigures_(atoi(
36 : CSLFetchNameValueDef(papszOptions, "SIGNIFICANT_FIGURES", "-1"))),
37 : bRFC7946_(
38 156 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "RFC7946", "FALSE"))),
39 156 : bWrapDateLine_(CPLTestBool(
40 : CSLFetchNameValueDef(papszOptions, "WRAPDATELINE", "YES"))),
41 : osForeignMembers_(
42 : CSLFetchNameValueDef(papszOptions, "FOREIGN_MEMBERS_FEATURE", "")),
43 936 : poCT_(poCT)
44 : {
45 156 : if (!osForeignMembers_.empty())
46 : {
47 : // Already checked in OGRGeoJSONDataSource::ICreateLayer()
48 1 : CPLAssert(osForeignMembers_.front() == '{');
49 1 : CPLAssert(osForeignMembers_.back() == '}');
50 : osForeignMembers_ =
51 1 : osForeignMembers_.substr(1, osForeignMembers_.size() - 2);
52 : }
53 156 : poFeatureDefn_->Reference();
54 156 : poFeatureDefn_->SetGeomType(eGType);
55 156 : SetDescription(poFeatureDefn_->GetName());
56 : const char *pszCoordPrecision =
57 156 : CSLFetchNameValue(papszOptions, "COORDINATE_PRECISION");
58 156 : if (pszCoordPrecision)
59 : {
60 3 : oWriteOptions_.nXYCoordPrecision = atoi(pszCoordPrecision);
61 3 : oWriteOptions_.nZCoordPrecision = atoi(pszCoordPrecision);
62 : }
63 : else
64 : {
65 153 : oWriteOptions_.nXYCoordPrecision = atoi(CSLFetchNameValueDef(
66 153 : papszOptions, "XY_COORD_PRECISION", bRFC7946_ ? "7" : "-1"));
67 153 : oWriteOptions_.nZCoordPrecision = atoi(CSLFetchNameValueDef(
68 153 : papszOptions, "Z_COORD_PRECISION", bRFC7946_ ? "3" : "-1"));
69 : }
70 156 : oWriteOptions_.bWriteBBOX = bWriteBBOX;
71 156 : oWriteOptions_.nSignificantFigures = nSignificantFigures_;
72 156 : if (bRFC7946_)
73 : {
74 23 : oWriteOptions_.SetRFC7946Settings();
75 : }
76 156 : oWriteOptions_.SetIDOptions(papszOptions);
77 156 : oWriteOptions_.bAllowNonFiniteValues = CPLTestBool(
78 : CSLFetchNameValueDef(papszOptions, "WRITE_NON_FINITE_VALUES", "FALSE"));
79 156 : oWriteOptions_.bAutodetectJsonStrings = CPLTestBool(
80 : CSLFetchNameValueDef(papszOptions, "AUTODETECT_JSON_STRINGS", "TRUE"));
81 156 : }
82 :
83 : /************************************************************************/
84 : /* ~OGRGeoJSONWriteLayer() */
85 : /************************************************************************/
86 :
87 312 : OGRGeoJSONWriteLayer::~OGRGeoJSONWriteLayer()
88 : {
89 156 : FinishWriting();
90 :
91 156 : if (nullptr != poFeatureDefn_)
92 : {
93 156 : poFeatureDefn_->Release();
94 : }
95 :
96 156 : delete poCT_;
97 312 : }
98 :
99 : /************************************************************************/
100 : /* FinishWriting() */
101 : /************************************************************************/
102 :
103 306 : void OGRGeoJSONWriteLayer::FinishWriting()
104 : {
105 306 : if (m_nPositionBeforeFCClosed == 0)
106 : {
107 157 : VSILFILE *fp = poDS_->GetOutputFile();
108 :
109 157 : m_nPositionBeforeFCClosed = fp->Tell();
110 :
111 157 : VSIFPrintfL(fp, "\n]");
112 :
113 157 : if (bWriteFC_BBOX && sEnvelopeLayer.IsInit())
114 : {
115 46 : CPLString osBBOX = "[ ";
116 : char szFormat[32];
117 23 : if (oWriteOptions_.nXYCoordPrecision >= 0)
118 20 : snprintf(szFormat, sizeof(szFormat), "%%.%df",
119 : oWriteOptions_.nXYCoordPrecision);
120 : else
121 3 : snprintf(szFormat, sizeof(szFormat), "%s", "%.15g");
122 :
123 23 : osBBOX += CPLSPrintf(szFormat, sEnvelopeLayer.MinX);
124 23 : osBBOX += ", ";
125 23 : osBBOX += CPLSPrintf(szFormat, sEnvelopeLayer.MinY);
126 23 : osBBOX += ", ";
127 23 : if (bBBOX3D)
128 : {
129 1 : osBBOX += CPLSPrintf(szFormat, sEnvelopeLayer.MinZ);
130 1 : osBBOX += ", ";
131 : }
132 23 : osBBOX += CPLSPrintf(szFormat, sEnvelopeLayer.MaxX);
133 23 : osBBOX += ", ";
134 23 : osBBOX += CPLSPrintf(szFormat, sEnvelopeLayer.MaxY);
135 23 : if (bBBOX3D)
136 : {
137 1 : osBBOX += ", ";
138 1 : osBBOX += CPLSPrintf(szFormat, sEnvelopeLayer.MaxZ);
139 : }
140 23 : osBBOX += " ]";
141 :
142 46 : if (poDS_->GetFpOutputIsSeekable() &&
143 23 : osBBOX.size() + 9 < OGRGeoJSONDataSource::SPACE_FOR_BBOX)
144 : {
145 23 : VSIFSeekL(fp, poDS_->GetBBOXInsertLocation(), SEEK_SET);
146 23 : VSIFPrintfL(fp, "\"bbox\": %s,", osBBOX.c_str());
147 23 : VSIFSeekL(fp, 0, SEEK_END);
148 : }
149 : else
150 : {
151 0 : VSIFPrintfL(fp, ",\n\"bbox\": %s", osBBOX.c_str());
152 : }
153 : }
154 :
155 157 : VSIFPrintfL(fp, "\n}\n");
156 157 : fp->Flush();
157 : }
158 306 : }
159 :
160 : /************************************************************************/
161 : /* SyncToDisk() */
162 : /************************************************************************/
163 :
164 234 : OGRErr OGRGeoJSONWriteLayer::SyncToDisk()
165 : {
166 234 : if (m_nPositionBeforeFCClosed == 0 && poDS_->GetFpOutputIsSeekable())
167 : {
168 150 : FinishWriting();
169 : }
170 :
171 234 : return OGRERR_NONE;
172 : }
173 :
174 : /************************************************************************/
175 : /* ICreateFeature() */
176 : /************************************************************************/
177 :
178 1083 : OGRErr OGRGeoJSONWriteLayer::ICreateFeature(OGRFeature *poFeature)
179 : {
180 1083 : VSILFILE *fp = poDS_->GetOutputFile();
181 :
182 : OGRFeature *poFeatureToWrite;
183 1083 : if (poCT_ != nullptr || bRFC7946_)
184 : {
185 42 : poFeatureToWrite = new OGRFeature(poFeatureDefn_);
186 42 : poFeatureToWrite->SetFrom(poFeature);
187 42 : poFeatureToWrite->SetFID(poFeature->GetFID());
188 42 : OGRGeometry *poGeometry = poFeatureToWrite->GetGeometryRef();
189 42 : if (poGeometry)
190 : {
191 42 : const char *const apszOptions[] = {
192 42 : bWrapDateLine_ ? "WRAPDATELINE=YES" : nullptr, nullptr};
193 84 : OGRGeometry *poNewGeom = OGRGeometryFactory::transformWithOptions(
194 : poGeometry, poCT_, const_cast<char **>(apszOptions),
195 42 : oTransformCache_);
196 42 : if (poNewGeom == nullptr)
197 : {
198 0 : delete poFeatureToWrite;
199 0 : return OGRERR_FAILURE;
200 : }
201 :
202 42 : OGREnvelope sEnvelope;
203 42 : poNewGeom->getEnvelope(&sEnvelope);
204 42 : if (sEnvelope.MinX < -180.0 || sEnvelope.MaxX > 180.0 ||
205 42 : sEnvelope.MinY < -90.0 || sEnvelope.MaxY > 90.0)
206 : {
207 0 : CPLError(CE_Failure, CPLE_AppDefined,
208 : "Geometry extent outside of "
209 : "[-180.0,180.0]x[-90.0,90.0] bounds");
210 0 : delete poFeatureToWrite;
211 0 : return OGRERR_FAILURE;
212 : }
213 :
214 42 : poFeatureToWrite->SetGeometryDirectly(poNewGeom);
215 42 : }
216 : }
217 : else
218 : {
219 1041 : poFeatureToWrite = poFeature;
220 : }
221 :
222 72 : const auto IsValid = [](const OGRGeometry *poGeom)
223 : {
224 144 : CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
225 144 : return poGeom->IsValid();
226 : };
227 :
228 : // Special processing to detect and repair invalid geometries due to
229 : // coordinate precision.
230 : // Normally drivers shouldn't do that as similar code is triggered by
231 : // setting the OGR_APPLY_GEOM_SET_PRECISION=YES configuration option by
232 : // the generic OGRLayer::CreateFeature() code path. But this code predates
233 : // its introduction and RFC99, and can be useful in RFC7946 mode due to
234 : // coordinate reprojection.
235 1083 : OGRGeometry *poOrigGeom = poFeature->GetGeometryRef();
236 1083 : if (OGRGeometryFactory::haveGEOS() &&
237 1083 : oWriteOptions_.nXYCoordPrecision >= 0 && poOrigGeom &&
238 2203 : wkbFlatten(poOrigGeom->getGeometryType()) != wkbPoint &&
239 37 : IsValid(poOrigGeom))
240 : {
241 : const double dfXYResolution =
242 34 : std::pow(10.0, double(-oWriteOptions_.nXYCoordPrecision));
243 : auto poNewGeom = std::unique_ptr<OGRGeometry>(
244 68 : poFeatureToWrite->GetGeometryRef()->clone());
245 68 : OGRGeomCoordinatePrecision sPrecision;
246 34 : sPrecision.dfXYResolution = dfXYResolution;
247 34 : poNewGeom->roundCoordinates(sPrecision);
248 34 : if (!IsValid(poNewGeom.get()))
249 : {
250 2 : std::unique_ptr<OGRGeometry> poValidGeom;
251 2 : if (poFeature == poFeatureToWrite)
252 : {
253 1 : CPLDebug("GeoJSON",
254 : "Running SetPrecision() to correct an invalid "
255 : "geometry due to reduced precision output");
256 1 : poValidGeom.reset(
257 : poOrigGeom->SetPrecision(dfXYResolution, /* nFlags = */ 0));
258 : }
259 : else
260 : {
261 1 : CPLDebug("GeoJSON", "Running MakeValid() to correct an invalid "
262 : "geometry due to reduced precision output");
263 1 : poValidGeom.reset(poNewGeom->MakeValid());
264 1 : if (poValidGeom)
265 : {
266 : auto poValidGeomRoundCoordinates =
267 2 : std::unique_ptr<OGRGeometry>(poValidGeom->clone());
268 1 : poValidGeomRoundCoordinates->roundCoordinates(sPrecision);
269 1 : if (!IsValid(poValidGeomRoundCoordinates.get()))
270 : {
271 1 : CPLDebug("GeoJSON",
272 : "Running SetPrecision() to correct an invalid "
273 : "geometry due to reduced precision output");
274 : auto poValidGeom2 = std::unique_ptr<OGRGeometry>(
275 : poValidGeom->SetPrecision(dfXYResolution,
276 2 : /* nFlags = */ 0));
277 1 : if (poValidGeom2)
278 1 : poValidGeom = std::move(poValidGeom2);
279 : }
280 : }
281 : }
282 2 : if (poValidGeom)
283 : {
284 2 : if (poFeature == poFeatureToWrite)
285 : {
286 1 : poFeatureToWrite = new OGRFeature(poFeatureDefn_);
287 1 : poFeatureToWrite->SetFrom(poFeature);
288 1 : poFeatureToWrite->SetFID(poFeature->GetFID());
289 : }
290 2 : poFeatureToWrite->SetGeometryDirectly(poValidGeom.release());
291 : }
292 : }
293 : }
294 :
295 1083 : if (oWriteOptions_.bGenerateID && poFeatureToWrite->GetFID() == OGRNullFID)
296 : {
297 3 : poFeatureToWrite->SetFID(nOutCounter_);
298 : }
299 : json_object *poObj =
300 1083 : OGRGeoJSONWriteFeature(poFeatureToWrite, oWriteOptions_);
301 1083 : CPLAssert(nullptr != poObj);
302 :
303 1083 : if (m_nPositionBeforeFCClosed)
304 : {
305 : // If we had called SyncToDisk() previously, undo its effects
306 1 : fp->Seek(m_nPositionBeforeFCClosed, SEEK_SET);
307 1 : m_nPositionBeforeFCClosed = 0;
308 : }
309 :
310 1083 : if (nOutCounter_ > 0)
311 : {
312 : /* Separate "Feature" entries in "FeatureCollection" object. */
313 948 : VSIFPrintfL(fp, ",\n");
314 : }
315 1083 : const char *pszJson = json_object_to_json_string_ext(
316 : poObj, JSON_C_TO_STRING_SPACED
317 : #ifdef JSON_C_TO_STRING_NOSLASHESCAPE
318 : | JSON_C_TO_STRING_NOSLASHESCAPE
319 : #endif
320 : );
321 :
322 1083 : OGRErr eErr = OGRERR_NONE;
323 1083 : size_t nLen = strlen(pszJson);
324 1083 : if (!osForeignMembers_.empty())
325 : {
326 1 : if (nLen > 2 && pszJson[nLen - 2] == ' ' && pszJson[nLen - 1] == '}')
327 : {
328 1 : nLen -= 2;
329 : }
330 : else
331 : {
332 : // should not happen
333 0 : CPLError(CE_Failure, CPLE_AppDefined,
334 : "Unexpected JSON output for feature. Cannot write foreign "
335 : "member");
336 0 : osForeignMembers_.clear();
337 : }
338 : }
339 1083 : if (VSIFWriteL(pszJson, nLen, 1, fp) != 1)
340 : {
341 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot write feature");
342 0 : eErr = OGRERR_FAILURE;
343 : }
344 1084 : else if (!osForeignMembers_.empty() &&
345 1 : (VSIFWriteL(", ", 2, 1, fp) != 1 ||
346 1 : VSIFWriteL(osForeignMembers_.c_str(), osForeignMembers_.size(), 1,
347 1 : fp) != 1 ||
348 1 : VSIFWriteL("}", 1, 1, fp) != 1))
349 : {
350 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot write feature");
351 0 : eErr = OGRERR_FAILURE;
352 : }
353 :
354 1083 : json_object_put(poObj);
355 :
356 1083 : ++nOutCounter_;
357 :
358 1083 : OGRGeometry *poGeometry = poFeatureToWrite->GetGeometryRef();
359 1083 : if (poGeometry != nullptr && !poGeometry->IsEmpty())
360 : {
361 983 : OGREnvelope3D sEnvelope = OGRGeoJSONGetBBox(poGeometry, oWriteOptions_);
362 983 : if (poGeometry->getCoordinateDimension() == 3)
363 21 : bBBOX3D = true;
364 :
365 983 : if (!sEnvelopeLayer.IsInit())
366 : {
367 98 : sEnvelopeLayer = sEnvelope;
368 : }
369 885 : else if (oWriteOptions_.bBBOXRFC7946)
370 : {
371 20 : const bool bEnvelopeCrossAM = (sEnvelope.MinX > sEnvelope.MaxX);
372 20 : const bool bEnvelopeLayerCrossAM =
373 20 : (sEnvelopeLayer.MinX > sEnvelopeLayer.MaxX);
374 20 : if (bEnvelopeCrossAM)
375 : {
376 8 : if (bEnvelopeLayerCrossAM)
377 : {
378 8 : sEnvelopeLayer.MinX =
379 8 : std::min(sEnvelopeLayer.MinX, sEnvelope.MinX);
380 8 : sEnvelopeLayer.MaxX =
381 8 : std::max(sEnvelopeLayer.MaxX, sEnvelope.MaxX);
382 : }
383 : else
384 : {
385 0 : if (sEnvelopeLayer.MinX > 0)
386 : {
387 0 : sEnvelopeLayer.MinX =
388 0 : std::min(sEnvelopeLayer.MinX, sEnvelope.MinX);
389 0 : sEnvelopeLayer.MaxX = sEnvelope.MaxX;
390 : }
391 0 : else if (sEnvelopeLayer.MaxX < 0)
392 : {
393 0 : sEnvelopeLayer.MaxX =
394 0 : std::max(sEnvelopeLayer.MaxX, sEnvelope.MaxX);
395 0 : sEnvelopeLayer.MinX = sEnvelope.MinX;
396 : }
397 : else
398 : {
399 0 : sEnvelopeLayer.MinX = -180.0;
400 0 : sEnvelopeLayer.MaxX = 180.0;
401 : }
402 : }
403 : }
404 12 : else if (bEnvelopeLayerCrossAM)
405 : {
406 0 : if (sEnvelope.MinX > 0)
407 : {
408 0 : sEnvelopeLayer.MinX =
409 0 : std::min(sEnvelopeLayer.MinX, sEnvelope.MinX);
410 : }
411 0 : else if (sEnvelope.MaxX < 0)
412 : {
413 0 : sEnvelopeLayer.MaxX =
414 0 : std::max(sEnvelopeLayer.MaxX, sEnvelope.MaxX);
415 : }
416 : else
417 : {
418 0 : sEnvelopeLayer.MinX = -180.0;
419 0 : sEnvelopeLayer.MaxX = 180.0;
420 : }
421 : }
422 : else
423 : {
424 12 : sEnvelopeLayer.MinX =
425 12 : std::min(sEnvelopeLayer.MinX, sEnvelope.MinX);
426 12 : sEnvelopeLayer.MaxX =
427 12 : std::max(sEnvelopeLayer.MaxX, sEnvelope.MaxX);
428 : }
429 :
430 20 : sEnvelopeLayer.MinY = std::min(sEnvelopeLayer.MinY, sEnvelope.MinY);
431 20 : sEnvelopeLayer.MaxY = std::max(sEnvelopeLayer.MaxY, sEnvelope.MaxY);
432 : }
433 : else
434 : {
435 865 : sEnvelopeLayer.Merge(sEnvelope);
436 : }
437 : }
438 :
439 1083 : if (poFeatureToWrite != poFeature)
440 43 : delete poFeatureToWrite;
441 :
442 1083 : return eErr;
443 : }
444 :
445 : /************************************************************************/
446 : /* CreateField() */
447 : /************************************************************************/
448 :
449 223 : OGRErr OGRGeoJSONWriteLayer::CreateField(const OGRFieldDefn *poField,
450 : int /* bApproxOK */)
451 : {
452 223 : if (poFeatureDefn_->GetFieldIndexCaseSensitive(poField->GetNameRef()) >= 0)
453 : {
454 0 : CPLDebug("GeoJSON", "Field '%s' already present in schema",
455 : poField->GetNameRef());
456 :
457 : // TODO - mloskot: Is this return code correct?
458 0 : return OGRERR_NONE;
459 : }
460 :
461 223 : poFeatureDefn_->AddFieldDefn(poField);
462 :
463 223 : return OGRERR_NONE;
464 : }
465 :
466 : /************************************************************************/
467 : /* TestCapability() */
468 : /************************************************************************/
469 :
470 371 : int OGRGeoJSONWriteLayer::TestCapability(const char *pszCap)
471 : {
472 371 : if (EQUAL(pszCap, OLCCreateField))
473 16 : return TRUE;
474 355 : else if (EQUAL(pszCap, OLCSequentialWrite))
475 20 : return TRUE;
476 335 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
477 0 : return TRUE;
478 335 : return FALSE;
479 : }
480 :
481 : /************************************************************************/
482 : /* GetExtent() */
483 : /************************************************************************/
484 :
485 2 : OGRErr OGRGeoJSONWriteLayer::GetExtent(OGREnvelope *psExtent, int)
486 : {
487 2 : if (sEnvelopeLayer.IsInit())
488 : {
489 2 : *psExtent = sEnvelopeLayer;
490 2 : return OGRERR_NONE;
491 : }
492 0 : return OGRERR_FAILURE;
493 : }
494 :
495 : /************************************************************************/
496 : /* GetDataset() */
497 : /************************************************************************/
498 :
499 34 : GDALDataset *OGRGeoJSONWriteLayer::GetDataset()
500 : {
501 34 : return poDS_;
502 : }
|