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