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