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 168 : OGRGeoJSONWriteLayer::OGRGeoJSONWriteLayer(const char *pszName,
26 : OGRwkbGeometryType eGType,
27 : CSLConstList papszOptions,
28 : bool bWriteFC_BBOXIn,
29 : OGRCoordinateTransformation *poCT,
30 168 : OGRGeoJSONDataSource *poDS)
31 168 : : poDS_(poDS), poFeatureDefn_(new OGRFeatureDefn(pszName)), nOutCounter_(0),
32 168 : bWriteBBOX(CPLTestBool(
33 : CSLFetchNameValueDef(papszOptions, "WRITE_BBOX", "FALSE"))),
34 : bBBOX3D(false), bWriteFC_BBOX(bWriteFC_BBOXIn),
35 168 : nSignificantFigures_(atoi(
36 : CSLFetchNameValueDef(papszOptions, "SIGNIFICANT_FIGURES", "-1"))),
37 : bRFC7946_(
38 168 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "RFC7946", "FALSE"))),
39 168 : bWrapDateLine_(CPLTestBool(
40 : CSLFetchNameValueDef(papszOptions, "WRAPDATELINE", "YES"))),
41 : osForeignMembers_(
42 : CSLFetchNameValueDef(papszOptions, "FOREIGN_MEMBERS_FEATURE", "")),
43 1008 : poCT_(poCT)
44 : {
45 168 : 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 168 : poFeatureDefn_->Reference();
54 168 : poFeatureDefn_->SetGeomType(eGType);
55 168 : SetDescription(poFeatureDefn_->GetName());
56 : const char *pszCoordPrecision =
57 168 : CSLFetchNameValue(papszOptions, "COORDINATE_PRECISION");
58 168 : if (pszCoordPrecision)
59 : {
60 3 : oWriteOptions_.nXYCoordPrecision = atoi(pszCoordPrecision);
61 3 : oWriteOptions_.nZCoordPrecision = atoi(pszCoordPrecision);
62 : }
63 : else
64 : {
65 165 : oWriteOptions_.nXYCoordPrecision = atoi(CSLFetchNameValueDef(
66 165 : papszOptions, "XY_COORD_PRECISION", bRFC7946_ ? "7" : "-1"));
67 165 : oWriteOptions_.nZCoordPrecision = atoi(CSLFetchNameValueDef(
68 165 : papszOptions, "Z_COORD_PRECISION", bRFC7946_ ? "3" : "-1"));
69 : }
70 168 : oWriteOptions_.bWriteBBOX = bWriteBBOX;
71 168 : oWriteOptions_.nSignificantFigures = nSignificantFigures_;
72 168 : if (bRFC7946_)
73 : {
74 23 : oWriteOptions_.SetRFC7946Settings();
75 : }
76 168 : oWriteOptions_.SetIDOptions(papszOptions);
77 168 : oWriteOptions_.bAllowNonFiniteValues = CPLTestBool(
78 : CSLFetchNameValueDef(papszOptions, "WRITE_NON_FINITE_VALUES", "FALSE"));
79 168 : oWriteOptions_.bAutodetectJsonStrings = CPLTestBool(
80 : CSLFetchNameValueDef(papszOptions, "AUTODETECT_JSON_STRINGS", "TRUE"));
81 168 : }
82 :
83 : /************************************************************************/
84 : /* ~OGRGeoJSONWriteLayer() */
85 : /************************************************************************/
86 :
87 336 : OGRGeoJSONWriteLayer::~OGRGeoJSONWriteLayer()
88 : {
89 168 : FinishWriting();
90 :
91 168 : if (nullptr != poFeatureDefn_)
92 : {
93 168 : poFeatureDefn_->Release();
94 : }
95 :
96 168 : delete poCT_;
97 336 : }
98 :
99 : /************************************************************************/
100 : /* FinishWriting() */
101 : /************************************************************************/
102 :
103 328 : void OGRGeoJSONWriteLayer::FinishWriting()
104 : {
105 328 : if (m_nPositionBeforeFCClosed == 0)
106 : {
107 169 : VSILFILE *fp = poDS_->GetOutputFile();
108 :
109 169 : m_nPositionBeforeFCClosed = fp->Tell();
110 :
111 169 : VSIFPrintfL(fp, "\n]");
112 :
113 169 : 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 169 : VSIFPrintfL(fp, "\n}\n");
156 169 : fp->Flush();
157 : }
158 328 : }
159 :
160 : /************************************************************************/
161 : /* SyncToDisk() */
162 : /************************************************************************/
163 :
164 257 : OGRErr OGRGeoJSONWriteLayer::SyncToDisk()
165 : {
166 257 : if (m_nPositionBeforeFCClosed == 0 && poDS_->GetFpOutputIsSeekable())
167 : {
168 160 : FinishWriting();
169 : }
170 :
171 257 : return OGRERR_NONE;
172 : }
173 :
174 : /************************************************************************/
175 : /* ICreateFeature() */
176 : /************************************************************************/
177 :
178 1116 : OGRErr OGRGeoJSONWriteLayer::ICreateFeature(OGRFeature *poFeature)
179 : {
180 1116 : VSILFILE *fp = poDS_->GetOutputFile();
181 :
182 : OGRFeature *poFeatureToWrite;
183 1116 : 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 1074 : poFeatureToWrite = poFeature;
220 : }
221 :
222 : // Special processing to detect and repair invalid geometries due to
223 : // coordinate precision.
224 : // Normally drivers shouldn't do that as similar code is triggered by
225 : // setting the OGR_APPLY_GEOM_SET_PRECISION=YES configuration option by
226 : // the generic OGRLayer::CreateFeature() code path. But this code predates
227 : // its introduction and RFC99, and can be useful in RFC7946 mode due to
228 : // coordinate reprojection.
229 1116 : std::string osReason;
230 1116 : OGRGeometry *poOrigGeom = poFeature->GetGeometryRef();
231 1116 : if (OGRGeometryFactory::haveGEOS() &&
232 1116 : oWriteOptions_.nXYCoordPrecision >= 0 && poOrigGeom &&
233 2269 : wkbFlatten(poOrigGeom->getGeometryType()) != wkbPoint &&
234 37 : poOrigGeom->IsValid(&osReason))
235 : {
236 : const double dfXYResolution =
237 34 : std::pow(10.0, double(-oWriteOptions_.nXYCoordPrecision));
238 : auto poNewGeom = std::unique_ptr<OGRGeometry>(
239 68 : poFeatureToWrite->GetGeometryRef()->clone());
240 68 : OGRGeomCoordinatePrecision sPrecision;
241 34 : sPrecision.dfXYResolution = dfXYResolution;
242 34 : poNewGeom->roundCoordinates(sPrecision);
243 34 : if (!poNewGeom->IsValid(&osReason))
244 : {
245 2 : std::unique_ptr<OGRGeometry> poValidGeom;
246 2 : if (poFeature == poFeatureToWrite)
247 : {
248 1 : CPLDebug("GeoJSON",
249 : "Running SetPrecision() to correct an invalid "
250 : "geometry due to reduced precision output");
251 1 : poValidGeom.reset(
252 : poOrigGeom->SetPrecision(dfXYResolution, /* nFlags = */ 0));
253 : }
254 : else
255 : {
256 1 : CPLDebug("GeoJSON", "Running MakeValid() to correct an invalid "
257 : "geometry due to reduced precision output");
258 1 : poValidGeom.reset(poNewGeom->MakeValid());
259 1 : if (poValidGeom)
260 : {
261 : auto poValidGeomRoundCoordinates =
262 2 : std::unique_ptr<OGRGeometry>(poValidGeom->clone());
263 1 : poValidGeomRoundCoordinates->roundCoordinates(sPrecision);
264 1 : if (!poValidGeomRoundCoordinates->IsValid(&osReason))
265 : {
266 1 : CPLDebug("GeoJSON",
267 : "Running SetPrecision() to correct an invalid "
268 : "geometry due to reduced precision output");
269 : auto poValidGeom2 = std::unique_ptr<OGRGeometry>(
270 : poValidGeom->SetPrecision(dfXYResolution,
271 2 : /* nFlags = */ 0));
272 1 : if (poValidGeom2)
273 1 : poValidGeom = std::move(poValidGeom2);
274 : }
275 : }
276 : else
277 : {
278 0 : CPLError(CE_Warning, CPLE_AppDefined,
279 : "Geometry %s is not valid: %s",
280 0 : poNewGeom->exportToWkt().c_str(),
281 : osReason.c_str());
282 : }
283 : }
284 2 : if (poValidGeom)
285 : {
286 2 : if (poFeature == poFeatureToWrite)
287 : {
288 1 : poFeatureToWrite = new OGRFeature(poFeatureDefn_);
289 1 : poFeatureToWrite->SetFrom(poFeature);
290 1 : poFeatureToWrite->SetFID(poFeature->GetFID());
291 : }
292 2 : poFeatureToWrite->SetGeometryDirectly(poValidGeom.release());
293 : }
294 : }
295 : }
296 :
297 1116 : if (oWriteOptions_.bGenerateID && poFeatureToWrite->GetFID() == OGRNullFID)
298 : {
299 3 : poFeatureToWrite->SetFID(nOutCounter_);
300 : }
301 : json_object *poObj =
302 1116 : OGRGeoJSONWriteFeature(poFeatureToWrite, oWriteOptions_);
303 1116 : CPLAssert(nullptr != poObj);
304 :
305 1116 : if (m_nPositionBeforeFCClosed)
306 : {
307 : // If we had called SyncToDisk() previously, undo its effects
308 1 : fp->Seek(m_nPositionBeforeFCClosed, SEEK_SET);
309 1 : m_nPositionBeforeFCClosed = 0;
310 : }
311 :
312 1116 : if (nOutCounter_ > 0)
313 : {
314 : /* Separate "Feature" entries in "FeatureCollection" object. */
315 969 : VSIFPrintfL(fp, ",\n");
316 : }
317 1116 : const char *pszJson = json_object_to_json_string_ext(
318 : poObj, JSON_C_TO_STRING_PLAIN
319 : #ifdef JSON_C_TO_STRING_NOSLASHESCAPE
320 : | JSON_C_TO_STRING_NOSLASHESCAPE
321 : #endif
322 : );
323 :
324 1116 : OGRErr eErr = OGRERR_NONE;
325 1116 : size_t nLen = strlen(pszJson);
326 1116 : if (!osForeignMembers_.empty())
327 : {
328 1 : if (nLen > 1 && pszJson[nLen - 1] == '}')
329 : {
330 1 : nLen -= 1;
331 : }
332 : else
333 : {
334 : // should not happen
335 0 : CPLError(CE_Failure, CPLE_AppDefined,
336 : "Unexpected JSON output for feature. Cannot write foreign "
337 : "member");
338 0 : osForeignMembers_.clear();
339 : }
340 : }
341 1116 : if (VSIFWriteL(pszJson, nLen, 1, fp) != 1)
342 : {
343 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot write feature");
344 0 : eErr = OGRERR_FAILURE;
345 : }
346 1117 : else if (!osForeignMembers_.empty() &&
347 1 : (VSIFWriteL(",", 1, 1, fp) != 1 ||
348 1 : VSIFWriteL(osForeignMembers_.c_str(), osForeignMembers_.size(), 1,
349 1 : fp) != 1 ||
350 1 : VSIFWriteL("}", 1, 1, fp) != 1))
351 : {
352 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot write feature");
353 0 : eErr = OGRERR_FAILURE;
354 : }
355 :
356 1116 : json_object_put(poObj);
357 :
358 1116 : ++nOutCounter_;
359 :
360 1116 : OGRGeometry *poGeometry = poFeatureToWrite->GetGeometryRef();
361 1116 : if (poGeometry != nullptr && !poGeometry->IsEmpty())
362 : {
363 1017 : OGREnvelope3D sEnvelope = OGRGeoJSONGetBBox(poGeometry, oWriteOptions_);
364 1017 : if (poGeometry->getCoordinateDimension() == 3)
365 21 : bBBOX3D = true;
366 :
367 1017 : if (!sEnvelopeLayer.IsInit())
368 : {
369 110 : sEnvelopeLayer = sEnvelope;
370 : }
371 907 : else if (oWriteOptions_.bBBOXRFC7946)
372 : {
373 20 : const bool bEnvelopeCrossAM = (sEnvelope.MinX > sEnvelope.MaxX);
374 20 : const bool bEnvelopeLayerCrossAM =
375 20 : (sEnvelopeLayer.MinX > sEnvelopeLayer.MaxX);
376 20 : if (bEnvelopeCrossAM)
377 : {
378 8 : if (bEnvelopeLayerCrossAM)
379 : {
380 8 : sEnvelopeLayer.MinX =
381 8 : std::min(sEnvelopeLayer.MinX, sEnvelope.MinX);
382 8 : sEnvelopeLayer.MaxX =
383 8 : std::max(sEnvelopeLayer.MaxX, sEnvelope.MaxX);
384 : }
385 : else
386 : {
387 0 : if (sEnvelopeLayer.MinX > 0)
388 : {
389 0 : sEnvelopeLayer.MinX =
390 0 : std::min(sEnvelopeLayer.MinX, sEnvelope.MinX);
391 0 : sEnvelopeLayer.MaxX = sEnvelope.MaxX;
392 : }
393 0 : else if (sEnvelopeLayer.MaxX < 0)
394 : {
395 0 : sEnvelopeLayer.MaxX =
396 0 : std::max(sEnvelopeLayer.MaxX, sEnvelope.MaxX);
397 0 : sEnvelopeLayer.MinX = sEnvelope.MinX;
398 : }
399 : else
400 : {
401 0 : sEnvelopeLayer.MinX = -180.0;
402 0 : sEnvelopeLayer.MaxX = 180.0;
403 : }
404 : }
405 : }
406 12 : else if (bEnvelopeLayerCrossAM)
407 : {
408 0 : if (sEnvelope.MinX > 0)
409 : {
410 0 : sEnvelopeLayer.MinX =
411 0 : std::min(sEnvelopeLayer.MinX, sEnvelope.MinX);
412 : }
413 0 : else if (sEnvelope.MaxX < 0)
414 : {
415 0 : sEnvelopeLayer.MaxX =
416 0 : std::max(sEnvelopeLayer.MaxX, sEnvelope.MaxX);
417 : }
418 : else
419 : {
420 0 : sEnvelopeLayer.MinX = -180.0;
421 0 : sEnvelopeLayer.MaxX = 180.0;
422 : }
423 : }
424 : else
425 : {
426 12 : sEnvelopeLayer.MinX =
427 12 : std::min(sEnvelopeLayer.MinX, sEnvelope.MinX);
428 12 : sEnvelopeLayer.MaxX =
429 12 : std::max(sEnvelopeLayer.MaxX, sEnvelope.MaxX);
430 : }
431 :
432 20 : sEnvelopeLayer.MinY = std::min(sEnvelopeLayer.MinY, sEnvelope.MinY);
433 20 : sEnvelopeLayer.MaxY = std::max(sEnvelopeLayer.MaxY, sEnvelope.MaxY);
434 : }
435 : else
436 : {
437 887 : sEnvelopeLayer.Merge(sEnvelope);
438 : }
439 : }
440 :
441 1116 : if (poFeatureToWrite != poFeature)
442 43 : delete poFeatureToWrite;
443 :
444 1116 : return eErr;
445 : }
446 :
447 : /************************************************************************/
448 : /* CreateField() */
449 : /************************************************************************/
450 :
451 231 : OGRErr OGRGeoJSONWriteLayer::CreateField(const OGRFieldDefn *poField,
452 : int /* bApproxOK */)
453 : {
454 231 : if (poFeatureDefn_->GetFieldIndexCaseSensitive(poField->GetNameRef()) >= 0)
455 : {
456 0 : CPLDebug("GeoJSON", "Field '%s' already present in schema",
457 : poField->GetNameRef());
458 :
459 : // TODO - mloskot: Is this return code correct?
460 0 : return OGRERR_NONE;
461 : }
462 :
463 231 : poFeatureDefn_->AddFieldDefn(poField);
464 :
465 231 : return OGRERR_NONE;
466 : }
467 :
468 : /************************************************************************/
469 : /* TestCapability() */
470 : /************************************************************************/
471 :
472 558 : int OGRGeoJSONWriteLayer::TestCapability(const char *pszCap) const
473 : {
474 558 : if (EQUAL(pszCap, OLCCreateField))
475 16 : return TRUE;
476 542 : else if (EQUAL(pszCap, OLCSequentialWrite))
477 20 : return TRUE;
478 522 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
479 0 : return TRUE;
480 522 : return FALSE;
481 : }
482 :
483 : /************************************************************************/
484 : /* IGetExtent() */
485 : /************************************************************************/
486 :
487 2 : OGRErr OGRGeoJSONWriteLayer::IGetExtent(int /*iGeomField*/,
488 : OGREnvelope *psExtent, bool)
489 : {
490 2 : if (sEnvelopeLayer.IsInit())
491 : {
492 2 : *psExtent = sEnvelopeLayer;
493 2 : return OGRERR_NONE;
494 : }
495 0 : return OGRERR_FAILURE;
496 : }
497 :
498 : /************************************************************************/
499 : /* GetDataset() */
500 : /************************************************************************/
501 :
502 39 : GDALDataset *OGRGeoJSONWriteLayer::GetDataset()
503 : {
504 39 : return poDS_;
505 : }
|