/src/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonseqdriver.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: OpenGIS Simple Features Reference Implementation |
4 | | * Purpose: GeoJSON feature sequence driver |
5 | | * Author: Even Rouault <even.rouault at spatialys.com> |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2018, Even Rouault <even.rouault at spatialys.com> |
9 | | * |
10 | | * SPDX-License-Identifier: MIT |
11 | | ****************************************************************************/ |
12 | | |
13 | | #include "cpl_port.h" |
14 | | #include "cpl_vsi_virtual.h" |
15 | | #include "cpl_http.h" |
16 | | #include "cpl_vsi_error.h" |
17 | | |
18 | | #include "ogr_geojson.h" |
19 | | #include "ogrlibjsonutils.h" |
20 | | #include "ogrgeojsonreader.h" |
21 | | #include "ogrgeojsonwriter.h" |
22 | | #include "ogrgeojsongeometry.h" |
23 | | |
24 | | #include <algorithm> |
25 | | #include <memory> |
26 | | |
27 | | constexpr char RS = '\x1e'; |
28 | | |
29 | | /************************************************************************/ |
30 | | /* OGRGeoJSONSeqDataSource */ |
31 | | /************************************************************************/ |
32 | | |
33 | | class OGRGeoJSONSeqDataSource final : public GDALDataset |
34 | | { |
35 | | friend class OGRGeoJSONSeqLayer; |
36 | | |
37 | | std::vector<std::unique_ptr<OGRLayer>> m_apoLayers{}; |
38 | | CPLString m_osTmpFile; |
39 | | VSILFILE *m_fp = nullptr; |
40 | | bool m_bSupportsRead = true; |
41 | | bool m_bAtEOF = false; |
42 | | bool m_bIsRSSeparated = false; |
43 | | |
44 | | public: |
45 | | OGRGeoJSONSeqDataSource(); |
46 | | ~OGRGeoJSONSeqDataSource(); |
47 | | |
48 | | int GetLayerCount() override |
49 | 0 | { |
50 | 0 | return static_cast<int>(m_apoLayers.size()); |
51 | 0 | } |
52 | | |
53 | | OGRLayer *GetLayer(int) override; |
54 | | OGRLayer *ICreateLayer(const char *pszName, |
55 | | const OGRGeomFieldDefn *poGeomFieldDefn, |
56 | | CSLConstList papszOptions) override; |
57 | | int TestCapability(const char *pszCap) override; |
58 | | |
59 | | bool Open(GDALOpenInfo *poOpenInfo, GeoJSONSourceType nSrcType); |
60 | | bool Create(const char *pszName, char **papszOptions); |
61 | | }; |
62 | | |
63 | | /************************************************************************/ |
64 | | /* OGRGeoJSONSeqLayer */ |
65 | | /************************************************************************/ |
66 | | |
67 | | class OGRGeoJSONSeqLayer final : public OGRLayer |
68 | | { |
69 | | OGRGeoJSONSeqDataSource *m_poDS = nullptr; |
70 | | OGRFeatureDefn *m_poFeatureDefn = nullptr; |
71 | | bool m_bLayerDefnEstablished = false; |
72 | | bool m_bWriteOnlyLayer = false; |
73 | | |
74 | | OGRGeoJSONBaseReader m_oReader; |
75 | | CPLString m_osFIDColumn; |
76 | | |
77 | | size_t m_nMaxObjectSize = 0; |
78 | | std::string m_osBuffer; |
79 | | std::string m_osFeatureBuffer; |
80 | | size_t m_nPosInBuffer = 0; |
81 | | size_t m_nBufferValidSize = 0; |
82 | | |
83 | | vsi_l_offset m_nFileSize = 0; |
84 | | GIntBig m_nIter = 0; |
85 | | |
86 | | GIntBig m_nTotalFeatures = 0; |
87 | | GIntBig m_nNextFID = 0; |
88 | | |
89 | | std::unique_ptr<OGRCoordinateTransformation> m_poCT{}; |
90 | | OGRGeometryFactory::TransformWithOptionsCache m_oTransformCache; |
91 | | OGRGeoJSONWriteOptions m_oWriteOptions; |
92 | | |
93 | | json_object *GetNextObject(bool bLooseIdentification); |
94 | | |
95 | | public: |
96 | | OGRGeoJSONSeqLayer(OGRGeoJSONSeqDataSource *poDS, const char *pszName); |
97 | | |
98 | | // Write-only constructor |
99 | | OGRGeoJSONSeqLayer(OGRGeoJSONSeqDataSource *poDS, const char *pszName, |
100 | | CSLConstList papszOptions, |
101 | | std::unique_ptr<OGRCoordinateTransformation> &&poCT); |
102 | | |
103 | | ~OGRGeoJSONSeqLayer(); |
104 | | |
105 | | bool Init(bool bLooseIdentification, bool bEstablishLayerDefn); |
106 | | |
107 | | const char *GetName() override |
108 | 0 | { |
109 | 0 | return GetDescription(); |
110 | 0 | } |
111 | | |
112 | | void ResetReading() override; |
113 | | OGRFeature *GetNextFeature() override; |
114 | | OGRFeatureDefn *GetLayerDefn() override; |
115 | | |
116 | | const char *GetFIDColumn() override |
117 | 0 | { |
118 | 0 | return m_osFIDColumn.c_str(); |
119 | 0 | } |
120 | | |
121 | | GIntBig GetFeatureCount(int) override; |
122 | | int TestCapability(const char *) override; |
123 | | OGRErr ICreateFeature(OGRFeature *poFeature) override; |
124 | | OGRErr CreateField(const OGRFieldDefn *, int) override; |
125 | | |
126 | | GDALDataset *GetDataset() override |
127 | 0 | { |
128 | 0 | return m_poDS; |
129 | 0 | } |
130 | | }; |
131 | | |
132 | | /************************************************************************/ |
133 | | /* OGRGeoJSONSeqDataSource() */ |
134 | | /************************************************************************/ |
135 | | |
136 | | OGRGeoJSONSeqDataSource::OGRGeoJSONSeqDataSource() |
137 | 0 | { |
138 | 0 | } |
139 | | |
140 | | /************************************************************************/ |
141 | | /* ~OGRGeoJSONSeqDataSource() */ |
142 | | /************************************************************************/ |
143 | | |
144 | | OGRGeoJSONSeqDataSource::~OGRGeoJSONSeqDataSource() |
145 | 0 | { |
146 | 0 | if (m_fp) |
147 | 0 | { |
148 | 0 | VSIFCloseL(m_fp); |
149 | 0 | } |
150 | 0 | if (!m_osTmpFile.empty()) |
151 | 0 | { |
152 | 0 | VSIUnlink(m_osTmpFile); |
153 | 0 | } |
154 | 0 | } |
155 | | |
156 | | /************************************************************************/ |
157 | | /* GetLayer() */ |
158 | | /************************************************************************/ |
159 | | |
160 | | OGRLayer *OGRGeoJSONSeqDataSource::GetLayer(int nIndex) |
161 | 0 | { |
162 | 0 | if (nIndex < 0 || nIndex >= GetLayerCount()) |
163 | 0 | return nullptr; |
164 | 0 | return m_apoLayers[nIndex].get(); |
165 | 0 | } |
166 | | |
167 | | /************************************************************************/ |
168 | | /* ICreateLayer() */ |
169 | | /************************************************************************/ |
170 | | |
171 | | OGRLayer *OGRGeoJSONSeqDataSource::ICreateLayer( |
172 | | const char *pszNameIn, const OGRGeomFieldDefn *poSrcGeomFieldDefn, |
173 | | CSLConstList papszOptions) |
174 | 0 | { |
175 | 0 | if (!TestCapability(ODsCCreateLayer)) |
176 | 0 | return nullptr; |
177 | | |
178 | 0 | const auto poSRS = |
179 | 0 | poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetSpatialRef() : nullptr; |
180 | |
|
181 | 0 | std::unique_ptr<OGRCoordinateTransformation> poCT; |
182 | 0 | if (poSRS == nullptr) |
183 | 0 | { |
184 | 0 | CPLError( |
185 | 0 | CE_Warning, CPLE_AppDefined, |
186 | 0 | "No SRS set on layer. Assuming it is long/lat on WGS84 ellipsoid"); |
187 | 0 | } |
188 | 0 | else |
189 | 0 | { |
190 | 0 | OGRSpatialReference oSRSWGS84; |
191 | 0 | oSRSWGS84.SetWellKnownGeogCS("WGS84"); |
192 | 0 | oSRSWGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
193 | 0 | const char *const apszOptions[] = { |
194 | 0 | "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES", nullptr}; |
195 | 0 | if (!poSRS->IsSame(&oSRSWGS84, apszOptions)) |
196 | 0 | { |
197 | 0 | poCT.reset(OGRCreateCoordinateTransformation(poSRS, &oSRSWGS84)); |
198 | 0 | if (poCT == nullptr) |
199 | 0 | { |
200 | 0 | CPLError( |
201 | 0 | CE_Warning, CPLE_AppDefined, |
202 | 0 | "Failed to create coordinate transformation between the " |
203 | 0 | "input coordinate system and WGS84."); |
204 | |
|
205 | 0 | return nullptr; |
206 | 0 | } |
207 | 0 | } |
208 | 0 | } |
209 | | |
210 | 0 | const char *pszRS = CSLFetchNameValue(papszOptions, "RS"); |
211 | 0 | if (pszRS) |
212 | 0 | { |
213 | 0 | m_bIsRSSeparated = CPLTestBool(pszRS); |
214 | 0 | } |
215 | |
|
216 | 0 | CPLStringList aosOptions(papszOptions); |
217 | |
|
218 | 0 | double dfXYResolution = OGRGeomCoordinatePrecision::UNKNOWN; |
219 | 0 | double dfZResolution = OGRGeomCoordinatePrecision::UNKNOWN; |
220 | 0 | if (const char *pszCoordPrecision = |
221 | 0 | CSLFetchNameValue(papszOptions, "COORDINATE_PRECISION")) |
222 | 0 | { |
223 | 0 | dfXYResolution = std::pow(10.0, -CPLAtof(pszCoordPrecision)); |
224 | 0 | dfZResolution = dfXYResolution; |
225 | 0 | } |
226 | 0 | else if (poSrcGeomFieldDefn) |
227 | 0 | { |
228 | 0 | const auto &oCoordPrec = poSrcGeomFieldDefn->GetCoordinatePrecision(); |
229 | 0 | OGRSpatialReference oSRSWGS84; |
230 | 0 | oSRSWGS84.SetWellKnownGeogCS("WGS84"); |
231 | 0 | const auto oCoordPrecWGS84 = |
232 | 0 | oCoordPrec.ConvertToOtherSRS(poSRS, &oSRSWGS84); |
233 | |
|
234 | 0 | if (oCoordPrec.dfXYResolution != OGRGeomCoordinatePrecision::UNKNOWN) |
235 | 0 | { |
236 | 0 | dfXYResolution = oCoordPrecWGS84.dfXYResolution; |
237 | |
|
238 | 0 | aosOptions.SetNameValue( |
239 | 0 | "XY_COORD_PRECISION", |
240 | 0 | CPLSPrintf("%d", |
241 | 0 | OGRGeomCoordinatePrecision::ResolutionToPrecision( |
242 | 0 | dfXYResolution))); |
243 | 0 | } |
244 | 0 | if (oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN) |
245 | 0 | { |
246 | 0 | dfZResolution = oCoordPrecWGS84.dfZResolution; |
247 | |
|
248 | 0 | aosOptions.SetNameValue( |
249 | 0 | "Z_COORD_PRECISION", |
250 | 0 | CPLSPrintf("%d", |
251 | 0 | OGRGeomCoordinatePrecision::ResolutionToPrecision( |
252 | 0 | dfZResolution))); |
253 | 0 | } |
254 | 0 | } |
255 | |
|
256 | 0 | m_apoLayers.emplace_back(std::make_unique<OGRGeoJSONSeqLayer>( |
257 | 0 | this, pszNameIn, aosOptions.List(), std::move(poCT))); |
258 | |
|
259 | 0 | auto poLayer = m_apoLayers.back().get(); |
260 | 0 | if (poLayer->GetGeomType() != wkbNone && |
261 | 0 | dfXYResolution != OGRGeomCoordinatePrecision::UNKNOWN) |
262 | 0 | { |
263 | 0 | auto poGeomFieldDefn = poLayer->GetLayerDefn()->GetGeomFieldDefn(0); |
264 | 0 | OGRGeomCoordinatePrecision oCoordPrec( |
265 | 0 | poGeomFieldDefn->GetCoordinatePrecision()); |
266 | 0 | oCoordPrec.dfXYResolution = dfXYResolution; |
267 | 0 | poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec); |
268 | 0 | } |
269 | |
|
270 | 0 | if (poLayer->GetGeomType() != wkbNone && |
271 | 0 | dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN) |
272 | 0 | { |
273 | 0 | auto poGeomFieldDefn = poLayer->GetLayerDefn()->GetGeomFieldDefn(0); |
274 | 0 | OGRGeomCoordinatePrecision oCoordPrec( |
275 | 0 | poGeomFieldDefn->GetCoordinatePrecision()); |
276 | 0 | oCoordPrec.dfZResolution = dfZResolution; |
277 | 0 | poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec); |
278 | 0 | } |
279 | |
|
280 | 0 | return poLayer; |
281 | 0 | } |
282 | | |
283 | | /************************************************************************/ |
284 | | /* TestCapability() */ |
285 | | /************************************************************************/ |
286 | | |
287 | | int OGRGeoJSONSeqDataSource::TestCapability(const char *pszCap) |
288 | 0 | { |
289 | 0 | if (EQUAL(pszCap, ODsCCreateLayer)) |
290 | 0 | return eAccess == GA_Update; |
291 | | |
292 | 0 | return FALSE; |
293 | 0 | } |
294 | | |
295 | | /************************************************************************/ |
296 | | /* OGRGeoJSONSeqLayer() */ |
297 | | /************************************************************************/ |
298 | | |
299 | | OGRGeoJSONSeqLayer::OGRGeoJSONSeqLayer(OGRGeoJSONSeqDataSource *poDS, |
300 | | const char *pszName) |
301 | 0 | : m_poDS(poDS) |
302 | 0 | { |
303 | 0 | SetDescription(pszName); |
304 | 0 | m_poFeatureDefn = new OGRFeatureDefn(pszName); |
305 | 0 | m_poFeatureDefn->Reference(); |
306 | |
|
307 | 0 | OGRSpatialReference *poSRSWGS84 = new OGRSpatialReference(); |
308 | 0 | poSRSWGS84->SetWellKnownGeogCS("WGS84"); |
309 | 0 | poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
310 | 0 | m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRSWGS84); |
311 | 0 | poSRSWGS84->Release(); |
312 | |
|
313 | 0 | const double dfTmp = |
314 | 0 | CPLAtof(CPLGetConfigOption("OGR_GEOJSON_MAX_OBJ_SIZE", "200")); |
315 | 0 | m_nMaxObjectSize = dfTmp > 0 ? static_cast<size_t>(dfTmp * 1024 * 1024) : 0; |
316 | 0 | } |
317 | | |
318 | | /************************************************************************/ |
319 | | /* OGRGeoJSONSeqLayer() */ |
320 | | /************************************************************************/ |
321 | | |
322 | | // Write-only constructor |
323 | | OGRGeoJSONSeqLayer::OGRGeoJSONSeqLayer( |
324 | | OGRGeoJSONSeqDataSource *poDS, const char *pszName, |
325 | | CSLConstList papszOptions, |
326 | | std::unique_ptr<OGRCoordinateTransformation> &&poCT) |
327 | 0 | : m_poDS(poDS), m_bWriteOnlyLayer(true) |
328 | 0 | { |
329 | 0 | m_bLayerDefnEstablished = true; |
330 | |
|
331 | 0 | SetDescription(pszName); |
332 | 0 | m_poFeatureDefn = new OGRFeatureDefn(pszName); |
333 | 0 | m_poFeatureDefn->Reference(); |
334 | 0 | m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef( |
335 | 0 | OGRSpatialReference::GetWGS84SRS()); |
336 | 0 | m_poCT = std::move(poCT); |
337 | |
|
338 | 0 | m_oWriteOptions.bWriteBBOX = |
339 | 0 | CPLTestBool(CSLFetchNameValueDef(papszOptions, "WRITE_BBOX", "FALSE")); |
340 | 0 | m_oWriteOptions.SetRFC7946Settings(); |
341 | 0 | m_oWriteOptions.SetIDOptions(papszOptions); |
342 | |
|
343 | 0 | const char *pszCoordPrecision = |
344 | 0 | CSLFetchNameValue(papszOptions, "COORDINATE_PRECISION"); |
345 | 0 | if (pszCoordPrecision) |
346 | 0 | { |
347 | 0 | m_oWriteOptions.nXYCoordPrecision = atoi(pszCoordPrecision); |
348 | 0 | m_oWriteOptions.nZCoordPrecision = atoi(pszCoordPrecision); |
349 | 0 | } |
350 | 0 | else |
351 | 0 | { |
352 | 0 | m_oWriteOptions.nXYCoordPrecision = |
353 | 0 | atoi(CSLFetchNameValueDef(papszOptions, "XY_COORD_PRECISION", "7")); |
354 | 0 | m_oWriteOptions.nZCoordPrecision = |
355 | 0 | atoi(CSLFetchNameValueDef(papszOptions, "Z_COORD_PRECISION", "3")); |
356 | 0 | } |
357 | |
|
358 | 0 | m_oWriteOptions.nSignificantFigures = |
359 | 0 | atoi(CSLFetchNameValueDef(papszOptions, "SIGNIFICANT_FIGURES", "-1")); |
360 | 0 | m_oWriteOptions.bAllowNonFiniteValues = CPLTestBool( |
361 | 0 | CSLFetchNameValueDef(papszOptions, "WRITE_NON_FINITE_VALUES", "FALSE")); |
362 | 0 | m_oWriteOptions.bAutodetectJsonStrings = CPLTestBool( |
363 | 0 | CSLFetchNameValueDef(papszOptions, "AUTODETECT_JSON_STRINGS", "TRUE")); |
364 | 0 | } |
365 | | |
366 | | /************************************************************************/ |
367 | | /* ~OGRGeoJSONSeqLayer() */ |
368 | | /************************************************************************/ |
369 | | |
370 | | OGRGeoJSONSeqLayer::~OGRGeoJSONSeqLayer() |
371 | 0 | { |
372 | 0 | m_poFeatureDefn->Release(); |
373 | 0 | } |
374 | | |
375 | | /************************************************************************/ |
376 | | /* GetLayerDefn() */ |
377 | | /************************************************************************/ |
378 | | |
379 | | OGRFeatureDefn *OGRGeoJSONSeqLayer::GetLayerDefn() |
380 | 0 | { |
381 | 0 | if (!m_bLayerDefnEstablished) |
382 | 0 | { |
383 | 0 | Init(/* bLooseIdentification = */ false, |
384 | 0 | /* bEstablishLayerDefn = */ true); |
385 | 0 | } |
386 | 0 | return m_poFeatureDefn; |
387 | 0 | } |
388 | | |
389 | | /************************************************************************/ |
390 | | /* Init() */ |
391 | | /************************************************************************/ |
392 | | |
393 | | bool OGRGeoJSONSeqLayer::Init(bool bLooseIdentification, |
394 | | bool bEstablishLayerDefn) |
395 | 0 | { |
396 | 0 | if (STARTS_WITH(m_poDS->GetDescription(), "/vsimem/") || |
397 | 0 | !STARTS_WITH(m_poDS->GetDescription(), "/vsi")) |
398 | 0 | { |
399 | 0 | VSIFSeekL(m_poDS->m_fp, 0, SEEK_END); |
400 | 0 | m_nFileSize = VSIFTellL(m_poDS->m_fp); |
401 | 0 | } |
402 | | |
403 | | // Set m_bLayerDefnEstablished = true early to avoid infinite recursive |
404 | | // calls. |
405 | 0 | if (bEstablishLayerDefn) |
406 | 0 | m_bLayerDefnEstablished = true; |
407 | |
|
408 | 0 | ResetReading(); |
409 | |
|
410 | 0 | std::map<std::string, int> oMapFieldNameToIdx; |
411 | 0 | std::vector<std::unique_ptr<OGRFieldDefn>> apoFieldDefn; |
412 | 0 | gdal::DirectedAcyclicGraph<int, std::string> dag; |
413 | 0 | bool bOK = false; |
414 | |
|
415 | 0 | while (true) |
416 | 0 | { |
417 | 0 | auto poObject = GetNextObject(bLooseIdentification); |
418 | 0 | if (!poObject) |
419 | 0 | break; |
420 | 0 | const auto eObjectType = OGRGeoJSONGetType(poObject); |
421 | 0 | if (bEstablishLayerDefn && eObjectType == GeoJSONObject::eFeature) |
422 | 0 | { |
423 | 0 | m_oReader.GenerateFeatureDefn(oMapFieldNameToIdx, apoFieldDefn, dag, |
424 | 0 | this, poObject); |
425 | 0 | } |
426 | 0 | json_object_put(poObject); |
427 | 0 | if (!bEstablishLayerDefn) |
428 | 0 | { |
429 | 0 | bOK = (eObjectType == GeoJSONObject::eFeature); |
430 | 0 | break; |
431 | 0 | } |
432 | 0 | m_nTotalFeatures++; |
433 | 0 | } |
434 | |
|
435 | 0 | if (bEstablishLayerDefn) |
436 | 0 | { |
437 | | // CPLDebug("GEOJSONSEQ", "Establish layer definition"); |
438 | |
|
439 | 0 | const auto sortedFields = dag.getTopologicalOrdering(); |
440 | 0 | CPLAssert(sortedFields.size() == apoFieldDefn.size()); |
441 | 0 | for (int idx : sortedFields) |
442 | 0 | { |
443 | 0 | m_poFeatureDefn->AddFieldDefn(apoFieldDefn[idx].get()); |
444 | 0 | } |
445 | 0 | m_poFeatureDefn->Seal(true); |
446 | 0 | m_oReader.FinalizeLayerDefn(this, m_osFIDColumn); |
447 | 0 | } |
448 | | |
449 | 0 | ResetReading(); |
450 | |
|
451 | 0 | m_nFileSize = 0; |
452 | 0 | m_nIter = 0; |
453 | |
|
454 | 0 | return bOK || m_nTotalFeatures > 0; |
455 | 0 | } |
456 | | |
457 | | /************************************************************************/ |
458 | | /* ResetReading() */ |
459 | | /************************************************************************/ |
460 | | |
461 | | void OGRGeoJSONSeqLayer::ResetReading() |
462 | 0 | { |
463 | 0 | if (!m_poDS->m_bSupportsRead || |
464 | 0 | (m_bWriteOnlyLayer && m_poDS->m_apoLayers.size() > 1)) |
465 | 0 | { |
466 | 0 | return; |
467 | 0 | } |
468 | | |
469 | 0 | m_poDS->m_bAtEOF = false; |
470 | 0 | VSIFSeekL(m_poDS->m_fp, 0, SEEK_SET); |
471 | | // Undocumented: for testing purposes only |
472 | 0 | const size_t nBufferSize = static_cast<size_t>(std::max( |
473 | 0 | 1, atoi(CPLGetConfigOption("OGR_GEOJSONSEQ_CHUNK_SIZE", "40960")))); |
474 | 0 | const size_t nBufferSizeValidated = |
475 | 0 | nBufferSize > static_cast<size_t>(100 * 1000 * 1000) |
476 | 0 | ? static_cast<size_t>(100 * 1000 * 1000) |
477 | 0 | : nBufferSize; |
478 | 0 | m_osBuffer.resize(nBufferSizeValidated); |
479 | 0 | m_osFeatureBuffer.clear(); |
480 | 0 | m_nPosInBuffer = nBufferSizeValidated; |
481 | 0 | m_nBufferValidSize = nBufferSizeValidated; |
482 | 0 | m_nNextFID = 0; |
483 | 0 | } |
484 | | |
485 | | /************************************************************************/ |
486 | | /* GetNextObject() */ |
487 | | /************************************************************************/ |
488 | | |
489 | | json_object *OGRGeoJSONSeqLayer::GetNextObject(bool bLooseIdentification) |
490 | 0 | { |
491 | 0 | m_osFeatureBuffer.clear(); |
492 | 0 | while (true) |
493 | 0 | { |
494 | | // If we read all the buffer, then reload it from file |
495 | 0 | if (m_nPosInBuffer >= m_nBufferValidSize) |
496 | 0 | { |
497 | 0 | if (m_nBufferValidSize < m_osBuffer.size()) |
498 | 0 | { |
499 | 0 | return nullptr; |
500 | 0 | } |
501 | 0 | m_nBufferValidSize = |
502 | 0 | VSIFReadL(&m_osBuffer[0], 1, m_osBuffer.size(), m_poDS->m_fp); |
503 | 0 | m_nPosInBuffer = 0; |
504 | 0 | if (VSIFTellL(m_poDS->m_fp) == m_nBufferValidSize && |
505 | 0 | m_nBufferValidSize > 0) |
506 | 0 | { |
507 | 0 | m_poDS->m_bIsRSSeparated = (m_osBuffer[0] == RS); |
508 | 0 | if (m_poDS->m_bIsRSSeparated) |
509 | 0 | { |
510 | 0 | m_nPosInBuffer++; |
511 | 0 | } |
512 | 0 | } |
513 | 0 | m_nIter++; |
514 | |
|
515 | 0 | if (m_nFileSize > 0 && (m_nBufferValidSize < m_osBuffer.size() || |
516 | 0 | (m_nIter % 100) == 0)) |
517 | 0 | { |
518 | 0 | CPLDebug("GeoJSONSeq", "First pass: %.2f %%", |
519 | 0 | 100.0 * VSIFTellL(m_poDS->m_fp) / m_nFileSize); |
520 | 0 | } |
521 | 0 | if (m_nPosInBuffer >= m_nBufferValidSize) |
522 | 0 | { |
523 | 0 | return nullptr; |
524 | 0 | } |
525 | 0 | } |
526 | | |
527 | | // Find next feature separator in buffer |
528 | 0 | const size_t nNextSepPos = m_osBuffer.find( |
529 | 0 | m_poDS->m_bIsRSSeparated ? RS : '\n', m_nPosInBuffer); |
530 | 0 | if (nNextSepPos != std::string::npos) |
531 | 0 | { |
532 | 0 | m_osFeatureBuffer.append(m_osBuffer.data() + m_nPosInBuffer, |
533 | 0 | nNextSepPos - m_nPosInBuffer); |
534 | 0 | m_nPosInBuffer = nNextSepPos + 1; |
535 | 0 | } |
536 | 0 | else |
537 | 0 | { |
538 | | // No separator ? then accummulate |
539 | 0 | m_osFeatureBuffer.append(m_osBuffer.data() + m_nPosInBuffer, |
540 | 0 | m_nBufferValidSize - m_nPosInBuffer); |
541 | 0 | if (m_nMaxObjectSize > 0 && |
542 | 0 | m_osFeatureBuffer.size() > m_nMaxObjectSize) |
543 | 0 | { |
544 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
545 | 0 | "Too large feature. You may define the " |
546 | 0 | "OGR_GEOJSON_MAX_OBJ_SIZE configuration option to " |
547 | 0 | "a value in megabytes (larger than %u) to allow " |
548 | 0 | "for larger features, or 0 to remove any size limit.", |
549 | 0 | static_cast<unsigned>(m_osFeatureBuffer.size() / 1024 / |
550 | 0 | 1024)); |
551 | 0 | return nullptr; |
552 | 0 | } |
553 | 0 | m_nPosInBuffer = m_nBufferValidSize; |
554 | 0 | if (m_nBufferValidSize == m_osBuffer.size()) |
555 | 0 | { |
556 | 0 | continue; |
557 | 0 | } |
558 | 0 | } |
559 | | |
560 | 0 | while (!m_osFeatureBuffer.empty() && |
561 | 0 | (m_osFeatureBuffer.back() == '\r' || |
562 | 0 | m_osFeatureBuffer.back() == '\n')) |
563 | 0 | { |
564 | 0 | m_osFeatureBuffer.pop_back(); |
565 | 0 | } |
566 | 0 | if (!m_osFeatureBuffer.empty()) |
567 | 0 | { |
568 | 0 | json_object *poObject = nullptr; |
569 | 0 | CPL_IGNORE_RET_VAL( |
570 | 0 | OGRJSonParse(m_osFeatureBuffer.c_str(), &poObject)); |
571 | 0 | m_osFeatureBuffer.clear(); |
572 | 0 | if (json_object_get_type(poObject) == json_type_object) |
573 | 0 | { |
574 | 0 | return poObject; |
575 | 0 | } |
576 | 0 | json_object_put(poObject); |
577 | 0 | if (bLooseIdentification) |
578 | 0 | { |
579 | 0 | return nullptr; |
580 | 0 | } |
581 | 0 | } |
582 | 0 | } |
583 | 0 | } |
584 | | |
585 | | /************************************************************************/ |
586 | | /* GetNextFeature() */ |
587 | | /************************************************************************/ |
588 | | |
589 | | OGRFeature *OGRGeoJSONSeqLayer::GetNextFeature() |
590 | 0 | { |
591 | 0 | if (!m_poDS->m_bSupportsRead) |
592 | 0 | { |
593 | 0 | return nullptr; |
594 | 0 | } |
595 | 0 | if (m_bWriteOnlyLayer && m_poDS->m_apoLayers.size() > 1) |
596 | 0 | { |
597 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
598 | 0 | "GetNextFeature() not supported when appending a new layer"); |
599 | 0 | return nullptr; |
600 | 0 | } |
601 | | |
602 | 0 | GetLayerDefn(); // force scan if not already done |
603 | 0 | while (true) |
604 | 0 | { |
605 | 0 | auto poObject = GetNextObject(false); |
606 | 0 | if (!poObject) |
607 | 0 | return nullptr; |
608 | 0 | OGRFeature *poFeature; |
609 | 0 | auto type = OGRGeoJSONGetType(poObject); |
610 | 0 | if (type == GeoJSONObject::eFeature) |
611 | 0 | { |
612 | 0 | poFeature = m_oReader.ReadFeature(this, poObject, |
613 | 0 | m_osFeatureBuffer.c_str()); |
614 | 0 | json_object_put(poObject); |
615 | 0 | } |
616 | 0 | else if (type == GeoJSONObject::eFeatureCollection || |
617 | 0 | type == GeoJSONObject::eUnknown) |
618 | 0 | { |
619 | 0 | json_object_put(poObject); |
620 | 0 | continue; |
621 | 0 | } |
622 | 0 | else |
623 | 0 | { |
624 | 0 | OGRGeometry *poGeom = |
625 | 0 | m_oReader.ReadGeometry(poObject, GetSpatialRef()); |
626 | 0 | json_object_put(poObject); |
627 | 0 | if (!poGeom) |
628 | 0 | { |
629 | 0 | continue; |
630 | 0 | } |
631 | 0 | poFeature = new OGRFeature(m_poFeatureDefn); |
632 | 0 | poFeature->SetGeometryDirectly(poGeom); |
633 | 0 | } |
634 | | |
635 | 0 | if (poFeature->GetFID() == OGRNullFID) |
636 | 0 | { |
637 | 0 | poFeature->SetFID(m_nNextFID); |
638 | 0 | m_nNextFID++; |
639 | 0 | } |
640 | 0 | if ((m_poFilterGeom == nullptr || |
641 | 0 | FilterGeometry(poFeature->GetGeomFieldRef(m_iGeomFieldFilter))) && |
642 | 0 | (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature))) |
643 | 0 | { |
644 | 0 | return poFeature; |
645 | 0 | } |
646 | 0 | delete poFeature; |
647 | 0 | } |
648 | 0 | } |
649 | | |
650 | | /************************************************************************/ |
651 | | /* GetFeatureCount() */ |
652 | | /************************************************************************/ |
653 | | |
654 | | GIntBig OGRGeoJSONSeqLayer::GetFeatureCount(int bForce) |
655 | 0 | { |
656 | 0 | if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr) |
657 | 0 | { |
658 | 0 | GetLayerDefn(); // force scan if not already done |
659 | 0 | return m_nTotalFeatures; |
660 | 0 | } |
661 | 0 | return OGRLayer::GetFeatureCount(bForce); |
662 | 0 | } |
663 | | |
664 | | /************************************************************************/ |
665 | | /* TestCapability() */ |
666 | | /************************************************************************/ |
667 | | |
668 | | int OGRGeoJSONSeqLayer::TestCapability(const char *pszCap) |
669 | 0 | { |
670 | 0 | if (EQUAL(pszCap, OLCStringsAsUTF8)) |
671 | 0 | return true; |
672 | 0 | if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr && |
673 | 0 | EQUAL(pszCap, OLCFastFeatureCount)) |
674 | 0 | { |
675 | 0 | return true; |
676 | 0 | } |
677 | 0 | if (EQUAL(pszCap, OLCCreateField) || EQUAL(pszCap, OLCSequentialWrite)) |
678 | 0 | { |
679 | 0 | return m_poDS->GetAccess() == GA_Update; |
680 | 0 | } |
681 | | |
682 | 0 | return false; |
683 | 0 | } |
684 | | |
685 | | /************************************************************************/ |
686 | | /* ICreateFeature() */ |
687 | | /************************************************************************/ |
688 | | |
689 | | OGRErr OGRGeoJSONSeqLayer::ICreateFeature(OGRFeature *poFeature) |
690 | 0 | { |
691 | 0 | if (m_poDS->GetAccess() != GA_Update) |
692 | 0 | return OGRERR_FAILURE; |
693 | | |
694 | 0 | if (!m_poDS->m_bAtEOF) |
695 | 0 | { |
696 | 0 | m_poDS->m_bAtEOF = true; |
697 | 0 | VSIFSeekL(m_poDS->m_fp, 0, SEEK_END); |
698 | 0 | } |
699 | |
|
700 | 0 | std::unique_ptr<OGRFeature> poFeatureToWrite; |
701 | 0 | if (m_poCT != nullptr) |
702 | 0 | { |
703 | 0 | poFeatureToWrite.reset(new OGRFeature(m_poFeatureDefn)); |
704 | 0 | poFeatureToWrite->SetFrom(poFeature); |
705 | 0 | poFeatureToWrite->SetFID(poFeature->GetFID()); |
706 | 0 | OGRGeometry *poGeometry = poFeatureToWrite->GetGeometryRef(); |
707 | 0 | if (poGeometry) |
708 | 0 | { |
709 | 0 | const char *const apszOptions[] = {"WRAPDATELINE=YES", nullptr}; |
710 | 0 | OGRGeometry *poNewGeom = OGRGeometryFactory::transformWithOptions( |
711 | 0 | poGeometry, m_poCT.get(), const_cast<char **>(apszOptions), |
712 | 0 | m_oTransformCache); |
713 | 0 | if (poNewGeom == nullptr) |
714 | 0 | { |
715 | 0 | return OGRERR_FAILURE; |
716 | 0 | } |
717 | | |
718 | 0 | OGREnvelope sEnvelope; |
719 | 0 | poNewGeom->getEnvelope(&sEnvelope); |
720 | 0 | if (sEnvelope.MinX < -180.0 || sEnvelope.MaxX > 180.0 || |
721 | 0 | sEnvelope.MinY < -90.0 || sEnvelope.MaxY > 90.0) |
722 | 0 | { |
723 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
724 | 0 | "Geometry extent outside of " |
725 | 0 | "[-180.0,180.0]x[-90.0,90.0] bounds"); |
726 | 0 | return OGRERR_FAILURE; |
727 | 0 | } |
728 | | |
729 | 0 | poFeatureToWrite->SetGeometryDirectly(poNewGeom); |
730 | 0 | } |
731 | 0 | } |
732 | | |
733 | 0 | ++m_nTotalFeatures; |
734 | |
|
735 | 0 | json_object *poObj = OGRGeoJSONWriteFeature( |
736 | 0 | poFeatureToWrite.get() ? poFeatureToWrite.get() : poFeature, |
737 | 0 | m_oWriteOptions); |
738 | 0 | CPLAssert(nullptr != poObj); |
739 | | |
740 | 0 | const char *pszJson = json_object_to_json_string(poObj); |
741 | |
|
742 | 0 | char chEOL = '\n'; |
743 | 0 | OGRErr eErr = OGRERR_NONE; |
744 | 0 | if ((m_poDS->m_bIsRSSeparated && |
745 | 0 | VSIFWriteL(&RS, 1, 1, m_poDS->m_fp) != 1) || |
746 | 0 | VSIFWriteL(pszJson, strlen(pszJson), 1, m_poDS->m_fp) != 1 || |
747 | 0 | VSIFWriteL(&chEOL, 1, 1, m_poDS->m_fp) != 1) |
748 | 0 | { |
749 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Cannot write feature"); |
750 | 0 | eErr = OGRERR_FAILURE; |
751 | 0 | } |
752 | |
|
753 | 0 | json_object_put(poObj); |
754 | |
|
755 | 0 | return eErr; |
756 | 0 | } |
757 | | |
758 | | /************************************************************************/ |
759 | | /* CreateField() */ |
760 | | /************************************************************************/ |
761 | | |
762 | | OGRErr OGRGeoJSONSeqLayer::CreateField(const OGRFieldDefn *poField, |
763 | | int /* bApproxOK */) |
764 | 0 | { |
765 | 0 | if (m_poDS->GetAccess() != GA_Update) |
766 | 0 | return OGRERR_FAILURE; |
767 | 0 | m_poFeatureDefn->AddFieldDefn(poField); |
768 | 0 | return OGRERR_NONE; |
769 | 0 | } |
770 | | |
771 | | /************************************************************************/ |
772 | | /* Open() */ |
773 | | /************************************************************************/ |
774 | | |
775 | | bool OGRGeoJSONSeqDataSource::Open(GDALOpenInfo *poOpenInfo, |
776 | | GeoJSONSourceType nSrcType) |
777 | 0 | { |
778 | 0 | CPLAssert(nullptr == m_fp); |
779 | | |
780 | 0 | CPLString osLayerName("GeoJSONSeq"); |
781 | |
|
782 | 0 | const char *pszUnprefixedFilename = poOpenInfo->pszFilename; |
783 | 0 | if (STARTS_WITH_CI(poOpenInfo->pszFilename, "GeoJSONSeq:")) |
784 | 0 | { |
785 | 0 | pszUnprefixedFilename = poOpenInfo->pszFilename + strlen("GeoJSONSeq:"); |
786 | 0 | } |
787 | |
|
788 | 0 | if (nSrcType == eGeoJSONSourceFile) |
789 | 0 | { |
790 | 0 | if (pszUnprefixedFilename != poOpenInfo->pszFilename) |
791 | 0 | { |
792 | 0 | osLayerName = CPLGetBasenameSafe(pszUnprefixedFilename); |
793 | 0 | m_fp = VSIFOpenL(pszUnprefixedFilename, |
794 | 0 | poOpenInfo->eAccess == GA_Update ? "rb+" : "rb"); |
795 | 0 | } |
796 | 0 | else |
797 | 0 | { |
798 | 0 | osLayerName = CPLGetBasenameSafe(poOpenInfo->pszFilename); |
799 | 0 | std::swap(m_fp, poOpenInfo->fpL); |
800 | 0 | } |
801 | 0 | } |
802 | 0 | else if (nSrcType == eGeoJSONSourceText) |
803 | 0 | { |
804 | 0 | if (poOpenInfo->eAccess == GA_Update) |
805 | 0 | return false; |
806 | | |
807 | 0 | m_osTmpFile = VSIMemGenerateHiddenFilename("geojsonseq"); |
808 | 0 | m_fp = VSIFileFromMemBuffer( |
809 | 0 | m_osTmpFile.c_str(), |
810 | 0 | reinterpret_cast<GByte *>(CPLStrdup(poOpenInfo->pszFilename)), |
811 | 0 | strlen(poOpenInfo->pszFilename), true); |
812 | 0 | } |
813 | 0 | else if (nSrcType == eGeoJSONSourceService) |
814 | 0 | { |
815 | 0 | if (poOpenInfo->eAccess == GA_Update) |
816 | 0 | return false; |
817 | | |
818 | 0 | char *pszStoredContent = |
819 | 0 | OGRGeoJSONDriverStealStoredContent(pszUnprefixedFilename); |
820 | 0 | if (pszStoredContent) |
821 | 0 | { |
822 | 0 | if (EQUAL(pszStoredContent, INVALID_CONTENT_FOR_JSON_LIKE) || |
823 | 0 | !GeoJSONSeqIsObject(pszStoredContent, poOpenInfo)) |
824 | 0 | { |
825 | 0 | OGRGeoJSONDriverStoreContent(poOpenInfo->pszFilename, |
826 | 0 | pszStoredContent); |
827 | 0 | return false; |
828 | 0 | } |
829 | 0 | else |
830 | 0 | { |
831 | 0 | m_osTmpFile = VSIMemGenerateHiddenFilename("geojsonseq"); |
832 | 0 | m_fp = VSIFileFromMemBuffer( |
833 | 0 | m_osTmpFile.c_str(), |
834 | 0 | reinterpret_cast<GByte *>(pszStoredContent), |
835 | 0 | strlen(pszStoredContent), true); |
836 | 0 | } |
837 | 0 | } |
838 | 0 | else |
839 | 0 | { |
840 | 0 | CPLHTTPResult *pResult = |
841 | 0 | GeoJSONHTTPFetchWithContentTypeHeader(pszUnprefixedFilename); |
842 | 0 | if (!pResult) |
843 | 0 | { |
844 | 0 | return FALSE; |
845 | 0 | } |
846 | | |
847 | 0 | m_osTmpFile = VSIMemGenerateHiddenFilename("geojsonseq"); |
848 | 0 | m_fp = VSIFileFromMemBuffer(m_osTmpFile.c_str(), pResult->pabyData, |
849 | 0 | pResult->nDataLen, true); |
850 | 0 | pResult->pabyData = nullptr; |
851 | 0 | pResult->nDataLen = 0; |
852 | 0 | CPLHTTPDestroyResult(pResult); |
853 | 0 | } |
854 | 0 | } |
855 | 0 | if (m_fp == nullptr) |
856 | 0 | { |
857 | 0 | return false; |
858 | 0 | } |
859 | 0 | SetDescription(poOpenInfo->pszFilename); |
860 | 0 | auto poLayer = new OGRGeoJSONSeqLayer(this, osLayerName.c_str()); |
861 | 0 | const bool bLooseIdentification = |
862 | 0 | nSrcType == eGeoJSONSourceService && |
863 | 0 | !STARTS_WITH_CI(poOpenInfo->pszFilename, "GeoJSONSeq:"); |
864 | 0 | if (bLooseIdentification) |
865 | 0 | { |
866 | 0 | CPLPushErrorHandler(CPLQuietErrorHandler); |
867 | 0 | } |
868 | 0 | const bool bEstablishLayerDefn = poOpenInfo->eAccess != GA_Update; |
869 | 0 | auto ret = poLayer->Init(bLooseIdentification, bEstablishLayerDefn); |
870 | 0 | if (bLooseIdentification) |
871 | 0 | { |
872 | 0 | CPLPopErrorHandler(); |
873 | 0 | CPLErrorReset(); |
874 | 0 | } |
875 | 0 | if (!ret) |
876 | 0 | { |
877 | 0 | delete poLayer; |
878 | 0 | return false; |
879 | 0 | } |
880 | 0 | m_apoLayers.emplace_back(std::move(poLayer)); |
881 | 0 | eAccess = poOpenInfo->eAccess; |
882 | 0 | return true; |
883 | 0 | } |
884 | | |
885 | | /************************************************************************/ |
886 | | /* Create() */ |
887 | | /************************************************************************/ |
888 | | |
889 | | bool OGRGeoJSONSeqDataSource::Create(const char *pszName, |
890 | | char ** /* papszOptions */) |
891 | 0 | { |
892 | 0 | CPLAssert(nullptr == m_fp); |
893 | | |
894 | 0 | if (strcmp(pszName, "/dev/stdout") == 0) |
895 | 0 | pszName = "/vsistdout/"; |
896 | | |
897 | | /* -------------------------------------------------------------------- */ |
898 | | /* Create the output file. */ |
899 | | /* -------------------------------------------------------------------- */ |
900 | 0 | m_bSupportsRead = |
901 | 0 | VSIFileManager::GetHandler(pszName)->SupportsRead(pszName) && |
902 | 0 | VSIFileManager::GetHandler(pszName)->SupportsRandomWrite(pszName, |
903 | 0 | false); |
904 | 0 | m_bAtEOF = !m_bSupportsRead; |
905 | 0 | m_fp = VSIFOpenExL(pszName, m_bSupportsRead ? "wb+" : "wb", true); |
906 | 0 | if (nullptr == m_fp) |
907 | 0 | { |
908 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, "Failed to create %s: %s", |
909 | 0 | pszName, VSIGetLastErrorMsg()); |
910 | 0 | return false; |
911 | 0 | } |
912 | | |
913 | 0 | eAccess = GA_Update; |
914 | |
|
915 | 0 | m_bIsRSSeparated = EQUAL(CPLGetExtensionSafe(pszName).c_str(), "GEOJSONS"); |
916 | |
|
917 | 0 | return true; |
918 | 0 | } |
919 | | |
920 | | /************************************************************************/ |
921 | | /* OGRGeoJSONSeqDriverIdentify() */ |
922 | | /************************************************************************/ |
923 | | |
924 | | static int OGRGeoJSONSeqDriverIdentifyInternal(GDALOpenInfo *poOpenInfo, |
925 | | GeoJSONSourceType &nSrcType) |
926 | 0 | { |
927 | 0 | nSrcType = GeoJSONSeqGetSourceType(poOpenInfo); |
928 | 0 | if (nSrcType == eGeoJSONSourceUnknown) |
929 | 0 | return FALSE; |
930 | 0 | if (nSrcType == eGeoJSONSourceService) |
931 | 0 | { |
932 | 0 | if (poOpenInfo->IsSingleAllowedDriver("GeoJSONSeq")) |
933 | 0 | return TRUE; |
934 | 0 | if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "GeoJSONSeq:")) |
935 | 0 | { |
936 | 0 | return -1; |
937 | 0 | } |
938 | 0 | } |
939 | 0 | return TRUE; |
940 | 0 | } |
941 | | |
942 | | /************************************************************************/ |
943 | | /* OGRGeoJSONSeqDriverIdentify() */ |
944 | | /************************************************************************/ |
945 | | |
946 | | static int OGRGeoJSONSeqDriverIdentify(GDALOpenInfo *poOpenInfo) |
947 | 0 | { |
948 | 0 | GeoJSONSourceType nSrcType; |
949 | 0 | return OGRGeoJSONSeqDriverIdentifyInternal(poOpenInfo, nSrcType); |
950 | 0 | } |
951 | | |
952 | | /************************************************************************/ |
953 | | /* Open() */ |
954 | | /************************************************************************/ |
955 | | |
956 | | static GDALDataset *OGRGeoJSONSeqDriverOpen(GDALOpenInfo *poOpenInfo) |
957 | 0 | { |
958 | 0 | GeoJSONSourceType nSrcType; |
959 | 0 | if (OGRGeoJSONSeqDriverIdentifyInternal(poOpenInfo, nSrcType) == FALSE) |
960 | 0 | { |
961 | 0 | return nullptr; |
962 | 0 | } |
963 | | |
964 | 0 | OGRGeoJSONSeqDataSource *poDS = new OGRGeoJSONSeqDataSource(); |
965 | |
|
966 | 0 | if (!poDS->Open(poOpenInfo, nSrcType)) |
967 | 0 | { |
968 | 0 | delete poDS; |
969 | 0 | poDS = nullptr; |
970 | 0 | } |
971 | |
|
972 | 0 | return poDS; |
973 | 0 | } |
974 | | |
975 | | /************************************************************************/ |
976 | | /* Create() */ |
977 | | /************************************************************************/ |
978 | | |
979 | | static GDALDataset * |
980 | | OGRGeoJSONSeqDriverCreate(const char *pszName, int /* nBands */, |
981 | | int /* nXSize */, int /* nYSize */, |
982 | | GDALDataType /* eDT */, char **papszOptions) |
983 | 0 | { |
984 | 0 | OGRGeoJSONSeqDataSource *poDS = new OGRGeoJSONSeqDataSource(); |
985 | |
|
986 | 0 | if (!poDS->Create(pszName, papszOptions)) |
987 | 0 | { |
988 | 0 | delete poDS; |
989 | 0 | poDS = nullptr; |
990 | 0 | } |
991 | |
|
992 | 0 | return poDS; |
993 | 0 | } |
994 | | |
995 | | /************************************************************************/ |
996 | | /* RegisterOGRGeoJSONSeq() */ |
997 | | /************************************************************************/ |
998 | | |
999 | | void RegisterOGRGeoJSONSeq() |
1000 | 0 | { |
1001 | 0 | if (GDALGetDriverByName("GeoJSONSeq") != nullptr) |
1002 | 0 | return; |
1003 | | |
1004 | 0 | GDALDriver *poDriver = new GDALDriver(); |
1005 | |
|
1006 | 0 | poDriver->SetDescription("GeoJSONSeq"); |
1007 | 0 | poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES"); |
1008 | 0 | poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES"); |
1009 | 0 | poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES"); |
1010 | 0 | poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES"); |
1011 | 0 | poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "GeoJSON Sequence"); |
1012 | 0 | poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "geojsonl geojsons"); |
1013 | 0 | poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, |
1014 | 0 | "drivers/vector/geojsonseq.html"); |
1015 | |
|
1016 | 0 | poDriver->SetMetadataItem( |
1017 | 0 | GDAL_DS_LAYER_CREATIONOPTIONLIST, |
1018 | 0 | "<LayerCreationOptionList>" |
1019 | 0 | " <Option name='RS' type='boolean' description='whether to prefix " |
1020 | 0 | "records with RS=0x1e character' default='NO'/>" |
1021 | 0 | " <Option name='COORDINATE_PRECISION' type='int' description='Number " |
1022 | 0 | "of decimal for coordinates. Default is 7'/>" |
1023 | 0 | " <Option name='SIGNIFICANT_FIGURES' type='int' description='Number " |
1024 | 0 | "of significant figures for floating-point values' default='17'/>" |
1025 | 0 | " <Option name='ID_FIELD' type='string' description='Name of the " |
1026 | 0 | "source field that must be used as the id member of Feature features'/>" |
1027 | 0 | " <Option name='ID_TYPE' type='string-select' description='Type of " |
1028 | 0 | "the id member of Feature features'>" |
1029 | 0 | " <Value>AUTO</Value>" |
1030 | 0 | " <Value>String</Value>" |
1031 | 0 | " <Value>Integer</Value>" |
1032 | 0 | " </Option>" |
1033 | 0 | " <Option name='WRITE_BBOX' type='boolean' description='whether to " |
1034 | 0 | "write a bbox property with the bounding box of each geometry' " |
1035 | 0 | "default='NO'/>" |
1036 | 0 | "</LayerCreationOptionList>"); |
1037 | |
|
1038 | 0 | poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); |
1039 | 0 | poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES, |
1040 | 0 | "Integer Integer64 Real String IntegerList " |
1041 | 0 | "Integer64List RealList StringList"); |
1042 | 0 | poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES, "Boolean"); |
1043 | 0 | poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE"); |
1044 | 0 | poDriver->SetMetadataItem(GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION, "YES"); |
1045 | |
|
1046 | 0 | poDriver->pfnOpen = OGRGeoJSONSeqDriverOpen; |
1047 | 0 | poDriver->pfnIdentify = OGRGeoJSONSeqDriverIdentify; |
1048 | 0 | poDriver->pfnCreate = OGRGeoJSONSeqDriverCreate; |
1049 | |
|
1050 | 0 | GetGDALDriverManager()->RegisterDriver(poDriver); |
1051 | 0 | } |