Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Arrow generic code
4 : * Purpose: Arrow generic code
5 : * Author: Even Rouault, <even.rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2022, Planet Labs
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #ifndef OGARROWWRITERLAYER_HPP_INCLUDED
14 : #define OGARROWWRITERLAYER_HPP_INCLUDED
15 :
16 : #include "ogr_arrow.h"
17 :
18 : #include "cpl_json.h"
19 : #include "cpl_time.h"
20 :
21 : #include "ogrlayerarrow.h"
22 : #include "ogr_wkb.h"
23 :
24 : #include <array>
25 : #include <cinttypes>
26 : #include <limits>
27 :
28 : static constexpr int TZFLAG_UNINITIALIZED = -1;
29 :
30 : #define OGR_ARROW_RETURN_NOT_OK(status, ret_value) \
31 : do \
32 : { \
33 : if (!(status).ok()) \
34 : { \
35 : CPLError(CE_Failure, CPLE_AppDefined, "%s failed", \
36 : (status).message().c_str()); \
37 : return (ret_value); \
38 : } \
39 : } while (false)
40 :
41 : #define OGR_ARROW_RETURN_FALSE_NOT_OK(status) \
42 : OGR_ARROW_RETURN_NOT_OK(status, false)
43 :
44 : #define OGR_ARROW_RETURN_OGRERR_NOT_OK(status) \
45 : OGR_ARROW_RETURN_NOT_OK(status, OGRERR_FAILURE)
46 :
47 : #define OGR_ARROW_PROPAGATE_OGRERR(ret_value) \
48 : do \
49 : { \
50 : if ((ret_value) != OGRERR_NONE) \
51 : return OGRERR_FAILURE; \
52 : } while (0)
53 :
54 : /************************************************************************/
55 : /* OGRArrowWriterLayer() */
56 : /************************************************************************/
57 :
58 516 : inline OGRArrowWriterLayer::OGRArrowWriterLayer(
59 : arrow::MemoryPool *poMemoryPool,
60 : const std::shared_ptr<arrow::io::OutputStream> &poOutputStream,
61 516 : const char *pszLayerName)
62 516 : : m_poMemoryPool(poMemoryPool), m_poOutputStream(poOutputStream)
63 : {
64 516 : m_poFeatureDefn = new OGRFeatureDefn(pszLayerName);
65 516 : m_poFeatureDefn->SetGeomType(wkbNone);
66 516 : m_poFeatureDefn->Reference();
67 516 : SetDescription(pszLayerName);
68 516 : }
69 :
70 : /************************************************************************/
71 : /* ~OGRArrowWriterLayer() */
72 : /************************************************************************/
73 :
74 516 : inline OGRArrowWriterLayer::~OGRArrowWriterLayer()
75 : {
76 516 : CPLDebug("ARROW", "Memory pool (writer layer): bytes_allocated = %" PRId64,
77 516 : m_poMemoryPool->bytes_allocated());
78 516 : CPLDebug("ARROW", "Memory pool (writer layer): max_memory = %" PRId64,
79 516 : m_poMemoryPool->max_memory());
80 :
81 516 : m_poFeatureDefn->Release();
82 516 : }
83 :
84 : /************************************************************************/
85 : /* FinalizeWriting() */
86 : /************************************************************************/
87 :
88 504 : inline bool OGRArrowWriterLayer::FinalizeWriting()
89 : {
90 504 : bool ret = true;
91 :
92 504 : if (!IsFileWriterCreated())
93 : {
94 363 : CreateWriter();
95 : }
96 504 : if (IsFileWriterCreated())
97 : {
98 504 : PerformStepsBeforeFinalFlushGroup();
99 :
100 504 : if (!m_apoBuilders.empty() && m_apoFieldsFromArrowSchema.empty())
101 314 : ret = FlushGroup();
102 :
103 504 : if (!CloseFileWriter())
104 0 : ret = false;
105 : }
106 :
107 504 : return ret;
108 : }
109 :
110 : /************************************************************************/
111 : /* RemoveIDFromMemberOfEnsembles() */
112 : /************************************************************************/
113 :
114 : /* static */
115 : inline void
116 704 : OGRArrowWriterLayer::RemoveIDFromMemberOfEnsembles(CPLJSONObject &obj)
117 : {
118 : // Remove "id" from members of datum ensembles for compatibility with
119 : // older PROJ versions
120 : // Cf https://github.com/opengeospatial/geoparquet/discussions/110
121 : // and https://github.com/OSGeo/PROJ/pull/3221
122 704 : if (obj.GetType() == CPLJSONObject::Type::Object)
123 : {
124 898 : for (auto &subObj : obj.GetChildren())
125 : {
126 684 : RemoveIDFromMemberOfEnsembles(subObj);
127 : }
128 : }
129 542 : else if (obj.GetType() == CPLJSONObject::Type::Array &&
130 542 : obj.GetName() == "members")
131 : {
132 0 : for (auto &subObj : obj.ToArray())
133 : {
134 0 : subObj.Delete("id");
135 : }
136 : }
137 704 : }
138 :
139 : /************************************************************************/
140 : /* IdentifyCRS() */
141 : /************************************************************************/
142 :
143 : /* static */
144 : inline OGRSpatialReference
145 41 : OGRArrowWriterLayer::IdentifyCRS(const OGRSpatialReference *poSRS)
146 : {
147 41 : OGRSpatialReference oSRSIdentified(*poSRS);
148 :
149 41 : if (poSRS->GetAuthorityName(nullptr) == nullptr)
150 : {
151 : // Try to find a registered CRS that matches the input one
152 4 : int nEntries = 0;
153 4 : int *panConfidence = nullptr;
154 : OGRSpatialReferenceH *pahSRS =
155 4 : poSRS->FindMatches(nullptr, &nEntries, &panConfidence);
156 :
157 : // If there are several matches >= 90%, take the only one
158 : // that is EPSG
159 4 : int iOtherAuthority = -1;
160 4 : int iEPSG = -1;
161 4 : const char *const apszOptions[] = {
162 : "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES", nullptr};
163 4 : int iConfidenceBestMatch = -1;
164 6 : for (int iSRS = 0; iSRS < nEntries; iSRS++)
165 : {
166 4 : auto poCandidateCRS = OGRSpatialReference::FromHandle(pahSRS[iSRS]);
167 4 : if (panConfidence[iSRS] < iConfidenceBestMatch ||
168 4 : panConfidence[iSRS] < 70)
169 : {
170 : break;
171 : }
172 3 : if (poSRS->IsSame(poCandidateCRS, apszOptions))
173 : {
174 : const char *pszAuthName =
175 3 : poCandidateCRS->GetAuthorityName(nullptr);
176 3 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "EPSG"))
177 : {
178 2 : iOtherAuthority = -2;
179 2 : if (iEPSG < 0)
180 : {
181 2 : iConfidenceBestMatch = panConfidence[iSRS];
182 2 : iEPSG = iSRS;
183 : }
184 : else
185 : {
186 0 : iEPSG = -1;
187 0 : break;
188 : }
189 : }
190 1 : else if (iEPSG < 0 && pszAuthName != nullptr)
191 : {
192 1 : if (EQUAL(pszAuthName, "OGC"))
193 : {
194 : const char *pszAuthCode =
195 1 : poCandidateCRS->GetAuthorityCode(nullptr);
196 1 : if (pszAuthCode && EQUAL(pszAuthCode, "CRS84"))
197 : {
198 1 : iOtherAuthority = iSRS;
199 1 : break;
200 : }
201 : }
202 0 : else if (iOtherAuthority == -1)
203 : {
204 0 : iConfidenceBestMatch = panConfidence[iSRS];
205 0 : iOtherAuthority = iSRS;
206 : }
207 : else
208 0 : iOtherAuthority = -2;
209 : }
210 : }
211 : }
212 4 : if (iEPSG >= 0)
213 : {
214 2 : oSRSIdentified = *OGRSpatialReference::FromHandle(pahSRS[iEPSG]);
215 : }
216 2 : else if (iOtherAuthority >= 0)
217 : {
218 : oSRSIdentified =
219 1 : *OGRSpatialReference::FromHandle(pahSRS[iOtherAuthority]);
220 : }
221 4 : OSRFreeSRSArray(pahSRS);
222 4 : CPLFree(panConfidence);
223 : }
224 :
225 41 : return oSRSIdentified;
226 : }
227 :
228 : /************************************************************************/
229 : /* CreateSchemaCommon() */
230 : /************************************************************************/
231 :
232 504 : inline void OGRArrowWriterLayer::CreateSchemaCommon()
233 : {
234 504 : CPLAssert(static_cast<int>(m_aeGeomEncoding.size()) ==
235 : m_poFeatureDefn->GetGeomFieldCount());
236 :
237 1008 : std::vector<std::shared_ptr<arrow::Field>> fields;
238 504 : bool bNeedGDALSchema = false;
239 :
240 504 : m_anTZFlag.resize(m_poFeatureDefn->GetFieldCount(), TZFLAG_UNINITIALIZED);
241 :
242 504 : if (!m_osFIDColumn.empty())
243 : {
244 21 : bNeedGDALSchema = true;
245 21 : fields.emplace_back(arrow::field(m_osFIDColumn, arrow::int64(), false));
246 : }
247 :
248 504 : if (!m_apoFieldsFromArrowSchema.empty())
249 : {
250 129 : fields.insert(fields.end(), m_apoFieldsFromArrowSchema.begin(),
251 258 : m_apoFieldsFromArrowSchema.end());
252 : }
253 :
254 1272 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i)
255 : {
256 768 : const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
257 768 : std::shared_ptr<arrow::DataType> dt;
258 768 : const auto eDT = poFieldDefn->GetType();
259 768 : const auto eSubDT = poFieldDefn->GetSubType();
260 768 : const auto &osDomainName = poFieldDefn->GetDomainName();
261 768 : const OGRFieldDomain *poFieldDomain = nullptr;
262 768 : const int nWidth = poFieldDefn->GetWidth();
263 768 : if (!osDomainName.empty())
264 : {
265 4 : const auto oIter = m_oMapFieldDomains.find(osDomainName);
266 4 : if (oIter == m_oMapFieldDomains.end())
267 : {
268 0 : CPLError(CE_Warning, CPLE_AppDefined,
269 : "Field %s references domain %s, but the later one "
270 : "has not been created",
271 : poFieldDefn->GetNameRef(), osDomainName.c_str());
272 : }
273 : else
274 : {
275 4 : poFieldDomain = oIter->second.get();
276 : }
277 : }
278 768 : const char *pszFieldMetadata = nullptr;
279 768 : switch (eDT)
280 : {
281 81 : case OFTInteger:
282 81 : if (eSubDT == OFSTBoolean)
283 4 : dt = arrow::boolean();
284 77 : else if (eSubDT == OFSTInt16)
285 4 : dt = arrow::int16();
286 : else
287 73 : dt = arrow::int32();
288 81 : if (poFieldDomain != nullptr)
289 : {
290 4 : dt = arrow::dictionary(dt, arrow::utf8());
291 : }
292 81 : break;
293 :
294 51 : case OFTInteger64:
295 51 : dt = arrow::int64();
296 51 : if (poFieldDomain != nullptr)
297 : {
298 0 : dt = arrow::dictionary(dt, arrow::utf8());
299 : }
300 51 : break;
301 :
302 68 : case OFTReal:
303 : {
304 68 : const int nPrecision = poFieldDefn->GetPrecision();
305 68 : if (nWidth != 0 && nPrecision != 0)
306 : {
307 : // Since arrow 18.0, we could use arrow::smallest_decimal()
308 : // to return the smallest representation (i.e. possibly
309 : // decimal32 and decimal64). But for now keep decimal128
310 : // as the minimum for backwards compatibility.
311 : // GetValueDecimal() and other functions in
312 : // ogrlayerarrow.cpp would have to be adapted for decimal32
313 : // and decimal64 compatibility.
314 11 : if (nWidth > 38)
315 0 : dt = arrow::decimal256(nWidth, nPrecision);
316 : else
317 11 : dt = arrow::decimal128(nWidth, nPrecision);
318 : }
319 57 : else if (eSubDT == OFSTFloat32)
320 7 : dt = arrow::float32();
321 : else
322 50 : dt = arrow::float64();
323 68 : break;
324 : }
325 :
326 343 : case OFTString:
327 : case OFTWideString:
328 343 : if ((eSubDT != OFSTNone && eSubDT != OFSTJSON) || nWidth > 0)
329 3 : bNeedGDALSchema = true;
330 343 : if (eSubDT == OFSTJSON)
331 83 : pszFieldMetadata = EXTENSION_NAME_ARROW_JSON;
332 343 : dt = arrow::utf8();
333 343 : break;
334 :
335 19 : case OFTBinary:
336 19 : if (nWidth != 0)
337 4 : dt = arrow::fixed_size_binary(nWidth);
338 : else
339 15 : dt = arrow::binary();
340 19 : break;
341 :
342 48 : case OFTIntegerList:
343 48 : if (eSubDT == OFSTBoolean)
344 8 : dt = arrow::list(arrow::boolean());
345 40 : else if (eSubDT == OFSTInt16)
346 0 : dt = arrow::list(arrow::int16());
347 : else
348 40 : dt = arrow::list(arrow::int32());
349 48 : break;
350 :
351 20 : case OFTInteger64List:
352 20 : dt = arrow::list(arrow::int64());
353 20 : break;
354 :
355 35 : case OFTRealList:
356 35 : if (eSubDT == OFSTFloat32)
357 11 : dt = arrow::list(arrow::float32());
358 : else
359 24 : dt = arrow::list(arrow::float64());
360 35 : break;
361 :
362 12 : case OFTStringList:
363 : case OFTWideStringList:
364 12 : dt = arrow::list(arrow::utf8());
365 12 : break;
366 :
367 31 : case OFTDate:
368 31 : dt = arrow::date32();
369 31 : break;
370 :
371 8 : case OFTTime:
372 8 : dt = arrow::time32(arrow::TimeUnit::MILLI);
373 8 : break;
374 :
375 52 : case OFTDateTime:
376 : {
377 52 : const int nTZFlag = poFieldDefn->GetTZFlag();
378 : const char *pszTIMESTAMP_WITH_OFFSET =
379 52 : m_aosCreationOptions.FetchNameValueDef(
380 : "TIMESTAMP_WITH_OFFSET", "AUTO");
381 52 : if ((nTZFlag == OGR_TZFLAG_MIXED_TZ &&
382 5 : !EQUAL(pszTIMESTAMP_WITH_OFFSET, "NO")) ||
383 48 : EQUAL(pszTIMESTAMP_WITH_OFFSET, "YES"))
384 : {
385 4 : m_anTZFlag[i] = nTZFlag;
386 : std::vector<std::shared_ptr<arrow::Field>>
387 : tsWithOffsetFields{
388 : arrow::field(
389 : ATSWO_TIMESTAMP_FIELD_NAME,
390 8 : arrow::timestamp(arrow::TimeUnit::MILLI, "UTC"),
391 : false),
392 : arrow::field(ATSWO_OFFSET_MINUTES_FIELD_NAME,
393 24 : arrow::int16(), false)};
394 4 : dt = arrow::struct_(std::move(tsWithOffsetFields));
395 4 : pszFieldMetadata =
396 4 : EXTENSION_NAME_ARROW_TIMESTAMP_WITH_OFFSET;
397 : }
398 : else
399 : {
400 48 : if (nTZFlag >= OGR_TZFLAG_MIXED_TZ)
401 : {
402 13 : m_anTZFlag[i] = nTZFlag;
403 : }
404 48 : dt = arrow::timestamp(arrow::TimeUnit::MILLI);
405 : }
406 52 : break;
407 : }
408 : }
409 :
410 768 : auto field = arrow::field(poFieldDefn->GetNameRef(), std::move(dt),
411 2304 : CPL_TO_BOOL(poFieldDefn->IsNullable()));
412 768 : if (pszFieldMetadata)
413 : {
414 87 : auto kvMetadata = std::make_shared<arrow::KeyValueMetadata>();
415 87 : kvMetadata->Append(ARROW_EXTENSION_NAME_KEY, pszFieldMetadata);
416 87 : field = field->WithMetadata(kvMetadata);
417 : }
418 :
419 768 : fields.emplace_back(std::move(field));
420 768 : if (poFieldDefn->GetAlternativeNameRef()[0])
421 2 : bNeedGDALSchema = true;
422 768 : if (!poFieldDefn->GetComment().empty())
423 3 : bNeedGDALSchema = true;
424 : }
425 :
426 999 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
427 : {
428 495 : const auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(i);
429 495 : const auto eGType = poGeomFieldDefn->GetType();
430 : const int nDim =
431 495 : 2 + (OGR_GT_HasZ(eGType) ? 1 : 0) + (OGR_GT_HasM(eGType) ? 1 : 0);
432 :
433 495 : const bool pointFieldNullable = GetDriverUCName() == "PARQUET";
434 :
435 : // Fixed Size List GeoArrow encoding
436 : const auto getFixedSizeListOfPoint =
437 228 : [nDim, eGType, pointFieldNullable]()
438 : {
439 : return arrow::fixed_size_list(
440 146 : arrow::field(nDim == 2 ? "xy"
441 30 : : nDim == 3 ? (OGR_GT_HasZ(eGType) ? "xyz" : "xym")
442 : : "xyzm",
443 : arrow::float64(), pointFieldNullable),
444 116 : nDim);
445 495 : };
446 :
447 : // Struct GeoArrow encoding
448 1485 : auto xField(arrow::field("x", arrow::float64(), false));
449 1485 : auto yField(arrow::field("y", arrow::float64(), false));
450 : std::vector<std::shared_ptr<arrow::Field>> pointFields{
451 : arrow::field("x", arrow::float64(), false),
452 2970 : arrow::field("y", arrow::float64(), false)};
453 495 : if (OGR_GT_HasZ(eGType))
454 : pointFields.emplace_back(
455 145 : arrow::field("z", arrow::float64(), false));
456 495 : if (OGR_GT_HasM(eGType))
457 : pointFields.emplace_back(
458 52 : arrow::field("m", arrow::float64(), false));
459 990 : auto pointStructType(arrow::struct_(std::move(pointFields)));
460 :
461 40 : const auto getListOfVertices = [&getFixedSizeListOfPoint]()
462 : {
463 80 : return arrow::list(std::make_shared<arrow::Field>(
464 120 : "vertices", getFixedSizeListOfPoint()));
465 495 : };
466 :
467 22 : const auto getListOfRings = [&getListOfVertices]()
468 : {
469 : return arrow::list(
470 44 : std::make_shared<arrow::Field>("rings", getListOfVertices()));
471 495 : };
472 :
473 146 : const auto getListOfVerticesStruct = [&pointStructType]()
474 : {
475 : return arrow::list(
476 292 : std::make_shared<arrow::Field>("vertices", pointStructType));
477 495 : };
478 :
479 86 : const auto getListOfRingsStruct = [&getListOfVerticesStruct]()
480 : {
481 172 : return arrow::list(std::make_shared<arrow::Field>(
482 258 : "rings", getListOfVerticesStruct()));
483 495 : };
484 :
485 495 : std::shared_ptr<arrow::DataType> dt;
486 495 : switch (m_aeGeomEncoding[i])
487 : {
488 169 : case OGRArrowGeomEncoding::WKB:
489 : #if ARROW_VERSION_MAJOR >= 21
490 : if (m_bUseArrowWKBExtension)
491 : {
492 : CPLJSONDocument oMetadataDoc;
493 :
494 : const auto poSRS = poGeomFieldDefn->GetSpatialRef();
495 : if (poSRS)
496 : {
497 : OGRSpatialReference oSRSIdentified(IdentifyCRS(poSRS));
498 :
499 : // CRS encoded as PROJJSON
500 : char *pszPROJJSON = nullptr;
501 : oSRSIdentified.exportToPROJJSON(&pszPROJJSON, nullptr);
502 : CPLJSONDocument oCRSDoc;
503 : CPL_IGNORE_RET_VAL(oCRSDoc.LoadMemory(pszPROJJSON));
504 : CPLFree(pszPROJJSON);
505 : CPLJSONObject oCRSRoot = oCRSDoc.GetRoot();
506 : RemoveIDFromMemberOfEnsembles(oCRSRoot);
507 :
508 : oMetadataDoc.GetRoot().Add("crs", oCRSRoot);
509 : }
510 : else
511 : {
512 : oMetadataDoc.GetRoot().Add("crs", "srid:0");
513 : }
514 :
515 : if (m_bEdgesSpherical)
516 : {
517 : oMetadataDoc.GetRoot().Add("edges", "spherical");
518 : }
519 :
520 : const std::string metadata = oMetadataDoc.GetRoot().Format(
521 : CPLJSONObject::PrettyFormat::Plain);
522 : dt = std::make_shared<OGRGeoArrowWkbExtensionType>(
523 : arrow::binary(), metadata);
524 : }
525 : else
526 : #endif
527 : {
528 169 : dt = arrow::binary();
529 : }
530 169 : break;
531 :
532 53 : case OGRArrowGeomEncoding::WKT:
533 53 : dt = arrow::utf8();
534 53 : break;
535 :
536 0 : case OGRArrowGeomEncoding::GEOARROW_FSL_GENERIC:
537 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_GENERIC:
538 0 : CPLAssert(false);
539 : break;
540 :
541 9 : case OGRArrowGeomEncoding::GEOARROW_FSL_POINT:
542 9 : dt = getFixedSizeListOfPoint();
543 9 : break;
544 :
545 9 : case OGRArrowGeomEncoding::GEOARROW_FSL_LINESTRING:
546 9 : dt = getListOfVertices();
547 9 : break;
548 :
549 11 : case OGRArrowGeomEncoding::GEOARROW_FSL_POLYGON:
550 11 : dt = getListOfRings();
551 11 : break;
552 :
553 9 : case OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOINT:
554 18 : dt = arrow::list(std::make_shared<arrow::Field>(
555 27 : "points", getFixedSizeListOfPoint()));
556 9 : break;
557 :
558 9 : case OGRArrowGeomEncoding::GEOARROW_FSL_MULTILINESTRING:
559 18 : dt = arrow::list(std::make_shared<arrow::Field>(
560 27 : "linestrings", getListOfVertices()));
561 9 : break;
562 :
563 11 : case OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOLYGON:
564 22 : dt = arrow::list(std::make_shared<arrow::Field>(
565 33 : "polygons", getListOfRings()));
566 11 : break;
567 :
568 39 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_POINT:
569 39 : dt = pointStructType;
570 39 : break;
571 :
572 30 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_LINESTRING:
573 30 : dt = getListOfVerticesStruct();
574 30 : break;
575 :
576 44 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_POLYGON:
577 44 : dt = getListOfRingsStruct();
578 44 : break;
579 :
580 30 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOINT:
581 60 : dt = arrow::list(
582 90 : std::make_shared<arrow::Field>("points", pointStructType));
583 30 : break;
584 :
585 30 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTILINESTRING:
586 60 : dt = arrow::list(std::make_shared<arrow::Field>(
587 90 : "linestrings", getListOfVerticesStruct()));
588 30 : break;
589 :
590 42 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOLYGON:
591 84 : dt = arrow::list(std::make_shared<arrow::Field>(
592 126 : "polygons", getListOfRingsStruct()));
593 42 : break;
594 : }
595 :
596 : std::shared_ptr<arrow::Field> field(
597 495 : arrow::field(poGeomFieldDefn->GetNameRef(), std::move(dt),
598 1485 : CPL_TO_BOOL(poGeomFieldDefn->IsNullable())));
599 495 : if (m_bWriteFieldArrowExtensionName)
600 : {
601 138 : auto kvMetadata = field->metadata()
602 138 : ? field->metadata()->Copy()
603 138 : : std::make_shared<arrow::KeyValueMetadata>();
604 276 : kvMetadata->Append(
605 : ARROW_EXTENSION_NAME_KEY,
606 138 : GetGeomEncodingAsString(m_aeGeomEncoding[i], false));
607 138 : field = field->WithMetadata(kvMetadata);
608 : }
609 :
610 495 : m_apoBaseStructGeomType.emplace_back(std::move(pointStructType));
611 :
612 495 : fields.emplace_back(std::move(field));
613 : }
614 :
615 504 : if (m_bWriteBBoxStruct)
616 : {
617 579 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
618 : {
619 288 : const auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(i);
620 864 : auto bbox_field_xmin(arrow::field("xmin", arrow::float32(), false));
621 864 : auto bbox_field_ymin(arrow::field("ymin", arrow::float32(), false));
622 864 : auto bbox_field_xmax(arrow::field("xmax", arrow::float32(), false));
623 864 : auto bbox_field_ymax(arrow::field("ymax", arrow::float32(), false));
624 : auto bbox_field(arrow::field(
625 288 : m_oBBoxStructFieldName.empty()
626 963 : ? std::string(poGeomFieldDefn->GetNameRef()).append("_bbox")
627 : : m_oBBoxStructFieldName,
628 1728 : arrow::struct_(
629 288 : {std::move(bbox_field_xmin), std::move(bbox_field_ymin),
630 1728 : std::move(bbox_field_xmax), std::move(bbox_field_ymax)}),
631 1602 : CPL_TO_BOOL(poGeomFieldDefn->IsNullable())));
632 288 : fields.emplace_back(bbox_field);
633 288 : m_apoFieldsBBOX.emplace_back(bbox_field);
634 : }
635 : }
636 :
637 504 : m_aoEnvelopes.resize(m_poFeatureDefn->GetGeomFieldCount());
638 504 : m_oSetWrittenGeometryTypes.resize(m_poFeatureDefn->GetGeomFieldCount());
639 :
640 504 : m_poSchema = arrow::schema(std::move(fields));
641 504 : CPLAssert(m_poSchema);
642 531 : if (bNeedGDALSchema &&
643 27 : CPLTestBool(CPLGetConfigOption(
644 531 : ("OGR_" + GetDriverUCName() + "_WRITE_GDAL_SCHEMA").c_str(),
645 : "YES")))
646 : {
647 54 : CPLJSONObject oRoot;
648 54 : CPLJSONObject oColumns;
649 :
650 27 : if (!m_osFIDColumn.empty())
651 21 : oRoot.Add("fid", m_osFIDColumn);
652 :
653 27 : oRoot.Add("columns", oColumns);
654 221 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i)
655 : {
656 194 : const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
657 388 : CPLJSONObject oColumn;
658 194 : oColumns.Add(poFieldDefn->GetNameRef(), oColumn);
659 194 : oColumn.Add("type", OGR_GetFieldTypeName(poFieldDefn->GetType()));
660 194 : const auto eSubDT = poFieldDefn->GetSubType();
661 194 : if (eSubDT != OFSTNone)
662 56 : oColumn.Add("subtype", OGR_GetFieldSubTypeName(eSubDT));
663 194 : const int nWidth = poFieldDefn->GetWidth();
664 194 : if (nWidth > 0)
665 15 : oColumn.Add("width", nWidth);
666 194 : const int nPrecision = poFieldDefn->GetPrecision();
667 194 : if (nPrecision > 0)
668 7 : oColumn.Add("precision", nPrecision);
669 194 : if (poFieldDefn->GetAlternativeNameRef()[0])
670 2 : oColumn.Add("alternative_name",
671 : poFieldDefn->GetAlternativeNameRef());
672 194 : if (!poFieldDefn->GetComment().empty())
673 3 : oColumn.Add("comment", poFieldDefn->GetComment());
674 : }
675 :
676 27 : auto kvMetadata = m_poSchema->metadata()
677 0 : ? m_poSchema->metadata()->Copy()
678 54 : : std::make_shared<arrow::KeyValueMetadata>();
679 54 : kvMetadata->Append("gdal:schema",
680 54 : oRoot.Format(CPLJSONObject::PrettyFormat::Plain));
681 27 : m_poSchema = m_poSchema->WithMetadata(kvMetadata);
682 27 : CPLAssert(m_poSchema);
683 : }
684 504 : }
685 :
686 : /************************************************************************/
687 : /* FinalizeSchema() */
688 : /************************************************************************/
689 :
690 446 : inline void OGRArrowWriterLayer::FinalizeSchema()
691 : {
692 : // Final tuning of schema taking into actual timezone values
693 : // from features
694 446 : int nArrowIdxFirstField = !m_osFIDColumn.empty() ? 1 : 0;
695 1210 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i)
696 : {
697 764 : const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
698 778 : if (m_anTZFlag[i] >= OGR_TZFLAG_MIXED_TZ &&
699 778 : poFieldDefn->GetTZFlag() != OGR_TZFLAG_MIXED_TZ &&
700 12 : m_poSchema->field(nArrowIdxFirstField + i)->type()->id() !=
701 : arrow::Type::STRUCT)
702 : {
703 12 : const int nOffset = m_anTZFlag[i] == OGR_TZFLAG_MIXED_TZ
704 12 : ? 0
705 12 : : (m_anTZFlag[i] - OGR_TZFLAG_UTC) * 15;
706 12 : int nHours = static_cast<int>(nOffset / 60); // Round towards zero.
707 12 : const int nMinutes = std::abs(nOffset - nHours * 60);
708 :
709 : const std::string osTZ =
710 : CPLSPrintf("%c%02d:%02d", nOffset >= 0 ? '+' : '-',
711 24 : std::abs(nHours), nMinutes);
712 24 : auto dt = arrow::timestamp(arrow::TimeUnit::MILLI, osTZ);
713 12 : auto field = arrow::field(poFieldDefn->GetNameRef(), std::move(dt),
714 36 : CPL_TO_BOOL(poFieldDefn->IsNullable()));
715 24 : auto result = m_poSchema->SetField(nArrowIdxFirstField + i, field);
716 12 : if (!result.ok())
717 : {
718 0 : CPLError(CE_Warning, CPLE_AppDefined,
719 : "Schema::SetField() failed with %s",
720 0 : result.status().message().c_str());
721 : }
722 : else
723 : {
724 12 : m_poSchema = *result;
725 : }
726 : }
727 : }
728 446 : }
729 :
730 : /************************************************************************/
731 : /* AddFieldDomain() */
732 : /************************************************************************/
733 :
734 : inline bool
735 11 : OGRArrowWriterLayer::AddFieldDomain(std::unique_ptr<OGRFieldDomain> &&domain,
736 : std::string &failureReason)
737 : {
738 11 : if (domain->GetDomainType() != OFDT_CODED)
739 : {
740 0 : failureReason = "Only coded field domains are supported by Arrow";
741 0 : return false;
742 : }
743 :
744 : const OGRCodedFieldDomain *poDomain =
745 11 : static_cast<const OGRCodedFieldDomain *>(domain.get());
746 11 : const OGRCodedValue *psIter = poDomain->GetEnumeration();
747 :
748 : auto poStringBuilder =
749 22 : std::make_shared<arrow::StringBuilder>(m_poMemoryPool);
750 :
751 11 : int nLastCode = -1;
752 44 : for (; psIter->pszCode; ++psIter)
753 : {
754 33 : if (CPLGetValueType(psIter->pszCode) != CPL_VALUE_INTEGER)
755 : {
756 0 : failureReason = "Non integer code in domain ";
757 0 : failureReason += domain->GetName();
758 0 : return false;
759 : }
760 33 : int nCode = atoi(psIter->pszCode);
761 33 : if (nCode <= nLastCode || nCode - nLastCode > 100)
762 : {
763 0 : failureReason = "Too sparse codes in domain ";
764 0 : failureReason += domain->GetName();
765 0 : return false;
766 : }
767 33 : for (int i = nLastCode + 1; i < nCode; ++i)
768 : {
769 0 : OGR_ARROW_RETURN_FALSE_NOT_OK(poStringBuilder->AppendNull());
770 : }
771 33 : if (psIter->pszValue)
772 33 : OGR_ARROW_RETURN_FALSE_NOT_OK(
773 : poStringBuilder->Append(psIter->pszValue));
774 : else
775 0 : OGR_ARROW_RETURN_FALSE_NOT_OK(poStringBuilder->AppendNull());
776 33 : nLastCode = nCode;
777 : }
778 :
779 11 : std::shared_ptr<arrow::Array> stringArray;
780 22 : auto status = poStringBuilder->Finish(&stringArray);
781 11 : if (!status.ok())
782 : {
783 0 : CPLError(CE_Failure, CPLE_AppDefined,
784 : "StringArray::Finish() failed with %s",
785 0 : status.message().c_str());
786 0 : return false;
787 : }
788 :
789 11 : m_oMapFieldDomainToStringArray[domain->GetName()] = std::move(stringArray);
790 11 : m_oMapFieldDomains[domain->GetName()] = std::move(domain);
791 11 : return true;
792 : }
793 :
794 : /************************************************************************/
795 : /* GetFieldDomainNames() */
796 : /************************************************************************/
797 :
798 0 : inline std::vector<std::string> OGRArrowWriterLayer::GetFieldDomainNames() const
799 : {
800 0 : std::vector<std::string> names;
801 0 : names.reserve(m_oMapFieldDomains.size());
802 0 : for (const auto &it : m_oMapFieldDomains)
803 : {
804 0 : names.emplace_back(it.first);
805 : }
806 0 : return names;
807 : }
808 :
809 : /************************************************************************/
810 : /* GetFieldDomain() */
811 : /************************************************************************/
812 :
813 : inline const OGRFieldDomain *
814 15 : OGRArrowWriterLayer::GetFieldDomain(const std::string &name) const
815 : {
816 15 : const auto iter = m_oMapFieldDomains.find(name);
817 15 : if (iter == m_oMapFieldDomains.end())
818 11 : return nullptr;
819 4 : return iter->second.get();
820 : }
821 :
822 : /************************************************************************/
823 : /* CreateField() */
824 : /************************************************************************/
825 :
826 769 : inline OGRErr OGRArrowWriterLayer::CreateField(const OGRFieldDefn *poField,
827 : int /* bApproxOK */)
828 : {
829 769 : if (m_poSchema)
830 : {
831 1 : CPLError(CE_Failure, CPLE_NotSupported,
832 : "Cannot add field after a first feature has been written");
833 1 : return OGRERR_FAILURE;
834 : }
835 768 : if (!m_apoFieldsFromArrowSchema.empty())
836 : {
837 0 : CPLError(CE_Failure, CPLE_NotSupported,
838 : "Cannot mix calls to CreateField() and "
839 : "CreateFieldFromArrowSchema()");
840 0 : return OGRERR_FAILURE;
841 : }
842 768 : m_poFeatureDefn->AddFieldDefn(poField);
843 768 : return OGRERR_NONE;
844 : }
845 :
846 : /************************************************************************/
847 : /* OGRLayer::CreateFieldFromArrowSchema() */
848 : /************************************************************************/
849 :
850 1081 : inline bool OGRArrowWriterLayer::CreateFieldFromArrowSchema(
851 : const struct ArrowSchema *schema, CSLConstList /*papszOptions*/)
852 : {
853 1081 : if (m_poSchema)
854 : {
855 0 : CPLError(CE_Failure, CPLE_NotSupported,
856 : "Cannot add field after a first feature has been written");
857 0 : return false;
858 : }
859 :
860 1081 : if (m_poFeatureDefn->GetFieldCount())
861 : {
862 0 : CPLError(CE_Failure, CPLE_NotSupported,
863 : "Cannot mix calls to CreateField() and "
864 : "CreateFieldFromArrowSchema()");
865 0 : return false;
866 : }
867 :
868 1081 : if (m_osFIDColumn == schema->name)
869 : {
870 0 : CPLError(CE_Failure, CPLE_AppDefined,
871 : "FID column has the same name as this field: %s",
872 0 : schema->name);
873 0 : return false;
874 : }
875 :
876 36422 : for (auto &apoField : m_apoFieldsFromArrowSchema)
877 : {
878 35341 : if (apoField->name() == schema->name)
879 : {
880 0 : CPLError(CE_Failure, CPLE_AppDefined,
881 0 : "Field of name %s already exists", schema->name);
882 0 : return false;
883 : }
884 : }
885 :
886 1081 : if (m_poFeatureDefn->GetGeomFieldIndex(schema->name) >= 0)
887 : {
888 0 : CPLError(CE_Failure, CPLE_AppDefined,
889 0 : "Geometry field of name %s already exists", schema->name);
890 0 : return false;
891 : }
892 :
893 : // ImportField() would release the schema, but we don't want that
894 : // So copy the structure content into a local variable, and override its
895 : // release callback to a no-op. This may be a bit fragile, but it doesn't
896 : // look like ImportField implementation tries to access the C ArrowSchema
897 : // after it has been called.
898 1081 : struct ArrowSchema lSchema = *schema;
899 1081 : const auto DummyFreeSchema = [](struct ArrowSchema *ptrSchema)
900 1081 : { ptrSchema->release = nullptr; };
901 1081 : lSchema.release = DummyFreeSchema;
902 2162 : auto result = arrow::ImportField(&lSchema);
903 1081 : CPLAssert(lSchema.release == nullptr);
904 1081 : if (!result.ok())
905 : {
906 0 : CPLError(CE_Failure, CPLE_AppDefined,
907 : "CreateFieldFromArrowSchema() failed: %s",
908 0 : result.status().message().c_str());
909 0 : return false;
910 : }
911 1081 : m_apoFieldsFromArrowSchema.emplace_back(std::move(*result));
912 1081 : return true;
913 : }
914 :
915 : /************************************************************************/
916 : /* GetPreciseArrowGeomEncoding() */
917 : /************************************************************************/
918 :
919 275 : inline OGRArrowGeomEncoding OGRArrowWriterLayer::GetPreciseArrowGeomEncoding(
920 : OGRArrowGeomEncoding eEncodingType, OGRwkbGeometryType eGType)
921 : {
922 275 : CPLAssert(eEncodingType == OGRArrowGeomEncoding::GEOARROW_FSL_GENERIC ||
923 : eEncodingType == OGRArrowGeomEncoding::GEOARROW_STRUCT_GENERIC);
924 275 : const auto eFlatType = wkbFlatten(eGType);
925 275 : if (eFlatType == wkbPoint)
926 : {
927 : return eEncodingType == OGRArrowGeomEncoding::GEOARROW_FSL_GENERIC
928 48 : ? OGRArrowGeomEncoding::GEOARROW_FSL_POINT
929 48 : : OGRArrowGeomEncoding::GEOARROW_STRUCT_POINT;
930 : }
931 227 : else if (eFlatType == wkbLineString)
932 : {
933 : return eEncodingType == OGRArrowGeomEncoding::GEOARROW_FSL_GENERIC
934 39 : ? OGRArrowGeomEncoding::GEOARROW_FSL_LINESTRING
935 39 : : OGRArrowGeomEncoding::GEOARROW_STRUCT_LINESTRING;
936 : }
937 188 : else if (eFlatType == wkbPolygon)
938 : {
939 : return eEncodingType == OGRArrowGeomEncoding::GEOARROW_FSL_GENERIC
940 55 : ? OGRArrowGeomEncoding::GEOARROW_FSL_POLYGON
941 55 : : OGRArrowGeomEncoding::GEOARROW_STRUCT_POLYGON;
942 : }
943 133 : else if (eFlatType == wkbMultiPoint)
944 : {
945 : return eEncodingType == OGRArrowGeomEncoding::GEOARROW_FSL_GENERIC
946 39 : ? OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOINT
947 39 : : OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOINT;
948 : }
949 94 : else if (eFlatType == wkbMultiLineString)
950 : {
951 : return eEncodingType == OGRArrowGeomEncoding::GEOARROW_FSL_GENERIC
952 39 : ? OGRArrowGeomEncoding::GEOARROW_FSL_MULTILINESTRING
953 39 : : OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTILINESTRING;
954 : }
955 55 : else if (eFlatType == wkbMultiPolygon)
956 : {
957 : return eEncodingType == OGRArrowGeomEncoding::GEOARROW_FSL_GENERIC
958 53 : ? OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOLYGON
959 53 : : OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOLYGON;
960 : }
961 : else
962 : {
963 2 : CPLError(CE_Failure, CPLE_NotSupported,
964 : "GeoArrow encoding is currently not supported for %s",
965 : OGRGeometryTypeToName(eGType));
966 2 : return eEncodingType;
967 : }
968 : }
969 :
970 : /************************************************************************/
971 : /* GetGeomEncodingAsString() */
972 : /************************************************************************/
973 :
974 : inline const char *
975 747 : OGRArrowWriterLayer::GetGeomEncodingAsString(OGRArrowGeomEncoding eGeomEncoding,
976 : bool bForParquetGeo)
977 : {
978 747 : switch (eGeomEncoding)
979 : {
980 216 : case OGRArrowGeomEncoding::WKB:
981 216 : return bForParquetGeo ? "WKB" : "geoarrow.wkb";
982 111 : case OGRArrowGeomEncoding::WKT:
983 111 : return bForParquetGeo ? "WKT" : "geoarrow.wkt";
984 0 : case OGRArrowGeomEncoding::GEOARROW_FSL_GENERIC:
985 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_GENERIC:
986 0 : CPLAssert(false);
987 : break;
988 19 : case OGRArrowGeomEncoding::GEOARROW_FSL_POINT:
989 19 : return "geoarrow.point";
990 19 : case OGRArrowGeomEncoding::GEOARROW_FSL_LINESTRING:
991 19 : return "geoarrow.linestring";
992 21 : case OGRArrowGeomEncoding::GEOARROW_FSL_POLYGON:
993 21 : return "geoarrow.polygon";
994 19 : case OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOINT:
995 19 : return "geoarrow.multipoint";
996 19 : case OGRArrowGeomEncoding::GEOARROW_FSL_MULTILINESTRING:
997 19 : return "geoarrow.multilinestring";
998 21 : case OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOLYGON:
999 21 : return "geoarrow.multipolygon";
1000 62 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_POINT:
1001 62 : return bForParquetGeo ? "point" : "geoarrow.point";
1002 42 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_LINESTRING:
1003 42 : return bForParquetGeo ? "linestring" : "geoarrow.linestring";
1004 60 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_POLYGON:
1005 60 : return bForParquetGeo ? "polygon" : "geoarrow.polygon";
1006 42 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOINT:
1007 42 : return bForParquetGeo ? "multipoint" : "geoarrow.multipoint";
1008 42 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTILINESTRING:
1009 42 : return bForParquetGeo ? "multilinestring"
1010 42 : : "geoarrow.multilinestring";
1011 54 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOLYGON:
1012 54 : return bForParquetGeo ? "multipolygon" : "geoarrow.multipolygon";
1013 : }
1014 0 : return nullptr;
1015 : }
1016 :
1017 : /************************************************************************/
1018 : /* CreateGeomField() */
1019 : /************************************************************************/
1020 :
1021 : inline OGRErr
1022 27 : OGRArrowWriterLayer::CreateGeomField(const OGRGeomFieldDefn *poField,
1023 : int /* bApproxOK */)
1024 : {
1025 27 : if (m_poSchema)
1026 : {
1027 1 : CPLError(CE_Failure, CPLE_NotSupported,
1028 : "Cannot add field after a first feature has been written");
1029 1 : return OGRERR_FAILURE;
1030 : }
1031 26 : const auto eGType = poField->GetType();
1032 26 : if (!IsSupportedGeometryType(eGType))
1033 : {
1034 0 : return OGRERR_FAILURE;
1035 : }
1036 :
1037 26 : if (IsSRSRequired() && poField->GetSpatialRef() == nullptr)
1038 : {
1039 0 : CPLError(CE_Warning, CPLE_AppDefined,
1040 : "Geometry column should have an associated CRS");
1041 : }
1042 26 : auto eGeomEncoding = m_eGeomEncoding;
1043 26 : if (eGeomEncoding == OGRArrowGeomEncoding::GEOARROW_FSL_GENERIC ||
1044 26 : eGeomEncoding == OGRArrowGeomEncoding::GEOARROW_STRUCT_GENERIC)
1045 : {
1046 0 : const auto eEncodingType = eGeomEncoding;
1047 0 : eGeomEncoding = GetPreciseArrowGeomEncoding(eEncodingType, eGType);
1048 0 : if (eGeomEncoding == eEncodingType)
1049 0 : return OGRERR_FAILURE;
1050 : }
1051 26 : m_aeGeomEncoding.push_back(eGeomEncoding);
1052 26 : m_poFeatureDefn->AddGeomFieldDefn(poField);
1053 26 : return OGRERR_NONE;
1054 : }
1055 :
1056 : /************************************************************************/
1057 : /* MakeGeoArrowBuilder() */
1058 : /************************************************************************/
1059 :
1060 : static std::shared_ptr<arrow::ArrayBuilder>
1061 134 : MakeGeoArrowBuilder(arrow::MemoryPool *poMemoryPool, int nDim, int nDepth)
1062 : {
1063 134 : if (nDepth == 0)
1064 104 : return std::make_shared<arrow::FixedSizeListBuilder>(
1065 104 : poMemoryPool, std::make_shared<arrow::DoubleBuilder>(poMemoryPool),
1066 52 : nDim);
1067 : else
1068 164 : return std::make_shared<arrow::ListBuilder>(
1069 246 : poMemoryPool, MakeGeoArrowBuilder(poMemoryPool, nDim, nDepth - 1));
1070 : }
1071 :
1072 : /************************************************************************/
1073 : /* MakeGeoArrowStructBuilder() */
1074 : /************************************************************************/
1075 :
1076 : static std::shared_ptr<arrow::ArrayBuilder>
1077 538 : MakeGeoArrowStructBuilder(arrow::MemoryPool *poMemoryPool, int nDim, int nDepth,
1078 : const std::shared_ptr<arrow::DataType> &eBaseType)
1079 : {
1080 538 : if (nDepth == 0)
1081 : {
1082 213 : std::vector<std::shared_ptr<arrow::ArrayBuilder>> builders;
1083 735 : for (int i = 0; i < nDim; ++i)
1084 : builders.emplace_back(
1085 522 : std::make_shared<arrow::DoubleBuilder>(poMemoryPool));
1086 426 : return std::make_shared<arrow::StructBuilder>(eBaseType, poMemoryPool,
1087 426 : std::move(builders));
1088 : }
1089 : else
1090 650 : return std::make_shared<arrow::ListBuilder>(
1091 650 : poMemoryPool, MakeGeoArrowStructBuilder(poMemoryPool, nDim,
1092 325 : nDepth - 1, eBaseType));
1093 : }
1094 :
1095 : /************************************************************************/
1096 : /* ClearArrayBuilers() */
1097 : /************************************************************************/
1098 :
1099 355 : inline void OGRArrowWriterLayer::ClearArrayBuilers()
1100 : {
1101 355 : m_apoBuilders.clear();
1102 355 : m_apoBuildersBBOXStruct.clear();
1103 355 : m_apoBuildersBBOXXMin.clear();
1104 355 : m_apoBuildersBBOXYMin.clear();
1105 355 : m_apoBuildersBBOXXMax.clear();
1106 355 : m_apoBuildersBBOXYMax.clear();
1107 355 : }
1108 :
1109 : /************************************************************************/
1110 : /* CreateArrayBuilders() */
1111 : /************************************************************************/
1112 :
1113 484 : inline void OGRArrowWriterLayer::CreateArrayBuilders()
1114 : {
1115 484 : m_apoBuilders.reserve(1 + m_poFeatureDefn->GetFieldCount() +
1116 484 : m_poFeatureDefn->GetGeomFieldCount());
1117 :
1118 484 : int nArrowIdx = 0;
1119 484 : if (!m_osFIDColumn.empty())
1120 : {
1121 51 : m_apoBuilders.emplace_back(std::make_shared<arrow::Int64Builder>());
1122 51 : nArrowIdx++;
1123 : }
1124 :
1125 1966 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i, ++nArrowIdx)
1126 : {
1127 1482 : const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
1128 1482 : std::shared_ptr<arrow::ArrayBuilder> builder;
1129 1482 : const auto eSubDT = poFieldDefn->GetSubType();
1130 1482 : switch (poFieldDefn->GetType())
1131 : {
1132 162 : case OFTInteger:
1133 162 : if (eSubDT == OFSTBoolean)
1134 : builder =
1135 12 : std::make_shared<arrow::BooleanBuilder>(m_poMemoryPool);
1136 150 : else if (eSubDT == OFSTInt16)
1137 : builder =
1138 12 : std::make_shared<arrow::Int16Builder>(m_poMemoryPool);
1139 : else
1140 : builder =
1141 138 : std::make_shared<arrow::Int32Builder>(m_poMemoryPool);
1142 162 : break;
1143 :
1144 99 : case OFTInteger64:
1145 99 : builder = std::make_shared<arrow::Int64Builder>(m_poMemoryPool);
1146 99 : break;
1147 :
1148 122 : case OFTReal:
1149 : {
1150 244 : const auto arrowType = m_poSchema->fields()[nArrowIdx]->type();
1151 122 : if (arrowType->id() == arrow::Type::DECIMAL128)
1152 27 : builder = std::make_shared<arrow::Decimal128Builder>(
1153 27 : arrowType, m_poMemoryPool);
1154 95 : else if (arrowType->id() == arrow::Type::DECIMAL256)
1155 0 : builder = std::make_shared<arrow::Decimal256Builder>(
1156 0 : arrowType, m_poMemoryPool);
1157 95 : else if (eSubDT == OFSTFloat32)
1158 : builder =
1159 21 : std::make_shared<arrow::FloatBuilder>(m_poMemoryPool);
1160 : else
1161 : builder =
1162 74 : std::make_shared<arrow::DoubleBuilder>(m_poMemoryPool);
1163 122 : break;
1164 : }
1165 :
1166 540 : case OFTString:
1167 : case OFTWideString:
1168 : builder =
1169 540 : std::make_shared<arrow::StringBuilder>(m_poMemoryPool);
1170 540 : break;
1171 :
1172 43 : case OFTBinary:
1173 43 : if (poFieldDefn->GetWidth() != 0)
1174 24 : builder = std::make_shared<arrow::FixedSizeBinaryBuilder>(
1175 24 : arrow::fixed_size_binary(poFieldDefn->GetWidth()),
1176 24 : m_poMemoryPool);
1177 : else
1178 : builder =
1179 31 : std::make_shared<arrow::BinaryBuilder>(m_poMemoryPool);
1180 43 : break;
1181 :
1182 144 : case OFTIntegerList:
1183 : {
1184 144 : std::shared_ptr<arrow::ArrayBuilder> poBaseBuilder;
1185 144 : if (eSubDT == OFSTBoolean)
1186 : poBaseBuilder =
1187 24 : std::make_shared<arrow::BooleanBuilder>(m_poMemoryPool);
1188 120 : else if (eSubDT == OFSTInt16)
1189 : poBaseBuilder =
1190 0 : std::make_shared<arrow::Int16Builder>(m_poMemoryPool);
1191 : else
1192 : poBaseBuilder =
1193 120 : std::make_shared<arrow::Int32Builder>(m_poMemoryPool);
1194 288 : builder = std::make_shared<arrow::ListBuilder>(m_poMemoryPool,
1195 144 : poBaseBuilder);
1196 144 : break;
1197 : }
1198 :
1199 60 : case OFTInteger64List:
1200 60 : builder = std::make_shared<arrow::ListBuilder>(
1201 60 : m_poMemoryPool,
1202 180 : std::make_shared<arrow::Int64Builder>(m_poMemoryPool));
1203 :
1204 60 : break;
1205 :
1206 105 : case OFTRealList:
1207 105 : if (eSubDT == OFSTFloat32)
1208 33 : builder = std::make_shared<arrow::ListBuilder>(
1209 33 : m_poMemoryPool,
1210 99 : std::make_shared<arrow::FloatBuilder>(m_poMemoryPool));
1211 : else
1212 72 : builder = std::make_shared<arrow::ListBuilder>(
1213 72 : m_poMemoryPool,
1214 216 : std::make_shared<arrow::DoubleBuilder>(m_poMemoryPool));
1215 105 : break;
1216 :
1217 36 : case OFTStringList:
1218 : case OFTWideStringList:
1219 36 : builder = std::make_shared<arrow::ListBuilder>(
1220 36 : m_poMemoryPool,
1221 108 : std::make_shared<arrow::StringBuilder>(m_poMemoryPool));
1222 :
1223 36 : break;
1224 :
1225 47 : case OFTDate:
1226 : builder =
1227 47 : std::make_shared<arrow::Date32Builder>(m_poMemoryPool);
1228 47 : break;
1229 :
1230 24 : case OFTTime:
1231 48 : builder = std::make_shared<arrow::Time32Builder>(
1232 72 : arrow::time32(arrow::TimeUnit::MILLI), m_poMemoryPool);
1233 24 : break;
1234 :
1235 100 : case OFTDateTime:
1236 : {
1237 200 : const auto arrowType = m_poSchema->fields()[nArrowIdx]->type();
1238 100 : if (arrowType->id() == arrow::Type::STRUCT)
1239 : {
1240 4 : builder = std::make_shared<arrow::StructBuilder>(
1241 4 : arrowType, m_poMemoryPool,
1242 20 : std::vector<std::shared_ptr<arrow::ArrayBuilder>>{
1243 4 : std::make_shared<arrow::TimestampBuilder>(
1244 4 : arrow::timestamp(arrow::TimeUnit::MILLI),
1245 4 : m_poMemoryPool),
1246 4 : std::make_shared<arrow::Int16Builder>(
1247 16 : m_poMemoryPool)});
1248 : }
1249 : else
1250 : {
1251 192 : builder = std::make_shared<arrow::TimestampBuilder>(
1252 96 : arrow::timestamp(arrow::TimeUnit::MILLI),
1253 192 : m_poMemoryPool);
1254 : }
1255 100 : break;
1256 : }
1257 : }
1258 1482 : m_apoBuilders.emplace_back(builder);
1259 : }
1260 :
1261 959 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i, ++nArrowIdx)
1262 : {
1263 475 : std::shared_ptr<arrow::ArrayBuilder> builder;
1264 475 : const auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(i);
1265 475 : const auto eGType = poGeomFieldDefn->GetType();
1266 : const int nDim =
1267 475 : 2 + (OGR_GT_HasZ(eGType) ? 1 : 0) + (OGR_GT_HasM(eGType) ? 1 : 0);
1268 :
1269 475 : switch (m_aeGeomEncoding[i])
1270 : {
1271 157 : case OGRArrowGeomEncoding::WKB:
1272 : builder =
1273 157 : std::make_shared<arrow::BinaryBuilder>(m_poMemoryPool);
1274 157 : break;
1275 :
1276 53 : case OGRArrowGeomEncoding::WKT:
1277 : builder =
1278 53 : std::make_shared<arrow::StringBuilder>(m_poMemoryPool);
1279 53 : break;
1280 :
1281 8 : case OGRArrowGeomEncoding::GEOARROW_FSL_POINT:
1282 8 : builder = MakeGeoArrowBuilder(m_poMemoryPool, nDim, 0);
1283 8 : break;
1284 :
1285 8 : case OGRArrowGeomEncoding::GEOARROW_FSL_LINESTRING:
1286 8 : builder = MakeGeoArrowBuilder(m_poMemoryPool, nDim, 1);
1287 8 : break;
1288 :
1289 10 : case OGRArrowGeomEncoding::GEOARROW_FSL_POLYGON:
1290 10 : builder = MakeGeoArrowBuilder(m_poMemoryPool, nDim, 2);
1291 10 : break;
1292 :
1293 8 : case OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOINT:
1294 8 : builder = MakeGeoArrowBuilder(m_poMemoryPool, nDim, 1);
1295 8 : break;
1296 :
1297 8 : case OGRArrowGeomEncoding::GEOARROW_FSL_MULTILINESTRING:
1298 8 : builder = MakeGeoArrowBuilder(m_poMemoryPool, nDim, 2);
1299 8 : break;
1300 :
1301 10 : case OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOLYGON:
1302 10 : builder = MakeGeoArrowBuilder(m_poMemoryPool, nDim, 3);
1303 10 : break;
1304 :
1305 42 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_POINT:
1306 84 : builder = MakeGeoArrowStructBuilder(m_poMemoryPool, nDim, 0,
1307 84 : m_apoBaseStructGeomType[i]);
1308 42 : break;
1309 :
1310 29 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_LINESTRING:
1311 58 : builder = MakeGeoArrowStructBuilder(m_poMemoryPool, nDim, 1,
1312 58 : m_apoBaseStructGeomType[i]);
1313 29 : break;
1314 :
1315 43 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_POLYGON:
1316 86 : builder = MakeGeoArrowStructBuilder(m_poMemoryPool, nDim, 2,
1317 86 : m_apoBaseStructGeomType[i]);
1318 43 : break;
1319 :
1320 29 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOINT:
1321 58 : builder = MakeGeoArrowStructBuilder(m_poMemoryPool, nDim, 1,
1322 58 : m_apoBaseStructGeomType[i]);
1323 29 : break;
1324 :
1325 29 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTILINESTRING:
1326 58 : builder = MakeGeoArrowStructBuilder(m_poMemoryPool, nDim, 2,
1327 58 : m_apoBaseStructGeomType[i]);
1328 29 : break;
1329 :
1330 41 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOLYGON:
1331 82 : builder = MakeGeoArrowStructBuilder(m_poMemoryPool, nDim, 3,
1332 82 : m_apoBaseStructGeomType[i]);
1333 41 : break;
1334 :
1335 0 : case OGRArrowGeomEncoding::GEOARROW_FSL_GENERIC:
1336 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_GENERIC:
1337 0 : CPLAssert(false);
1338 : break;
1339 : }
1340 :
1341 475 : m_apoBuilders.emplace_back(builder);
1342 :
1343 475 : if (m_bWriteBBoxStruct)
1344 : {
1345 : m_apoBuildersBBOXXMin.emplace_back(
1346 276 : std::make_shared<arrow::FloatBuilder>(m_poMemoryPool));
1347 : m_apoBuildersBBOXYMin.emplace_back(
1348 276 : std::make_shared<arrow::FloatBuilder>(m_poMemoryPool));
1349 : m_apoBuildersBBOXXMax.emplace_back(
1350 276 : std::make_shared<arrow::FloatBuilder>(m_poMemoryPool));
1351 : m_apoBuildersBBOXYMax.emplace_back(
1352 276 : std::make_shared<arrow::FloatBuilder>(m_poMemoryPool));
1353 : m_apoBuildersBBOXStruct.emplace_back(
1354 552 : std::make_shared<arrow::StructBuilder>(
1355 276 : m_apoFieldsBBOX[i]->type(), m_poMemoryPool,
1356 2484 : std::vector<std::shared_ptr<arrow::ArrayBuilder>>{
1357 276 : m_apoBuildersBBOXXMin.back(),
1358 276 : m_apoBuildersBBOXYMin.back(),
1359 276 : m_apoBuildersBBOXXMax.back(),
1360 1932 : m_apoBuildersBBOXYMax.back()}));
1361 : }
1362 : }
1363 484 : }
1364 :
1365 : /************************************************************************/
1366 : /* castToFloatDown() */
1367 : /************************************************************************/
1368 :
1369 : // Cf https://github.com/sqlite/sqlite/blob/90e4a3b7fcdf63035d6f35eb44d11ff58ff4b068/ext/rtree/rtree.c#L2993C1-L2995C3
1370 : /*
1371 : ** Rounding constants for float->double conversion.
1372 : */
1373 : #define RNDTOWARDS (1.0 - 1.0 / 8388608.0) /* Round towards zero */
1374 : #define RNDAWAY (1.0 + 1.0 / 8388608.0) /* Round away from zero */
1375 :
1376 : /*
1377 : ** Convert an sqlite3_value into an RtreeValue (presumably a float)
1378 : ** while taking care to round toward negative or positive, respectively.
1379 : */
1380 3836 : static float castToFloatDown(double d)
1381 : {
1382 3836 : float f = static_cast<float>(d);
1383 3836 : if (f > d)
1384 : {
1385 22 : f = static_cast<float>(d * (d < 0 ? RNDAWAY : RNDTOWARDS));
1386 : }
1387 3836 : return f;
1388 : }
1389 :
1390 3836 : static float castToFloatUp(double d)
1391 : {
1392 3836 : float f = static_cast<float>(d);
1393 3836 : if (f < d)
1394 : {
1395 12 : f = static_cast<float>(d * (d < 0 ? RNDTOWARDS : RNDAWAY));
1396 : }
1397 3836 : return f;
1398 : }
1399 :
1400 : /************************************************************************/
1401 : /* GeoArrowLineBuilder() */
1402 : /************************************************************************/
1403 :
1404 : template <class PointBuilderType>
1405 676 : static OGRErr GeoArrowLineBuilder(const OGRLineString *poLS,
1406 : PointBuilderType *poPointBuilder,
1407 : arrow::DoubleBuilder *poXBuilder,
1408 : arrow::DoubleBuilder *poYBuilder,
1409 : arrow::DoubleBuilder *poZBuilder,
1410 : arrow::DoubleBuilder *poMBuilder)
1411 : {
1412 3076 : for (int j = 0; j < poLS->getNumPoints(); ++j)
1413 : {
1414 2400 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poPointBuilder->Append());
1415 2400 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poXBuilder->Append(poLS->getX(j)));
1416 2400 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poYBuilder->Append(poLS->getY(j)));
1417 2400 : if (poZBuilder)
1418 668 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poZBuilder->Append(poLS->getZ(j)));
1419 2400 : if (poMBuilder)
1420 220 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poMBuilder->Append(poLS->getM(j)));
1421 : }
1422 676 : return OGRERR_NONE;
1423 : }
1424 :
1425 : /************************************************************************/
1426 : /* BuildGeometry() */
1427 : /************************************************************************/
1428 :
1429 3892 : inline OGRErr OGRArrowWriterLayer::BuildGeometry(OGRGeometry *poGeom,
1430 : int iGeomField,
1431 : arrow::ArrayBuilder *poBuilder)
1432 : {
1433 3892 : const auto eGType = poGeom ? poGeom->getGeometryType() : wkbNone;
1434 : const auto eColumnGType =
1435 3892 : m_poFeatureDefn->GetGeomFieldDefn(iGeomField)->GetType();
1436 3892 : const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eColumnGType));
1437 3892 : const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eColumnGType));
1438 3892 : const bool bIsEmpty = poGeom != nullptr && poGeom->IsEmpty();
1439 3892 : OGREnvelope3D oEnvelope;
1440 3892 : if (poGeom != nullptr && !bIsEmpty)
1441 : {
1442 2258 : if (poGeom->Is3D())
1443 : {
1444 322 : poGeom->getEnvelope(&oEnvelope);
1445 322 : m_aoEnvelopes[iGeomField].Merge(oEnvelope);
1446 : }
1447 : else
1448 : {
1449 1936 : poGeom->getEnvelope(static_cast<OGREnvelope *>(&oEnvelope));
1450 1936 : m_aoEnvelopes[iGeomField].Merge(oEnvelope);
1451 : }
1452 2258 : m_oSetWrittenGeometryTypes[iGeomField].insert(eGType);
1453 : }
1454 :
1455 3892 : if (m_bWriteBBoxStruct)
1456 : {
1457 3185 : if (poGeom && !bIsEmpty)
1458 : {
1459 1867 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1460 : m_apoBuildersBBOXXMin[iGeomField]->Append(
1461 : castToFloatDown(oEnvelope.MinX)));
1462 1867 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1463 : m_apoBuildersBBOXYMin[iGeomField]->Append(
1464 : castToFloatDown(oEnvelope.MinY)));
1465 1867 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1466 : m_apoBuildersBBOXXMax[iGeomField]->Append(
1467 : castToFloatUp(oEnvelope.MaxX)));
1468 1867 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1469 : m_apoBuildersBBOXYMax[iGeomField]->Append(
1470 : castToFloatUp(oEnvelope.MaxY)));
1471 1867 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1472 : m_apoBuildersBBOXStruct[iGeomField]->Append());
1473 : }
1474 : else
1475 : {
1476 1318 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1477 : m_apoBuildersBBOXStruct[iGeomField]->AppendNull());
1478 : }
1479 : }
1480 :
1481 3892 : if (poGeom == nullptr)
1482 : {
1483 4068 : if (m_aeGeomEncoding[iGeomField] ==
1484 1364 : OGRArrowGeomEncoding::GEOARROW_FSL_POINT &&
1485 1364 : GetDriverUCName() == "PARQUET")
1486 : {
1487 : // For some reason, Parquet doesn't support a NULL FixedSizeList
1488 : // on reading
1489 4 : auto poPointBuilder =
1490 : static_cast<arrow::FixedSizeListBuilder *>(poBuilder);
1491 4 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poPointBuilder->Append());
1492 : auto poValueBuilder = static_cast<arrow::DoubleBuilder *>(
1493 4 : poPointBuilder->value_builder());
1494 4 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poValueBuilder->Append(
1495 : std::numeric_limits<double>::quiet_NaN()));
1496 4 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poValueBuilder->Append(
1497 : std::numeric_limits<double>::quiet_NaN()));
1498 4 : if (bHasZ)
1499 2 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poValueBuilder->Append(
1500 : std::numeric_limits<double>::quiet_NaN()));
1501 4 : if (bHasM)
1502 0 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poValueBuilder->Append(
1503 : std::numeric_limits<double>::quiet_NaN()));
1504 : }
1505 : else
1506 : {
1507 1352 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poBuilder->AppendNull());
1508 : }
1509 :
1510 1356 : return OGRERR_NONE;
1511 : }
1512 :
1513 : // The following checks are only valid for GeoArrow encoding
1514 3714 : if (m_aeGeomEncoding[iGeomField] != OGRArrowGeomEncoding::WKB &&
1515 1178 : m_aeGeomEncoding[iGeomField] != OGRArrowGeomEncoding::WKT)
1516 : {
1517 1114 : if ((!bIsEmpty && eGType != eColumnGType) ||
1518 244 : (bIsEmpty && wkbFlatten(eGType) != wkbFlatten(eColumnGType)))
1519 : {
1520 6 : CPLError(CE_Warning, CPLE_AppDefined,
1521 : "Geometry of type %s found, whereas %s is expected. "
1522 : "Writing null geometry",
1523 : OGRGeometryTypeToName(eGType),
1524 : OGRGeometryTypeToName(eColumnGType));
1525 6 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poBuilder->AppendNull());
1526 :
1527 6 : return OGRERR_NONE;
1528 : }
1529 : }
1530 :
1531 2530 : switch (m_aeGeomEncoding[iGeomField])
1532 : {
1533 1358 : case OGRArrowGeomEncoding::WKB:
1534 : {
1535 0 : std::unique_ptr<OGRGeometry> poGeomModified;
1536 1358 : if (OGR_GT_HasM(eGType) && !OGR_GT_HasM(eColumnGType))
1537 : {
1538 : static bool bHasWarned = false;
1539 0 : if (!bHasWarned)
1540 : {
1541 0 : CPLError(CE_Warning, CPLE_AppDefined,
1542 : "Removing M component from geometry");
1543 0 : bHasWarned = true;
1544 : }
1545 0 : poGeomModified.reset(poGeom->clone());
1546 0 : poGeomModified->setMeasured(false);
1547 0 : poGeom = poGeomModified.get();
1548 : }
1549 1358 : FixupGeometryBeforeWriting(poGeom);
1550 1358 : const auto nSize = poGeom->WkbSize();
1551 1358 : if (nSize < INT_MAX)
1552 : {
1553 1358 : m_abyBuffer.resize(nSize);
1554 1358 : poGeom->exportToWkb(wkbNDR, &m_abyBuffer[0], wkbVariantIso);
1555 1358 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1556 : static_cast<arrow::BinaryBuilder *>(poBuilder)->Append(
1557 : m_abyBuffer.data(),
1558 : static_cast<int>(m_abyBuffer.size())));
1559 : }
1560 : else
1561 : {
1562 0 : CPLError(CE_Warning, CPLE_AppDefined,
1563 : "Too big geometry. "
1564 : "Writing null geometry");
1565 0 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poBuilder->AppendNull());
1566 : }
1567 1358 : break;
1568 : }
1569 :
1570 308 : case OGRArrowGeomEncoding::WKT:
1571 : {
1572 308 : OGRWktOptions options;
1573 308 : options.variant = wkbVariantIso;
1574 308 : if (m_nWKTCoordinatePrecision >= 0)
1575 : {
1576 0 : options.format = OGRWktFormat::F;
1577 0 : options.xyPrecision = m_nWKTCoordinatePrecision;
1578 0 : options.zPrecision = m_nWKTCoordinatePrecision;
1579 0 : options.mPrecision = m_nWKTCoordinatePrecision;
1580 : }
1581 308 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1582 : static_cast<arrow::StringBuilder *>(poBuilder)->Append(
1583 : poGeom->exportToWkt(options)));
1584 308 : break;
1585 : }
1586 :
1587 20 : case OGRArrowGeomEncoding::GEOARROW_FSL_POINT:
1588 : {
1589 20 : const auto poPoint = poGeom->toPoint();
1590 20 : auto poPointBuilder =
1591 : static_cast<arrow::FixedSizeListBuilder *>(poBuilder);
1592 20 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poPointBuilder->Append());
1593 : auto poValueBuilder = static_cast<arrow::DoubleBuilder *>(
1594 20 : poPointBuilder->value_builder());
1595 20 : if (bIsEmpty)
1596 : {
1597 8 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poValueBuilder->Append(
1598 : std::numeric_limits<double>::quiet_NaN()));
1599 8 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poValueBuilder->Append(
1600 : std::numeric_limits<double>::quiet_NaN()));
1601 8 : if (bHasZ)
1602 4 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poValueBuilder->Append(
1603 : std::numeric_limits<double>::quiet_NaN()));
1604 8 : if (bHasM)
1605 2 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poValueBuilder->Append(
1606 : std::numeric_limits<double>::quiet_NaN()));
1607 : }
1608 : else
1609 : {
1610 12 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1611 : poValueBuilder->Append(poPoint->getX()));
1612 12 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1613 : poValueBuilder->Append(poPoint->getY()));
1614 12 : if (bHasZ)
1615 6 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1616 : poValueBuilder->Append(poPoint->getZ()));
1617 12 : if (bHasM)
1618 2 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1619 : poValueBuilder->Append(poPoint->getM()));
1620 : }
1621 20 : break;
1622 : }
1623 :
1624 : #define GET_XYZM_STRUCT_FIELD_BUILDERS_FROM(poPointBuilder) \
1625 : auto poXBuilder = \
1626 : static_cast<arrow::DoubleBuilder *>(poPointBuilder->field_builder(0)); \
1627 : auto poYBuilder = \
1628 : static_cast<arrow::DoubleBuilder *>(poPointBuilder->field_builder(1)); \
1629 : int iSubField = 2; \
1630 : arrow::DoubleBuilder *poZBuilder = nullptr; \
1631 : if (bHasZ) \
1632 : { \
1633 : poZBuilder = static_cast<arrow::DoubleBuilder *>( \
1634 : poPointBuilder->field_builder(iSubField)); \
1635 : ++iSubField; \
1636 : } \
1637 : arrow::DoubleBuilder *poMBuilder = nullptr; \
1638 : if (bHasM) \
1639 : { \
1640 : poMBuilder = static_cast<arrow::DoubleBuilder *>( \
1641 : poPointBuilder->field_builder(iSubField)); \
1642 : } \
1643 : do \
1644 : { \
1645 : } while (0)
1646 :
1647 109 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_POINT:
1648 : {
1649 109 : const auto poPoint = poGeom->toPoint();
1650 109 : auto poPointBuilder =
1651 : static_cast<arrow::StructBuilder *>(poBuilder);
1652 109 : GET_XYZM_STRUCT_FIELD_BUILDERS_FROM(poPointBuilder);
1653 109 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poPointBuilder->Append());
1654 :
1655 109 : if (bIsEmpty)
1656 : {
1657 28 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poXBuilder->Append(
1658 : std::numeric_limits<double>::quiet_NaN()));
1659 28 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poYBuilder->Append(
1660 : std::numeric_limits<double>::quiet_NaN()));
1661 : }
1662 : else
1663 : {
1664 81 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1665 : poXBuilder->Append(poPoint->getX()));
1666 81 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1667 : poYBuilder->Append(poPoint->getY()));
1668 : }
1669 109 : if (poZBuilder)
1670 : {
1671 40 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poZBuilder->Append(
1672 : bIsEmpty ? std::numeric_limits<double>::quiet_NaN()
1673 : : poPoint->getZ()));
1674 : }
1675 109 : if (poMBuilder)
1676 : {
1677 4 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poMBuilder->Append(
1678 : bIsEmpty ? std::numeric_limits<double>::quiet_NaN()
1679 : : poPoint->getM()));
1680 : }
1681 109 : break;
1682 : }
1683 :
1684 20 : case OGRArrowGeomEncoding::GEOARROW_FSL_LINESTRING:
1685 : {
1686 20 : const auto poLS = poGeom->toLineString();
1687 20 : auto poListBuilder = static_cast<arrow::ListBuilder *>(poBuilder);
1688 : auto poPointBuilder = static_cast<arrow::FixedSizeListBuilder *>(
1689 20 : poListBuilder->value_builder());
1690 : auto poValueBuilder = static_cast<arrow::DoubleBuilder *>(
1691 20 : poPointBuilder->value_builder());
1692 :
1693 20 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poListBuilder->Append());
1694 20 : OGR_ARROW_PROPAGATE_OGRERR(GeoArrowLineBuilder(
1695 : poLS, poPointBuilder, poValueBuilder, poValueBuilder,
1696 : bHasZ ? poValueBuilder : nullptr,
1697 : bHasM ? poValueBuilder : nullptr));
1698 20 : break;
1699 : }
1700 :
1701 81 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_LINESTRING:
1702 : {
1703 81 : const auto poLS = poGeom->toLineString();
1704 81 : auto poListBuilder = static_cast<arrow::ListBuilder *>(poBuilder);
1705 : auto poPointBuilder = static_cast<arrow::StructBuilder *>(
1706 81 : poListBuilder->value_builder());
1707 81 : GET_XYZM_STRUCT_FIELD_BUILDERS_FROM(poPointBuilder);
1708 :
1709 81 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poListBuilder->Append());
1710 81 : OGR_ARROW_PROPAGATE_OGRERR(
1711 : GeoArrowLineBuilder(poLS, poPointBuilder, poXBuilder,
1712 : poYBuilder, poZBuilder, poMBuilder));
1713 81 : break;
1714 : }
1715 :
1716 32 : case OGRArrowGeomEncoding::GEOARROW_FSL_POLYGON:
1717 : {
1718 32 : const auto poPolygon = poGeom->toPolygon();
1719 32 : auto poPolygonBuilder =
1720 : static_cast<arrow::ListBuilder *>(poBuilder);
1721 : auto poRingBuilder = static_cast<arrow::ListBuilder *>(
1722 32 : poPolygonBuilder->value_builder());
1723 : auto poPointBuilder = static_cast<arrow::FixedSizeListBuilder *>(
1724 32 : poRingBuilder->value_builder());
1725 : auto poValueBuilder = static_cast<arrow::DoubleBuilder *>(
1726 32 : poPointBuilder->value_builder());
1727 32 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poPolygonBuilder->Append());
1728 62 : for (const auto *poRing : *poPolygon)
1729 : {
1730 30 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poRingBuilder->Append());
1731 30 : OGR_ARROW_PROPAGATE_OGRERR(GeoArrowLineBuilder(
1732 : poRing, poPointBuilder, poValueBuilder, poValueBuilder,
1733 : bHasZ ? poValueBuilder : nullptr,
1734 : bHasM ? poValueBuilder : nullptr));
1735 : }
1736 32 : break;
1737 : }
1738 :
1739 133 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_POLYGON:
1740 : {
1741 133 : const auto poPolygon = poGeom->toPolygon();
1742 133 : auto poPolygonBuilder =
1743 : static_cast<arrow::ListBuilder *>(poBuilder);
1744 : auto poRingBuilder = static_cast<arrow::ListBuilder *>(
1745 133 : poPolygonBuilder->value_builder());
1746 : auto poPointBuilder = static_cast<arrow::StructBuilder *>(
1747 133 : poRingBuilder->value_builder());
1748 133 : GET_XYZM_STRUCT_FIELD_BUILDERS_FROM(poPointBuilder);
1749 :
1750 133 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poPolygonBuilder->Append());
1751 254 : for (const auto *poRing : *poPolygon)
1752 : {
1753 121 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poRingBuilder->Append());
1754 121 : OGR_ARROW_PROPAGATE_OGRERR(
1755 : GeoArrowLineBuilder(poRing, poPointBuilder, poXBuilder,
1756 : poYBuilder, poZBuilder, poMBuilder));
1757 : }
1758 133 : break;
1759 : }
1760 :
1761 32 : case OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOINT:
1762 : {
1763 32 : const auto poMultiPoint = poGeom->toMultiPoint();
1764 32 : auto poListBuilder = static_cast<arrow::ListBuilder *>(poBuilder);
1765 : auto poPointBuilder = static_cast<arrow::FixedSizeListBuilder *>(
1766 32 : poListBuilder->value_builder());
1767 : auto poValueBuilder = static_cast<arrow::DoubleBuilder *>(
1768 32 : poPointBuilder->value_builder());
1769 32 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poListBuilder->Append());
1770 88 : for (const auto *poPoint : *poMultiPoint)
1771 : {
1772 56 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poPointBuilder->Append());
1773 56 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1774 : poValueBuilder->Append(poPoint->getX()));
1775 56 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1776 : poValueBuilder->Append(poPoint->getY()));
1777 56 : if (bHasZ)
1778 28 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1779 : poValueBuilder->Append(poPoint->getZ()));
1780 56 : if (bHasM)
1781 18 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1782 : poValueBuilder->Append(poPoint->getM()));
1783 : }
1784 32 : break;
1785 : }
1786 :
1787 113 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOINT:
1788 : {
1789 113 : const auto poMultiPoint = poGeom->toMultiPoint();
1790 113 : auto poListBuilder = static_cast<arrow::ListBuilder *>(poBuilder);
1791 : auto poPointBuilder = static_cast<arrow::StructBuilder *>(
1792 113 : poListBuilder->value_builder());
1793 113 : GET_XYZM_STRUCT_FIELD_BUILDERS_FROM(poPointBuilder);
1794 :
1795 113 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poListBuilder->Append());
1796 270 : for (const auto *poPoint : *poMultiPoint)
1797 : {
1798 157 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poPointBuilder->Append());
1799 157 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1800 : poXBuilder->Append(poPoint->getX()));
1801 157 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1802 : poYBuilder->Append(poPoint->getY()));
1803 157 : if (poZBuilder)
1804 78 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1805 : poZBuilder->Append(poPoint->getZ()));
1806 157 : if (poMBuilder)
1807 18 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
1808 : poMBuilder->Append(poPoint->getM()));
1809 : }
1810 113 : break;
1811 : }
1812 :
1813 28 : case OGRArrowGeomEncoding::GEOARROW_FSL_MULTILINESTRING:
1814 : {
1815 28 : const auto poMLS = poGeom->toMultiLineString();
1816 28 : auto poMLSBuilder = static_cast<arrow::ListBuilder *>(poBuilder);
1817 : auto poLSBuilder = static_cast<arrow::ListBuilder *>(
1818 28 : poMLSBuilder->value_builder());
1819 : auto poPointBuilder = static_cast<arrow::FixedSizeListBuilder *>(
1820 28 : poLSBuilder->value_builder());
1821 : auto poValueBuilder = static_cast<arrow::DoubleBuilder *>(
1822 28 : poPointBuilder->value_builder());
1823 28 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poMLSBuilder->Append());
1824 60 : for (const auto *poLS : *poMLS)
1825 : {
1826 32 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poLSBuilder->Append());
1827 32 : OGR_ARROW_PROPAGATE_OGRERR(GeoArrowLineBuilder(
1828 : poLS, poPointBuilder, poValueBuilder, poValueBuilder,
1829 : bHasZ ? poValueBuilder : nullptr,
1830 : bHasM ? poValueBuilder : nullptr));
1831 : }
1832 28 : break;
1833 : }
1834 :
1835 109 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTILINESTRING:
1836 : {
1837 109 : const auto poMLS = poGeom->toMultiLineString();
1838 109 : auto poMLSBuilder = static_cast<arrow::ListBuilder *>(poBuilder);
1839 : auto poLSBuilder = static_cast<arrow::ListBuilder *>(
1840 109 : poMLSBuilder->value_builder());
1841 : auto poPointBuilder = static_cast<arrow::StructBuilder *>(
1842 109 : poLSBuilder->value_builder());
1843 109 : GET_XYZM_STRUCT_FIELD_BUILDERS_FROM(poPointBuilder);
1844 :
1845 109 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poMLSBuilder->Append());
1846 242 : for (const auto *poLS : *poMLS)
1847 : {
1848 133 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poLSBuilder->Append());
1849 133 : OGR_ARROW_PROPAGATE_OGRERR(
1850 : GeoArrowLineBuilder(poLS, poPointBuilder, poXBuilder,
1851 : poYBuilder, poZBuilder, poMBuilder));
1852 : }
1853 109 : break;
1854 : }
1855 :
1856 38 : case OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOLYGON:
1857 : {
1858 38 : const auto poMPoly = poGeom->toMultiPolygon();
1859 38 : auto poMPolyBuilder = static_cast<arrow::ListBuilder *>(poBuilder);
1860 : auto poPolyBuilder = static_cast<arrow::ListBuilder *>(
1861 38 : poMPolyBuilder->value_builder());
1862 : auto poRingBuilder = static_cast<arrow::ListBuilder *>(
1863 38 : poPolyBuilder->value_builder());
1864 : auto poPointBuilder = static_cast<arrow::FixedSizeListBuilder *>(
1865 38 : poRingBuilder->value_builder());
1866 : auto poValueBuilder = static_cast<arrow::DoubleBuilder *>(
1867 38 : poPointBuilder->value_builder());
1868 38 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poMPolyBuilder->Append());
1869 82 : for (const auto *poPolygon : *poMPoly)
1870 : {
1871 44 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poPolyBuilder->Append());
1872 98 : for (const auto *poRing : *poPolygon)
1873 : {
1874 54 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poRingBuilder->Append());
1875 54 : OGR_ARROW_PROPAGATE_OGRERR(GeoArrowLineBuilder(
1876 : poRing, poPointBuilder, poValueBuilder, poValueBuilder,
1877 : bHasZ ? poValueBuilder : nullptr,
1878 : bHasM ? poValueBuilder : nullptr));
1879 : }
1880 : }
1881 38 : break;
1882 : }
1883 :
1884 149 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOLYGON:
1885 : {
1886 149 : const auto poMPoly = poGeom->toMultiPolygon();
1887 149 : auto poMPolyBuilder = static_cast<arrow::ListBuilder *>(poBuilder);
1888 : auto poPolyBuilder = static_cast<arrow::ListBuilder *>(
1889 149 : poMPolyBuilder->value_builder());
1890 : auto poRingBuilder = static_cast<arrow::ListBuilder *>(
1891 149 : poPolyBuilder->value_builder());
1892 : auto poPointBuilder = static_cast<arrow::StructBuilder *>(
1893 149 : poRingBuilder->value_builder());
1894 149 : GET_XYZM_STRUCT_FIELD_BUILDERS_FROM(poPointBuilder);
1895 :
1896 149 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poMPolyBuilder->Append());
1897 314 : for (const auto *poPolygon : *poMPoly)
1898 : {
1899 165 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poPolyBuilder->Append());
1900 370 : for (const auto *poRing : *poPolygon)
1901 : {
1902 205 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poRingBuilder->Append());
1903 205 : OGR_ARROW_PROPAGATE_OGRERR(GeoArrowLineBuilder(
1904 : poRing, poPointBuilder, poXBuilder, poYBuilder,
1905 : poZBuilder, poMBuilder));
1906 : }
1907 : }
1908 149 : break;
1909 : }
1910 :
1911 0 : case OGRArrowGeomEncoding::GEOARROW_FSL_GENERIC:
1912 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_GENERIC:
1913 : {
1914 0 : CPLAssert(false);
1915 : break;
1916 : }
1917 : }
1918 :
1919 2530 : return OGRERR_NONE;
1920 : }
1921 :
1922 : /************************************************************************/
1923 : /* ICreateFeature() */
1924 : /************************************************************************/
1925 :
1926 3441 : inline OGRErr OGRArrowWriterLayer::ICreateFeature(OGRFeature *poFeature)
1927 : {
1928 3441 : if (m_poSchema == nullptr)
1929 : {
1930 315 : CreateSchema();
1931 : }
1932 :
1933 3441 : if (m_apoBuilders.empty())
1934 : {
1935 353 : if (!m_apoFieldsFromArrowSchema.empty())
1936 : {
1937 0 : CPLError(CE_Failure, CPLE_NotSupported,
1938 : "ICreateFeature() cannot be used after "
1939 : "CreateFieldFromArrowSchema()");
1940 0 : return OGRERR_FAILURE;
1941 : }
1942 353 : CreateArrayBuilders();
1943 : }
1944 :
1945 : // First pass to check not-null constraints as Arrow doesn't seem
1946 : // to do that on the writing side. But such files can't be read.
1947 3441 : const int nFieldCount = m_poFeatureDefn->GetFieldCount();
1948 9048 : for (int i = 0; i < nFieldCount; ++i)
1949 : {
1950 5608 : const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
1951 5610 : if (!poFieldDefn->IsNullable() &&
1952 2 : !poFeature->IsFieldSetAndNotNullUnsafe(i))
1953 : {
1954 1 : CPLError(CE_Failure, CPLE_AppDefined,
1955 : "Null value found in non-nullable field %s",
1956 : poFieldDefn->GetNameRef());
1957 1 : return OGRERR_FAILURE;
1958 : }
1959 : }
1960 :
1961 3440 : const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
1962 6983 : for (int i = 0; i < nGeomFieldCount; ++i)
1963 : {
1964 3543 : const auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(i);
1965 3575 : if (!poGeomFieldDefn->IsNullable() &&
1966 32 : poFeature->GetGeomFieldRef(i) == nullptr)
1967 : {
1968 0 : CPLError(CE_Failure, CPLE_AppDefined,
1969 : "Null value found in non-nullable geometry field %s",
1970 : poGeomFieldDefn->GetNameRef());
1971 0 : return OGRERR_FAILURE;
1972 : }
1973 : }
1974 :
1975 : // Write FID, if FID column present
1976 3440 : int nArrowIdx = 0;
1977 3440 : if (!m_osFIDColumn.empty())
1978 : {
1979 2261 : int64_t nFID = poFeature->GetFID();
1980 2261 : if (nFID == OGRNullFID)
1981 : {
1982 37 : nFID = m_nFeatureCount;
1983 37 : poFeature->SetFID(nFID);
1984 : }
1985 : auto poBuilder =
1986 2261 : static_cast<arrow::Int64Builder *>(m_apoBuilders[0].get());
1987 2261 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poBuilder->Append(nFID));
1988 2261 : nArrowIdx++;
1989 : }
1990 :
1991 : // Write attributes
1992 9047 : for (int i = 0; i < nFieldCount; ++i, ++nArrowIdx)
1993 : {
1994 5607 : auto poBuilder = m_apoBuilders[nArrowIdx].get();
1995 5607 : if (!poFeature->IsFieldSetAndNotNullUnsafe(i))
1996 : {
1997 1248 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poBuilder->AppendNull());
1998 1248 : continue;
1999 : }
2000 :
2001 4359 : const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
2002 4359 : const auto eSubDT = poFieldDefn->GetSubType();
2003 4359 : switch (poFieldDefn->GetType())
2004 : {
2005 2485 : case OFTInteger:
2006 2485 : if (eSubDT == OFSTBoolean)
2007 16 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
2008 : static_cast<arrow::BooleanBuilder *>(poBuilder)->Append(
2009 : poFeature->GetFieldAsIntegerUnsafe(i) != 0));
2010 2469 : else if (eSubDT == OFSTInt16)
2011 16 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
2012 : static_cast<arrow::Int16Builder *>(poBuilder)->Append(
2013 : static_cast<int16_t>(
2014 : poFeature->GetFieldAsIntegerUnsafe(i))));
2015 : else
2016 2453 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
2017 : static_cast<arrow::Int32Builder *>(poBuilder)->Append(
2018 : poFeature->GetFieldAsIntegerUnsafe(i)));
2019 2485 : break;
2020 :
2021 172 : case OFTInteger64:
2022 172 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
2023 : static_cast<arrow::Int64Builder *>(poBuilder)->Append(
2024 : static_cast<int64_t>(
2025 : poFeature->GetFieldAsInteger64Unsafe(i))));
2026 172 : break;
2027 :
2028 236 : case OFTReal:
2029 : {
2030 236 : const auto arrowType = m_poSchema->fields()[nArrowIdx]->type();
2031 236 : const double dfVal = poFeature->GetFieldAsDoubleUnsafe(i);
2032 236 : if (arrowType->id() == arrow::Type::DECIMAL128)
2033 : {
2034 : auto res = arrow::Decimal128::FromReal(
2035 : dfVal, poFieldDefn->GetWidth(),
2036 62 : poFieldDefn->GetPrecision());
2037 62 : if (res.ok())
2038 : {
2039 62 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
2040 : static_cast<arrow::Decimal128Builder *>(poBuilder)
2041 : ->Append(*res));
2042 : }
2043 : else
2044 : {
2045 0 : CPLError(CE_Warning, CPLE_AppDefined,
2046 : "Cannot parse %.18g as a %d.%d decimal", dfVal,
2047 : poFieldDefn->GetWidth(),
2048 : poFieldDefn->GetPrecision());
2049 0 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poBuilder->AppendNull());
2050 : }
2051 : }
2052 174 : else if (arrowType->id() == arrow::Type::DECIMAL256)
2053 : {
2054 : auto res = arrow::Decimal256::FromReal(
2055 : dfVal, poFieldDefn->GetWidth(),
2056 0 : poFieldDefn->GetPrecision());
2057 0 : if (res.ok())
2058 : {
2059 0 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
2060 : static_cast<arrow::Decimal256Builder *>(poBuilder)
2061 : ->Append(*res));
2062 : }
2063 : else
2064 : {
2065 0 : CPLError(CE_Warning, CPLE_AppDefined,
2066 : "Cannot parse %.18g as a %d.%d decimal", dfVal,
2067 : poFieldDefn->GetWidth(),
2068 : poFieldDefn->GetPrecision());
2069 0 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poBuilder->AppendNull());
2070 : }
2071 : }
2072 174 : else if (eSubDT == OFSTFloat32)
2073 : {
2074 28 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
2075 : static_cast<arrow::FloatBuilder *>(poBuilder)->Append(
2076 : static_cast<float>(dfVal)));
2077 : }
2078 : else
2079 : {
2080 146 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
2081 : static_cast<arrow::DoubleBuilder *>(poBuilder)->Append(
2082 : dfVal));
2083 : }
2084 236 : break;
2085 : }
2086 :
2087 545 : case OFTString:
2088 : case OFTWideString:
2089 545 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
2090 : static_cast<arrow::StringBuilder *>(poBuilder)->Append(
2091 : poFeature->GetFieldAsStringUnsafe(i)));
2092 545 : break;
2093 :
2094 62 : case OFTBinary:
2095 : {
2096 62 : int nSize = 0;
2097 62 : const auto pData = poFeature->GetFieldAsBinary(i, &nSize);
2098 62 : if (poFieldDefn->GetWidth() != 0)
2099 : {
2100 20 : if (poFieldDefn->GetWidth() != nSize)
2101 : {
2102 0 : CPLError(
2103 : CE_Warning, CPLE_AppDefined,
2104 : "Cannot write field %s. Got %d bytes, expected %d",
2105 : poFieldDefn->GetNameRef(), nSize,
2106 : poFieldDefn->GetWidth());
2107 0 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poBuilder->AppendNull());
2108 : }
2109 : else
2110 : {
2111 20 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
2112 : static_cast<arrow::FixedSizeBinaryBuilder *>(
2113 : poBuilder)
2114 : ->Append(pData));
2115 : }
2116 : }
2117 : else
2118 42 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
2119 : static_cast<arrow::BinaryBuilder *>(poBuilder)->Append(
2120 : pData, nSize));
2121 62 : break;
2122 : }
2123 :
2124 216 : case OFTIntegerList:
2125 : {
2126 216 : auto poListBuilder =
2127 : static_cast<arrow::ListBuilder *>(poBuilder);
2128 216 : if (eSubDT == OFSTBoolean)
2129 : {
2130 36 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poListBuilder->Append());
2131 : auto poValueBuilder = static_cast<arrow::BooleanBuilder *>(
2132 36 : poListBuilder->value_builder());
2133 36 : int nValues = 0;
2134 : const auto panValues =
2135 36 : poFeature->GetFieldAsIntegerList(i, &nValues);
2136 108 : for (int j = 0; j < nValues; ++j)
2137 72 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
2138 : poValueBuilder->Append(panValues[j] != 0));
2139 : }
2140 180 : else if (eSubDT == OFSTInt16)
2141 : {
2142 0 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poListBuilder->Append());
2143 : auto poValueBuilder = static_cast<arrow::Int16Builder *>(
2144 0 : poListBuilder->value_builder());
2145 0 : int nValues = 0;
2146 : const auto panValues =
2147 0 : poFeature->GetFieldAsIntegerList(i, &nValues);
2148 0 : for (int j = 0; j < nValues; ++j)
2149 0 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poValueBuilder->Append(
2150 : static_cast<int16_t>(panValues[j])));
2151 : }
2152 : else
2153 : {
2154 180 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poListBuilder->Append());
2155 : auto poValueBuilder = static_cast<arrow::Int32Builder *>(
2156 180 : poListBuilder->value_builder());
2157 180 : int nValues = 0;
2158 : const auto panValues =
2159 180 : poFeature->GetFieldAsIntegerList(i, &nValues);
2160 540 : for (int j = 0; j < nValues; ++j)
2161 360 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
2162 : poValueBuilder->Append(panValues[j]));
2163 : }
2164 216 : break;
2165 : }
2166 :
2167 92 : case OFTInteger64List:
2168 : {
2169 92 : auto poListBuilder =
2170 : static_cast<arrow::ListBuilder *>(poBuilder);
2171 92 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poListBuilder->Append());
2172 : auto poValueBuilder = static_cast<arrow::Int64Builder *>(
2173 92 : poListBuilder->value_builder());
2174 92 : int nValues = 0;
2175 : const auto panValues =
2176 92 : poFeature->GetFieldAsInteger64List(i, &nValues);
2177 292 : for (int j = 0; j < nValues; ++j)
2178 200 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poValueBuilder->Append(
2179 : static_cast<int64_t>(panValues[j])));
2180 92 : break;
2181 : }
2182 :
2183 152 : case OFTRealList:
2184 : {
2185 152 : auto poListBuilder =
2186 : static_cast<arrow::ListBuilder *>(poBuilder);
2187 152 : if (eSubDT == OFSTFloat32)
2188 : {
2189 48 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poListBuilder->Append());
2190 : auto poValueBuilder = static_cast<arrow::FloatBuilder *>(
2191 48 : poListBuilder->value_builder());
2192 48 : int nValues = 0;
2193 : const auto padfValues =
2194 48 : poFeature->GetFieldAsDoubleList(i, &nValues);
2195 144 : for (int j = 0; j < nValues; ++j)
2196 96 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poValueBuilder->Append(
2197 : static_cast<float>(padfValues[j])));
2198 : }
2199 : else
2200 : {
2201 104 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poListBuilder->Append());
2202 : auto poValueBuilder = static_cast<arrow::DoubleBuilder *>(
2203 104 : poListBuilder->value_builder());
2204 104 : int nValues = 0;
2205 : const auto padfValues =
2206 104 : poFeature->GetFieldAsDoubleList(i, &nValues);
2207 280 : for (int j = 0; j < nValues; ++j)
2208 176 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
2209 : poValueBuilder->Append(padfValues[j]));
2210 : }
2211 152 : break;
2212 : }
2213 :
2214 52 : case OFTStringList:
2215 : case OFTWideStringList:
2216 : {
2217 52 : auto poListBuilder =
2218 : static_cast<arrow::ListBuilder *>(poBuilder);
2219 52 : OGR_ARROW_RETURN_OGRERR_NOT_OK(poListBuilder->Append());
2220 : auto poValueBuilder = static_cast<arrow::StringBuilder *>(
2221 52 : poListBuilder->value_builder());
2222 52 : const auto papszValues = poFeature->GetFieldAsStringList(i);
2223 132 : for (int j = 0; papszValues && papszValues[j]; ++j)
2224 80 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
2225 : poValueBuilder->Append(papszValues[j]));
2226 52 : break;
2227 : }
2228 :
2229 109 : case OFTDate:
2230 : {
2231 : int nYear, nMonth, nDay, nHour, nMinute;
2232 : float fSec;
2233 : int nTZFlag;
2234 109 : poFeature->GetFieldAsDateTime(i, &nYear, &nMonth, &nDay, &nHour,
2235 : &nMinute, &fSec, &nTZFlag);
2236 : struct tm brokenDown;
2237 109 : memset(&brokenDown, 0, sizeof(brokenDown));
2238 109 : brokenDown.tm_year = nYear - 1900;
2239 109 : brokenDown.tm_mon = nMonth - 1;
2240 109 : brokenDown.tm_mday = nDay;
2241 109 : GIntBig nVal = CPLYMDHMSToUnixTime(&brokenDown);
2242 109 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
2243 : static_cast<arrow::Date32Builder *>(poBuilder)->Append(
2244 : static_cast<int>(nVal / 86400)));
2245 109 : break;
2246 : }
2247 :
2248 36 : case OFTTime:
2249 : {
2250 : int nYear, nMonth, nDay, nHour, nMinute;
2251 : float fSec;
2252 : int nTZFlag;
2253 36 : poFeature->GetFieldAsDateTime(i, &nYear, &nMonth, &nDay, &nHour,
2254 : &nMinute, &fSec, &nTZFlag);
2255 36 : int nVal = nHour * 3600 + nMinute * 60;
2256 36 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
2257 : static_cast<arrow::Time32Builder *>(poBuilder)->Append(
2258 : static_cast<int>(
2259 : (static_cast<double>(nVal) + fSec) * 1000 + 0.5)));
2260 36 : break;
2261 : }
2262 :
2263 202 : case OFTDateTime:
2264 : {
2265 : int nYear, nMonth, nDay, nHour, nMinute;
2266 : float fSec;
2267 : int nTZFlag;
2268 202 : poFeature->GetFieldAsDateTime(i, &nYear, &nMonth, &nDay, &nHour,
2269 : &nMinute, &fSec, &nTZFlag);
2270 : struct tm brokenDown;
2271 202 : memset(&brokenDown, 0, sizeof(brokenDown));
2272 202 : brokenDown.tm_year = nYear - 1900;
2273 202 : brokenDown.tm_mon = nMonth - 1;
2274 202 : brokenDown.tm_mday = nDay;
2275 202 : brokenDown.tm_hour = nHour;
2276 202 : brokenDown.tm_min = nMinute;
2277 202 : brokenDown.tm_sec = 0;
2278 202 : GIntBig nVal = CPLYMDHMSToUnixTime(&brokenDown);
2279 332 : if (!IsFileWriterCreated() &&
2280 130 : m_anTZFlag[i] != OGR_TZFLAG_UNKNOWN)
2281 : {
2282 72 : if (m_anTZFlag[i] == TZFLAG_UNINITIALIZED)
2283 35 : m_anTZFlag[i] = nTZFlag;
2284 37 : else if (m_anTZFlag[i] != nTZFlag)
2285 : {
2286 26 : if (m_anTZFlag[i] >= OGR_TZFLAG_MIXED_TZ &&
2287 13 : nTZFlag >= OGR_TZFLAG_MIXED_TZ)
2288 : {
2289 10 : m_anTZFlag[i] =
2290 : OGR_TZFLAG_MIXED_TZ; // harmonize on UTC ultimately
2291 : }
2292 : else
2293 : {
2294 3 : CPLError(CE_Warning, CPLE_AppDefined,
2295 : "Field %s contains a mix of "
2296 : "timezone-aware and local/without "
2297 : "timezone values.",
2298 : poFieldDefn->GetNameRef());
2299 3 : m_anTZFlag[i] = OGR_TZFLAG_UNKNOWN;
2300 : }
2301 : }
2302 : }
2303 202 : if (nTZFlag > OGR_TZFLAG_MIXED_TZ)
2304 : {
2305 70 : const int nOffsetSec = (nTZFlag - OGR_TZFLAG_UTC) * 15 * 60;
2306 70 : nVal -= nOffsetSec;
2307 : }
2308 202 : const int64_t nTimestamp = static_cast<int64_t>(
2309 202 : (static_cast<double>(nVal) + fSec) * 1000 + 0.5);
2310 : auto structBuilder =
2311 202 : dynamic_cast<arrow::StructBuilder *>(poBuilder);
2312 202 : if (structBuilder)
2313 : {
2314 10 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
2315 : static_cast<arrow::TimestampBuilder *>(
2316 : structBuilder->field_builder(0))
2317 : ->Append(nTimestamp));
2318 10 : const int16_t nUTCOffsetMin =
2319 10 : static_cast<int16_t>((nTZFlag - OGR_TZFLAG_UTC) * 15);
2320 10 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
2321 : static_cast<arrow::Int16Builder *>(
2322 : structBuilder->field_builder(1))
2323 : ->Append(nUTCOffsetMin));
2324 10 : OGR_ARROW_RETURN_OGRERR_NOT_OK(structBuilder->Append());
2325 : }
2326 : else
2327 : {
2328 192 : OGR_ARROW_RETURN_OGRERR_NOT_OK(
2329 : static_cast<arrow::TimestampBuilder *>(poBuilder)
2330 : ->Append(nTimestamp));
2331 : }
2332 202 : break;
2333 : }
2334 : }
2335 : }
2336 :
2337 : // Write geometries
2338 6983 : for (int i = 0; i < nGeomFieldCount; ++i, ++nArrowIdx)
2339 : {
2340 3543 : auto poBuilder = m_apoBuilders[nArrowIdx].get();
2341 3543 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
2342 3543 : if (BuildGeometry(poGeom, i, poBuilder) != OGRERR_NONE)
2343 0 : return OGRERR_FAILURE;
2344 : }
2345 :
2346 3440 : m_nFeatureCount++;
2347 :
2348 : // Flush the current row group if reaching the limit of rows per group.
2349 3440 : if (!m_apoBuilders.empty() && m_apoBuilders[0]->length() == m_nRowGroupSize)
2350 : {
2351 25 : if (!FlushFeatures())
2352 0 : return OGRERR_FAILURE;
2353 : }
2354 :
2355 3440 : return OGRERR_NONE;
2356 : }
2357 :
2358 : /************************************************************************/
2359 : /* FlushFeatures() */
2360 : /************************************************************************/
2361 :
2362 42 : inline bool OGRArrowWriterLayer::FlushFeatures()
2363 : {
2364 42 : if (m_apoBuilders.empty() || m_apoBuilders[0]->length() == 0)
2365 1 : return true;
2366 :
2367 41 : if (!IsFileWriterCreated())
2368 : {
2369 10 : CreateWriter();
2370 10 : if (!IsFileWriterCreated())
2371 0 : return false;
2372 : }
2373 :
2374 41 : return FlushGroup();
2375 : }
2376 :
2377 : /************************************************************************/
2378 : /* GetFeatureCount() */
2379 : /************************************************************************/
2380 :
2381 1 : inline GIntBig OGRArrowWriterLayer::GetFeatureCount(int bForce)
2382 : {
2383 1 : if (m_poAttrQuery == nullptr && m_poFilterGeom == nullptr)
2384 : {
2385 1 : return m_nFeatureCount;
2386 : }
2387 0 : return OGRLayer::GetFeatureCount(bForce);
2388 : }
2389 :
2390 : /************************************************************************/
2391 : /* TestCapability() */
2392 : /************************************************************************/
2393 :
2394 1075 : inline bool OGRArrowWriterLayer::TestCapability(const char *pszCap) const
2395 : {
2396 1075 : if (EQUAL(pszCap, OLCCreateField) || EQUAL(pszCap, OLCCreateGeomField))
2397 27 : return m_poSchema == nullptr;
2398 :
2399 1048 : if (EQUAL(pszCap, OLCSequentialWrite))
2400 24 : return true;
2401 :
2402 1024 : if (EQUAL(pszCap, OLCFastWriteArrowBatch))
2403 0 : return true;
2404 :
2405 1024 : if (EQUAL(pszCap, OLCStringsAsUTF8))
2406 1 : return true;
2407 :
2408 1023 : if (EQUAL(pszCap, OLCMeasuredGeometries))
2409 446 : return true;
2410 :
2411 577 : return false;
2412 : }
2413 :
2414 : /************************************************************************/
2415 : /* WriteArrays() */
2416 : /************************************************************************/
2417 :
2418 355 : inline bool OGRArrowWriterLayer::WriteArrays(
2419 : std::function<bool(const std::shared_ptr<arrow::Field> &,
2420 : const std::shared_ptr<arrow::Array> &)>
2421 : postProcessArray)
2422 : {
2423 355 : int nArrowIdx = 0;
2424 355 : int nArrowIdxFirstField = !m_osFIDColumn.empty() ? 1 : 0;
2425 2233 : for (const auto &poBuilder : m_apoBuilders)
2426 : {
2427 1878 : const auto &field = m_poSchema->fields()[nArrowIdx];
2428 :
2429 0 : std::shared_ptr<arrow::Array> array;
2430 1878 : auto status = poBuilder->Finish(&array);
2431 1878 : if (!status.ok())
2432 : {
2433 0 : CPLError(CE_Failure, CPLE_AppDefined,
2434 : "builder::Finish() for field %s failed with %s",
2435 0 : field->name().c_str(), status.message().c_str());
2436 0 : return false;
2437 : }
2438 :
2439 : // CPLDebug("ARROW", "%s", array->ToString().c_str());
2440 :
2441 1878 : const int iCol = nArrowIdx - nArrowIdxFirstField;
2442 1878 : if (iCol >= 0 && iCol < m_poFeatureDefn->GetFieldCount())
2443 : {
2444 1482 : const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(iCol);
2445 1482 : const auto eFieldType = poFieldDefn->GetType();
2446 1482 : if (eFieldType == OFTInteger || eFieldType == OFTInteger64)
2447 : {
2448 261 : const auto &osDomainName = poFieldDefn->GetDomainName();
2449 : const auto oIter =
2450 261 : m_oMapFieldDomainToStringArray.find(osDomainName);
2451 261 : if (oIter != m_oMapFieldDomainToStringArray.end())
2452 : {
2453 : auto result = arrow::DictionaryArray::FromArrays(
2454 12 : field->type(), array, oIter->second);
2455 12 : if (!result.ok())
2456 : {
2457 0 : CPLError(CE_Failure, CPLE_AppDefined,
2458 : "DictionaryArray::FromArrays() for field %s "
2459 : "failed with %s",
2460 0 : field->name().c_str(),
2461 0 : result.status().message().c_str());
2462 0 : return false;
2463 : }
2464 12 : array = *result;
2465 : }
2466 : }
2467 : }
2468 :
2469 1878 : if (!postProcessArray(field, array))
2470 : {
2471 0 : return false;
2472 : }
2473 :
2474 1878 : nArrowIdx++;
2475 : }
2476 :
2477 355 : if (m_bWriteBBoxStruct)
2478 : {
2479 263 : const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
2480 522 : for (int i = 0; i < nGeomFieldCount; ++i)
2481 : {
2482 259 : const auto &field = m_apoFieldsBBOX[i];
2483 0 : std::shared_ptr<arrow::Array> array;
2484 259 : auto status = m_apoBuildersBBOXStruct[i]->Finish(&array);
2485 259 : if (!status.ok())
2486 : {
2487 0 : CPLError(CE_Failure, CPLE_AppDefined,
2488 : "builder::Finish() for field %s failed with %s",
2489 0 : field->name().c_str(), status.message().c_str());
2490 0 : return false;
2491 : }
2492 :
2493 259 : if (!postProcessArray(field, array))
2494 : {
2495 0 : return false;
2496 : }
2497 : }
2498 : }
2499 :
2500 355 : return true;
2501 : }
2502 :
2503 : /************************************************************************/
2504 : /* TestBit() */
2505 : /************************************************************************/
2506 :
2507 516 : static inline bool TestBit(const uint8_t *pabyData, size_t nIdx)
2508 : {
2509 516 : return (pabyData[nIdx / 8] & (1 << (nIdx % 8))) != 0;
2510 : }
2511 :
2512 : /************************************************************************/
2513 : /* WriteArrowBatchInternal() */
2514 : /************************************************************************/
2515 :
2516 137 : inline bool OGRArrowWriterLayer::WriteArrowBatchInternal(
2517 : const struct ArrowSchema *schema, struct ArrowArray *array,
2518 : CSLConstList papszOptions,
2519 : std::function<bool(const std::shared_ptr<arrow::RecordBatch> &)> writeBatch)
2520 : {
2521 : #ifdef __COVERITY__
2522 : (void)schema;
2523 : (void)array;
2524 : (void)papszOptions;
2525 : (void)writeBatch;
2526 : CPLError(CE_Failure, CPLE_AppDefined, "Not implemented");
2527 : return false;
2528 : #else
2529 137 : if (m_poSchema == nullptr)
2530 : {
2531 131 : CreateSchema();
2532 : }
2533 :
2534 137 : if (!IsFileWriterCreated())
2535 : {
2536 131 : CreateWriter();
2537 131 : if (!IsFileWriterCreated())
2538 0 : return false;
2539 : }
2540 :
2541 137 : if (m_apoBuilders.empty())
2542 : {
2543 131 : CreateArrayBuilders();
2544 : }
2545 :
2546 137 : const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
2547 137 : const int nGeomFieldCountBBoxFields =
2548 137 : m_bWriteBBoxStruct ? nGeomFieldCount : 0;
2549 :
2550 137 : const char *pszFIDName = CSLFetchNameValueDef(
2551 : papszOptions, "FID", OGRLayer::DEFAULT_ARROW_FID_NAME);
2552 : const char *pszSingleGeomFieldName =
2553 137 : CSLFetchNameValue(papszOptions, "GEOMETRY_NAME");
2554 :
2555 : // Sort schema and array children in the same order as m_poSchema.
2556 : // This is needed for non-WKB geometry encoding
2557 274 : std::map<std::string, int> oMapSchemaChildrenNameToIdx;
2558 1760 : for (int i = 0; i < static_cast<int>(schema->n_children); ++i)
2559 : {
2560 1623 : if (cpl::contains(oMapSchemaChildrenNameToIdx,
2561 1623 : schema->children[i]->name))
2562 : {
2563 0 : CPLError(CE_Failure, CPLE_AppDefined,
2564 : "Several fields with same name '%s' found",
2565 0 : schema->children[i]->name);
2566 0 : return false;
2567 : }
2568 1623 : oMapSchemaChildrenNameToIdx[schema->children[i]->name] = i;
2569 :
2570 1623 : if (!pszSingleGeomFieldName && schema->children[i]->metadata)
2571 : {
2572 : const auto oMetadata =
2573 284 : OGRParseArrowMetadata(schema->children[i]->metadata);
2574 142 : const auto oIter = oMetadata.find(ARROW_EXTENSION_NAME_KEY);
2575 301 : if (oIter != oMetadata.end() &&
2576 159 : (oIter->second == EXTENSION_NAME_OGC_WKB ||
2577 19 : oIter->second == EXTENSION_NAME_GEOARROW_WKB))
2578 : {
2579 129 : pszSingleGeomFieldName = schema->children[i]->name;
2580 : }
2581 : }
2582 : }
2583 137 : if (!pszSingleGeomFieldName)
2584 8 : pszSingleGeomFieldName = OGRLayer::DEFAULT_ARROW_GEOMETRY_NAME;
2585 :
2586 137 : std::vector<int> anMapLayerSchemaToArraySchema(m_poSchema->num_fields(),
2587 411 : -1);
2588 : struct ArrowArray fidArray;
2589 : struct ArrowSchema fidSchema;
2590 137 : memset(&fidArray, 0, sizeof(fidArray));
2591 137 : memset(&fidSchema, 0, sizeof(fidSchema));
2592 274 : std::vector<void *> apBuffersFid;
2593 274 : std::vector<int64_t> fids;
2594 :
2595 274 : std::set<int> oSetReferencedFieldsInArraySchema;
2596 0 : const auto DummyFreeArray = [](struct ArrowArray *ptrArray)
2597 0 : { ptrArray->release = nullptr; };
2598 137 : const auto DummyFreeSchema = [](struct ArrowSchema *ptrSchema)
2599 137 : { ptrSchema->release = nullptr; };
2600 137 : bool bRebuildBatch = false;
2601 1758 : for (int i = 0; i < m_poSchema->num_fields() - nGeomFieldCountBBoxFields;
2602 : ++i)
2603 : {
2604 : auto oIter =
2605 1621 : oMapSchemaChildrenNameToIdx.find(m_poSchema->field(i)->name());
2606 1621 : if (oIter == oMapSchemaChildrenNameToIdx.end())
2607 : {
2608 7 : if (m_poSchema->field(i)->name() == m_osFIDColumn)
2609 : {
2610 1 : oIter = oMapSchemaChildrenNameToIdx.find(pszFIDName);
2611 1 : if (oIter == oMapSchemaChildrenNameToIdx.end())
2612 : {
2613 : // If the input data does not contain a FID column, but
2614 : // the output file requires it, creates a default FID column
2615 0 : fidArray.release = DummyFreeArray;
2616 0 : fidArray.n_buffers = 2;
2617 0 : apBuffersFid.resize(2);
2618 0 : fidArray.buffers =
2619 0 : const_cast<const void **>(apBuffersFid.data());
2620 0 : fids.reserve(static_cast<size_t>(array->length));
2621 0 : for (size_t iRow = 0;
2622 0 : iRow < static_cast<size_t>(array->length); ++iRow)
2623 0 : fids.push_back(m_nFeatureCount + iRow);
2624 0 : fidArray.buffers[1] = fids.data();
2625 0 : fidArray.length = array->length;
2626 0 : fidSchema.release = DummyFreeSchema;
2627 0 : fidSchema.name = m_osFIDColumn.c_str();
2628 0 : fidSchema.format = "l"; // int64
2629 0 : continue;
2630 : }
2631 : }
2632 12 : else if (nGeomFieldCount == 1 &&
2633 6 : m_poFeatureDefn->GetGeomFieldIndex(
2634 6 : m_poSchema->field(i)->name().c_str()) == 0)
2635 : {
2636 : oIter =
2637 6 : oMapSchemaChildrenNameToIdx.find(pszSingleGeomFieldName);
2638 6 : if (oIter != oMapSchemaChildrenNameToIdx.end())
2639 6 : bRebuildBatch = true;
2640 : }
2641 :
2642 7 : if (oIter == oMapSchemaChildrenNameToIdx.end())
2643 : {
2644 0 : CPLError(CE_Failure, CPLE_AppDefined,
2645 : "Cannot find field '%s' in schema",
2646 0 : m_poSchema->field(i)->name().c_str());
2647 0 : return false;
2648 : }
2649 : }
2650 1621 : anMapLayerSchemaToArraySchema[i] = oIter->second;
2651 1621 : oSetReferencedFieldsInArraySchema.insert(oIter->second);
2652 : }
2653 :
2654 : // Note: we cheat a bit by declaring a single instance of the minx/miny/
2655 : // maxx/maxy sub-field ArrowSchema*, and make all struct ArrowSchema point
2656 : // to them. That's OK because we use DummyFreeSchema to release, which does
2657 : // nothing.
2658 : struct ArrowSchema bboxStructSchemaXMin;
2659 : struct ArrowSchema bboxStructSchemaYMin;
2660 : struct ArrowSchema bboxStructSchemaXMax;
2661 : struct ArrowSchema bboxStructSchemaYMax;
2662 137 : constexpr int BBOX_SUBFIELD_COUNT = 4;
2663 : std::array<struct ArrowSchema *, BBOX_SUBFIELD_COUNT>
2664 : bboxStructSchemaChildren;
2665 137 : constexpr int BBOX_STRUCT_BUFFER_COUNT = 1; // validity bitmap array
2666 : // cppcheck-suppress constStatement
2667 : std::vector<std::array<const void *, BBOX_STRUCT_BUFFER_COUNT>>
2668 274 : bboxStructBuffersPtr;
2669 274 : std::vector<std::vector<GByte>> aabyBboxStructValidity;
2670 274 : std::vector<std::vector<float>> aadfMinX, aadfMinY, aadfMaxX, aadfMaxY;
2671 : // cppcheck-suppress constStatement
2672 274 : std::vector<std::array<struct ArrowArray, BBOX_SUBFIELD_COUNT>> bboxArrays;
2673 : // cppcheck-suppress constStatement
2674 : std::vector<std::array<struct ArrowArray *, BBOX_SUBFIELD_COUNT>>
2675 274 : bboxArraysPtr;
2676 137 : constexpr int BBOX_SUBFIELD_BUFFER_COUNT =
2677 : 2; // validity bitmap array and float array
2678 : std::vector<std::array<std::array<const void *, BBOX_SUBFIELD_BUFFER_COUNT>,
2679 : BBOX_SUBFIELD_COUNT>>
2680 274 : bboxBuffersPtr;
2681 :
2682 : // Temporary arrays to hold the geometry bounding boxes.
2683 274 : std::vector<struct ArrowArray> bboxStructArray;
2684 274 : std::vector<struct ArrowSchema> bboxStructSchema;
2685 :
2686 274 : std::vector<struct ArrowSchema *> newSchemaChildren;
2687 274 : std::vector<struct ArrowArray *> newArrayChildren;
2688 137 : newSchemaChildren.reserve(m_poSchema->num_fields());
2689 137 : newArrayChildren.reserve(m_poSchema->num_fields());
2690 1758 : for (int i = 0; i < m_poSchema->num_fields() - nGeomFieldCountBBoxFields;
2691 : ++i)
2692 : {
2693 1621 : if (anMapLayerSchemaToArraySchema[i] < 0)
2694 : {
2695 0 : CPLAssert(m_poSchema->field(i)->name() == m_osFIDColumn);
2696 0 : newSchemaChildren.emplace_back(&fidSchema);
2697 0 : newArrayChildren.emplace_back(&fidArray);
2698 : }
2699 : else
2700 : {
2701 : newSchemaChildren.emplace_back(
2702 1621 : schema->children[anMapLayerSchemaToArraySchema[i]]);
2703 : newArrayChildren.emplace_back(
2704 1621 : array->children[anMapLayerSchemaToArraySchema[i]]);
2705 : }
2706 : }
2707 :
2708 137 : if (m_bWriteBBoxStruct)
2709 : {
2710 25 : memset(&bboxStructSchemaXMin, 0, sizeof(bboxStructSchemaXMin));
2711 25 : memset(&bboxStructSchemaYMin, 0, sizeof(bboxStructSchemaYMin));
2712 25 : memset(&bboxStructSchemaXMax, 0, sizeof(bboxStructSchemaXMax));
2713 25 : memset(&bboxStructSchemaYMax, 0, sizeof(bboxStructSchemaYMax));
2714 :
2715 25 : bboxStructSchemaXMin.release = DummyFreeSchema;
2716 25 : bboxStructSchemaXMin.name = "xmin";
2717 25 : bboxStructSchemaXMin.format = "f"; // float32
2718 :
2719 25 : bboxStructSchemaYMin.release = DummyFreeSchema;
2720 25 : bboxStructSchemaYMin.name = "ymin";
2721 25 : bboxStructSchemaYMin.format = "f"; // float32
2722 :
2723 25 : bboxStructSchemaXMax.release = DummyFreeSchema;
2724 25 : bboxStructSchemaXMax.name = "xmax";
2725 25 : bboxStructSchemaXMax.format = "f"; // float32
2726 :
2727 25 : bboxStructSchemaYMax.release = DummyFreeSchema;
2728 25 : bboxStructSchemaYMax.name = "ymax";
2729 25 : bboxStructSchemaYMax.format = "f"; // float32
2730 :
2731 : try
2732 : {
2733 25 : constexpr int XMIN_IDX = 0;
2734 25 : constexpr int YMIN_IDX = 1;
2735 25 : constexpr int XMAX_IDX = 2;
2736 25 : constexpr int YMAX_IDX = 3;
2737 25 : bboxStructSchemaChildren[XMIN_IDX] = &bboxStructSchemaXMin;
2738 : // cppcheck-suppress objectIndex
2739 25 : bboxStructSchemaChildren[YMIN_IDX] = &bboxStructSchemaYMin;
2740 : // cppcheck-suppress objectIndex
2741 25 : bboxStructSchemaChildren[XMAX_IDX] = &bboxStructSchemaXMax;
2742 : // cppcheck-suppress objectIndex
2743 25 : bboxStructSchemaChildren[YMAX_IDX] = &bboxStructSchemaYMax;
2744 :
2745 25 : bboxStructArray.resize(nGeomFieldCount);
2746 25 : bboxStructSchema.resize(nGeomFieldCount);
2747 25 : bboxArrays.resize(nGeomFieldCount);
2748 25 : bboxArraysPtr.resize(nGeomFieldCount);
2749 25 : bboxBuffersPtr.resize(nGeomFieldCount);
2750 25 : bboxStructBuffersPtr.resize(nGeomFieldCount);
2751 25 : aabyBboxStructValidity.resize(nGeomFieldCount);
2752 50 : memset(bboxStructArray.data(), 0,
2753 25 : nGeomFieldCount * sizeof(bboxStructArray[0]));
2754 50 : memset(bboxStructSchema.data(), 0,
2755 25 : nGeomFieldCount * sizeof(bboxStructSchema[0]));
2756 50 : memset(bboxArrays.data(), 0,
2757 25 : nGeomFieldCount * sizeof(bboxArrays[0]));
2758 25 : aadfMinX.resize(nGeomFieldCount);
2759 25 : aadfMinY.resize(nGeomFieldCount);
2760 25 : aadfMaxX.resize(nGeomFieldCount);
2761 25 : aadfMaxY.resize(nGeomFieldCount);
2762 50 : for (int i = 0; i < nGeomFieldCount; ++i)
2763 : {
2764 25 : const bool bIsNullable = CPL_TO_BOOL(
2765 25 : m_poFeatureDefn->GetGeomFieldDefn(i)->IsNullable());
2766 25 : aadfMinX[i].reserve(static_cast<size_t>(array->length));
2767 25 : aadfMinY[i].reserve(static_cast<size_t>(array->length));
2768 25 : aadfMaxX[i].reserve(static_cast<size_t>(array->length));
2769 25 : aadfMaxY[i].reserve(static_cast<size_t>(array->length));
2770 25 : aabyBboxStructValidity[i].resize(
2771 25 : static_cast<size_t>(array->length + 7) / 8, 0xFF);
2772 :
2773 25 : bboxStructSchema[i].release = DummyFreeSchema;
2774 25 : bboxStructSchema[i].name = m_apoFieldsBBOX[i]->name().c_str();
2775 25 : bboxStructSchema[i].format = "+s"; // structure
2776 25 : bboxStructSchema[i].flags =
2777 25 : bIsNullable ? ARROW_FLAG_NULLABLE : 0;
2778 25 : bboxStructSchema[i].n_children = BBOX_SUBFIELD_COUNT;
2779 25 : bboxStructSchema[i].children = bboxStructSchemaChildren.data();
2780 :
2781 25 : constexpr int VALIDITY_ARRAY_IDX = 0;
2782 25 : constexpr int BBOX_SUBFIELD_FLOAT_VALUE_IDX = 1;
2783 25 : bboxBuffersPtr[i][XMIN_IDX][BBOX_SUBFIELD_FLOAT_VALUE_IDX] =
2784 25 : aadfMinX[i].data();
2785 25 : bboxBuffersPtr[i][YMIN_IDX][BBOX_SUBFIELD_FLOAT_VALUE_IDX] =
2786 25 : aadfMinY[i].data();
2787 25 : bboxBuffersPtr[i][XMAX_IDX][BBOX_SUBFIELD_FLOAT_VALUE_IDX] =
2788 25 : aadfMaxX[i].data();
2789 25 : bboxBuffersPtr[i][YMAX_IDX][BBOX_SUBFIELD_FLOAT_VALUE_IDX] =
2790 25 : aadfMaxY[i].data();
2791 :
2792 125 : for (int j = 0; j < BBOX_SUBFIELD_COUNT; ++j)
2793 : {
2794 100 : bboxBuffersPtr[i][j][VALIDITY_ARRAY_IDX] = nullptr;
2795 :
2796 100 : bboxArrays[i][j].release = DummyFreeArray;
2797 100 : bboxArrays[i][j].length = array->length;
2798 100 : bboxArrays[i][j].n_buffers = BBOX_SUBFIELD_BUFFER_COUNT;
2799 100 : bboxArrays[i][j].buffers = bboxBuffersPtr[i][j].data();
2800 :
2801 100 : bboxArraysPtr[i][j] = &bboxArrays[i][j];
2802 : }
2803 :
2804 25 : bboxStructArray[i].release = DummyFreeArray;
2805 25 : bboxStructArray[i].n_children = BBOX_SUBFIELD_COUNT;
2806 : // coverity[escape]
2807 25 : bboxStructArray[i].children = bboxArraysPtr[i].data();
2808 25 : bboxStructArray[i].length = array->length;
2809 25 : bboxStructArray[i].n_buffers = BBOX_STRUCT_BUFFER_COUNT;
2810 25 : bboxStructBuffersPtr[i][VALIDITY_ARRAY_IDX] =
2811 25 : bIsNullable ? aabyBboxStructValidity[i].data() : nullptr;
2812 : // coverity[escape]
2813 25 : bboxStructArray[i].buffers = bboxStructBuffersPtr[i].data();
2814 :
2815 25 : newSchemaChildren.emplace_back(&bboxStructSchema[i]);
2816 25 : newArrayChildren.emplace_back(&bboxStructArray[i]);
2817 : }
2818 : }
2819 0 : catch (const std::bad_alloc &)
2820 : {
2821 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
2822 : "Out of memory in "
2823 : "OGRArrowWriterLayer::WriteArrowBatchInternal()");
2824 0 : return false;
2825 : }
2826 : }
2827 :
2828 1760 : for (int i = 0; i < static_cast<int>(schema->n_children); ++i)
2829 : {
2830 1623 : if (!cpl::contains(oSetReferencedFieldsInArraySchema, i))
2831 : {
2832 4 : if (m_osFIDColumn.empty() &&
2833 2 : strcmp(schema->children[i]->name, pszFIDName) == 0)
2834 : {
2835 : // If the input data contains a FID column, but the output data
2836 : // does not, then ignore it.
2837 : }
2838 : else
2839 : {
2840 0 : CPLError(CE_Failure, CPLE_AppDefined,
2841 : "Found field '%s' in array schema that does not exist "
2842 : "in layer schema",
2843 0 : schema->children[i]->name);
2844 0 : return false;
2845 : }
2846 : }
2847 : }
2848 :
2849 : // ImportSchema() would release the schema, but we don't want that
2850 : // So copy the structure content into a local variable, and override its
2851 : // release callback to a no-op. This may be a bit fragile, but it doesn't
2852 : // look like ImportSchema implementation tries to access the C ArrowSchema
2853 : // after it has been called.
2854 137 : struct ArrowSchema lSchema = *schema;
2855 137 : schema = &lSchema;
2856 137 : CPL_IGNORE_RET_VAL(schema);
2857 :
2858 137 : lSchema.n_children = newSchemaChildren.size();
2859 137 : lSchema.children = newSchemaChildren.data();
2860 :
2861 137 : lSchema.release = DummyFreeSchema;
2862 274 : auto poSchemaResult = arrow::ImportSchema(&lSchema);
2863 137 : CPLAssert(lSchema.release == nullptr);
2864 137 : if (!poSchemaResult.ok())
2865 : {
2866 0 : CPLError(CE_Failure, CPLE_AppDefined, "ImportSchema() failed with %s",
2867 0 : poSchemaResult.status().message().c_str());
2868 0 : return false;
2869 : }
2870 274 : auto poSchema = *poSchemaResult;
2871 :
2872 : // Hack the array to use the new children we've computed above
2873 : // but make sure the original release() callback sees the original children
2874 : struct ArrayReleaser
2875 : {
2876 : struct ArrowArray ori_array{};
2877 :
2878 137 : explicit ArrayReleaser(struct ArrowArray *array)
2879 137 : {
2880 137 : memcpy(&ori_array, array, sizeof(*array));
2881 137 : array->release = ArrayReleaser::release;
2882 137 : array->private_data = this;
2883 137 : }
2884 :
2885 137 : static void release(struct ArrowArray *array)
2886 : {
2887 137 : struct ArrayReleaser *releaser =
2888 : static_cast<struct ArrayReleaser *>(array->private_data);
2889 137 : memcpy(array, &(releaser->ori_array), sizeof(*array));
2890 137 : CPLAssert(array->release != nullptr);
2891 137 : array->release(array);
2892 137 : CPLAssert(array->release == nullptr);
2893 137 : delete releaser;
2894 137 : }
2895 : };
2896 :
2897 : // Must be allocated on the heap, since ArrayReleaser::release() will be
2898 : // called after this method has ended.
2899 137 : ArrayReleaser *releaser = new ArrayReleaser(array);
2900 137 : array->private_data = releaser;
2901 137 : array->n_children = newArrayChildren.size();
2902 : // cppcheck-suppress autoVariables
2903 137 : array->children = newArrayChildren.data();
2904 :
2905 : // Process geometry columns:
2906 : // - if the output encoding is WKB, then just note the geometry type and
2907 : // envelope.
2908 : // - otherwise convert to the output encoding.
2909 137 : int nBuilderIdx = 0;
2910 137 : if (!m_osFIDColumn.empty())
2911 : {
2912 2 : nBuilderIdx++;
2913 : }
2914 : std::map<std::string, std::shared_ptr<arrow::Array>>
2915 274 : oMapGeomFieldNameToArray;
2916 274 : for (int i = 0; i < nGeomFieldCount; ++i, ++nBuilderIdx)
2917 : {
2918 : const char *pszThisGeomFieldName =
2919 137 : m_poFeatureDefn->GetGeomFieldDefn(i)->GetNameRef();
2920 137 : int nIdx = poSchema->GetFieldIndex(pszThisGeomFieldName);
2921 137 : if (nIdx < 0)
2922 : {
2923 6 : if (nGeomFieldCount == 1)
2924 6 : nIdx = poSchema->GetFieldIndex(pszSingleGeomFieldName);
2925 6 : if (nIdx < 0)
2926 : {
2927 0 : CPLError(CE_Failure, CPLE_AppDefined,
2928 : "Cannot find geometry field '%s' in schema",
2929 : pszThisGeomFieldName);
2930 0 : return false;
2931 : }
2932 : }
2933 :
2934 137 : if (strcmp(lSchema.children[nIdx]->format, "z") != 0 &&
2935 1 : strcmp(lSchema.children[nIdx]->format, "Z") != 0)
2936 : {
2937 0 : CPLError(CE_Failure, CPLE_AppDefined,
2938 : "Type of geometry field '%s' is not binary, but '%s'",
2939 0 : pszThisGeomFieldName, lSchema.children[nIdx]->format);
2940 0 : return false;
2941 : }
2942 :
2943 137 : const auto psGeomArray = array->children[nIdx];
2944 137 : const uint8_t *pabyValidity =
2945 137 : psGeomArray->null_count != 0
2946 137 : ? static_cast<const uint8_t *>(psGeomArray->buffers[0])
2947 : : nullptr;
2948 137 : const bool bUseOffsets32 =
2949 137 : (strcmp(lSchema.children[nIdx]->format, "z") == 0);
2950 137 : const uint32_t *panOffsets32 =
2951 137 : static_cast<const uint32_t *>(psGeomArray->buffers[1]) +
2952 137 : psGeomArray->offset;
2953 137 : const uint64_t *panOffsets64 =
2954 137 : static_cast<const uint64_t *>(psGeomArray->buffers[1]) +
2955 137 : psGeomArray->offset;
2956 137 : GByte *pabyData =
2957 137 : static_cast<GByte *>(const_cast<void *>(psGeomArray->buffers[2]));
2958 137 : OGREnvelope sEnvelope;
2959 137 : auto poBuilder = m_apoBuilders[nBuilderIdx].get();
2960 :
2961 712 : for (size_t iRow = 0; iRow < static_cast<size_t>(psGeomArray->length);
2962 : ++iRow)
2963 : {
2964 575 : bool bValidGeom = false;
2965 :
2966 1091 : if (!pabyValidity ||
2967 516 : TestBit(pabyValidity,
2968 516 : static_cast<size_t>(iRow + psGeomArray->offset)))
2969 : {
2970 447 : const auto nLen =
2971 447 : bUseOffsets32 ? static_cast<size_t>(panOffsets32[iRow + 1] -
2972 437 : panOffsets32[iRow])
2973 10 : : static_cast<size_t>(panOffsets64[iRow + 1] -
2974 10 : panOffsets64[iRow]);
2975 447 : GByte *pabyWkb =
2976 447 : pabyData + (bUseOffsets32
2977 437 : ? panOffsets32[iRow]
2978 10 : : static_cast<size_t>(panOffsets64[iRow]));
2979 447 : if (m_aeGeomEncoding[i] == OGRArrowGeomEncoding::WKB)
2980 : {
2981 179 : FixupWKBGeometryBeforeWriting(pabyWkb, nLen);
2982 :
2983 179 : uint32_t nType = 0;
2984 179 : bool bNeedSwap = false;
2985 179 : if (OGRWKBGetGeomType(pabyWkb, nLen, bNeedSwap, nType))
2986 : {
2987 179 : m_oSetWrittenGeometryTypes[i].insert(
2988 179 : static_cast<OGRwkbGeometryType>(nType));
2989 179 : if (OGRWKBGetBoundingBox(pabyWkb, nLen, sEnvelope))
2990 : {
2991 179 : bValidGeom = true;
2992 179 : m_aoEnvelopes[i].Merge(sEnvelope);
2993 :
2994 179 : if (m_bWriteBBoxStruct)
2995 : {
2996 51 : aadfMinX[i].push_back(
2997 51 : castToFloatDown(sEnvelope.MinX));
2998 51 : aadfMinY[i].push_back(
2999 51 : castToFloatDown(sEnvelope.MinY));
3000 51 : aadfMaxX[i].push_back(
3001 51 : castToFloatUp(sEnvelope.MaxX));
3002 51 : aadfMaxY[i].push_back(
3003 51 : castToFloatUp(sEnvelope.MaxY));
3004 : }
3005 : }
3006 : }
3007 : }
3008 : else
3009 : {
3010 268 : size_t nBytesConsumedOut = 0;
3011 268 : OGRGeometry *poGeometry = nullptr;
3012 268 : OGRGeometryFactory::createFromWkb(
3013 : pabyWkb, nullptr, &poGeometry, nLen, wkbVariantIso,
3014 : nBytesConsumedOut);
3015 268 : if (BuildGeometry(poGeometry, i, poBuilder) != OGRERR_NONE)
3016 : {
3017 0 : delete poGeometry;
3018 0 : return false;
3019 : }
3020 268 : bValidGeom = true;
3021 268 : if (m_bWriteBBoxStruct)
3022 : {
3023 0 : poGeometry->getEnvelope(&sEnvelope);
3024 0 : aadfMinX[i].push_back(castToFloatDown(sEnvelope.MinX));
3025 0 : aadfMinY[i].push_back(castToFloatDown(sEnvelope.MinY));
3026 0 : aadfMaxX[i].push_back(castToFloatUp(sEnvelope.MaxX));
3027 0 : aadfMaxY[i].push_back(castToFloatUp(sEnvelope.MaxY));
3028 : }
3029 268 : delete poGeometry;
3030 : }
3031 : }
3032 : else
3033 : {
3034 128 : if (m_aeGeomEncoding[i] != OGRArrowGeomEncoding::WKB)
3035 : {
3036 81 : if (BuildGeometry(nullptr, i, poBuilder) != OGRERR_NONE)
3037 0 : return false;
3038 : }
3039 : }
3040 :
3041 575 : if (!bValidGeom && m_bWriteBBoxStruct)
3042 : {
3043 18 : if ((bboxStructSchema[i].flags & ARROW_FLAG_NULLABLE))
3044 : {
3045 18 : bboxStructArray[i].null_count++;
3046 18 : aabyBboxStructValidity[i][iRow / 8] &=
3047 18 : ~(1 << static_cast<int>(iRow % 8));
3048 : }
3049 18 : aadfMinX[i].push_back(0.0f);
3050 18 : aadfMinY[i].push_back(0.0f);
3051 18 : aadfMaxX[i].push_back(0.0f);
3052 18 : aadfMaxY[i].push_back(0.0f);
3053 : }
3054 : }
3055 :
3056 137 : if (m_aeGeomEncoding[i] != OGRArrowGeomEncoding::WKB)
3057 : {
3058 0 : std::shared_ptr<arrow::Array> geomArray;
3059 81 : auto status = poBuilder->Finish(&geomArray);
3060 81 : if (!status.ok())
3061 : {
3062 0 : CPLError(CE_Failure, CPLE_AppDefined,
3063 : "builder::Finish() for field %s failed with %s",
3064 0 : pszThisGeomFieldName, status.message().c_str());
3065 0 : return false;
3066 : }
3067 162 : oMapGeomFieldNameToArray[pszThisGeomFieldName] =
3068 162 : std::move(geomArray);
3069 : }
3070 : }
3071 :
3072 : auto poRecordBatchResult =
3073 274 : arrow::ImportRecordBatch(array, std::move(poSchema));
3074 137 : if (!poRecordBatchResult.ok())
3075 : {
3076 0 : CPLError(CE_Failure, CPLE_AppDefined,
3077 : "ImportRecordBatch() failed with %s",
3078 0 : poRecordBatchResult.status().message().c_str());
3079 0 : return false;
3080 : }
3081 274 : auto poRecordBatch = *poRecordBatchResult;
3082 :
3083 137 : if (!(bRebuildBatch || !oMapGeomFieldNameToArray.empty()))
3084 : {
3085 1106 : for (int i = 0; i < m_poSchema->num_fields(); ++i)
3086 : {
3087 : const auto oIter =
3088 1056 : oMapGeomFieldNameToArray.find(m_poSchema->field(i)->name());
3089 1056 : auto l_array = (oIter != oMapGeomFieldNameToArray.end())
3090 0 : ? oIter->second
3091 2112 : : poRecordBatch->column(i);
3092 2112 : const auto schemaType = m_poSchema->field(i)->type();
3093 2112 : const auto arrayType = l_array->type();
3094 2104 : if (schemaType->id() != arrow::Type::EXTENSION &&
3095 1048 : arrayType->id() == arrow::Type::EXTENSION)
3096 : {
3097 0 : bRebuildBatch = true;
3098 : }
3099 1056 : else if (schemaType->id() != arrayType->id())
3100 : {
3101 3 : CPLDebug(
3102 : "Arrow",
3103 : "Field idx=%d name='%s', schema type=%s, array type=%s", i,
3104 1 : m_poSchema->field(i)->name().c_str(),
3105 2 : schemaType->ToString().c_str(),
3106 2 : arrayType->ToString().c_str());
3107 : }
3108 : }
3109 : }
3110 :
3111 : // below assertion commented out since it is not strictly necessary, but
3112 : // reflects what ImportRecordBatch() does.
3113 : // CPLAssert(array->release == nullptr);
3114 :
3115 : // We may need to reconstruct a final record batch that perfectly matches
3116 : // the expected schema.
3117 137 : if (bRebuildBatch || !oMapGeomFieldNameToArray.empty())
3118 : {
3119 87 : std::vector<std::shared_ptr<arrow::Array>> apoArrays;
3120 677 : for (int i = 0; i < m_poSchema->num_fields(); ++i)
3121 : {
3122 : const auto oIter =
3123 590 : oMapGeomFieldNameToArray.find(m_poSchema->field(i)->name());
3124 590 : if (oIter != oMapGeomFieldNameToArray.end())
3125 81 : apoArrays.emplace_back(oIter->second);
3126 : else
3127 509 : apoArrays.emplace_back(poRecordBatch->column(i));
3128 :
3129 590 : auto expectedFieldType = m_poSchema->field(i)->type();
3130 590 : if (expectedFieldType->id() == arrow::Type::EXTENSION)
3131 : {
3132 0 : auto extensionType = cpl::down_cast<arrow::ExtensionType *>(
3133 : expectedFieldType.get());
3134 0 : expectedFieldType = extensionType->storage_type();
3135 : }
3136 :
3137 590 : if (apoArrays.back()->type()->id() == arrow::Type::EXTENSION)
3138 : {
3139 0 : apoArrays.back() =
3140 0 : std::static_pointer_cast<arrow::ExtensionArray>(
3141 0 : apoArrays.back())
3142 0 : ->storage();
3143 : }
3144 :
3145 590 : if (apoArrays.back()->type()->id() != expectedFieldType->id())
3146 : {
3147 0 : CPLError(
3148 : CE_Failure, CPLE_AppDefined,
3149 : "Field '%s' of unexpected type. Got '%s', expected '%s'",
3150 0 : m_poSchema->field(i)->name().c_str(),
3151 0 : apoArrays.back()->type()->name().c_str(),
3152 0 : expectedFieldType->name().c_str());
3153 0 : return false;
3154 : }
3155 : }
3156 348 : poRecordBatchResult = arrow::RecordBatch::Make(
3157 261 : m_poSchema, poRecordBatch->num_rows(), std::move(apoArrays));
3158 87 : if (!poRecordBatchResult.ok())
3159 : {
3160 0 : CPLError(CE_Failure, CPLE_AppDefined,
3161 : "RecordBatch::Make() failed with %s",
3162 0 : poRecordBatchResult.status().message().c_str());
3163 0 : return false;
3164 : }
3165 87 : poRecordBatch = *poRecordBatchResult;
3166 : }
3167 :
3168 137 : if (writeBatch(poRecordBatch))
3169 : {
3170 137 : m_nFeatureCount += poRecordBatch->num_rows();
3171 137 : return true;
3172 : }
3173 0 : return false;
3174 : #endif
3175 : }
3176 :
3177 : #endif /* OGARROWWRITERLAYER_HPP_INCLUDED */
|