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 OGARROWLAYER_HPP_INCLUDED
14 : #define OGARROWLAYER_HPP_INCLUDED
15 :
16 : #include "ogr_arrow.h"
17 :
18 : #include "cpl_float.h"
19 : #include "cpl_json.h"
20 : #include "cpl_time.h"
21 : #include "ogrlayerarrow.h"
22 : #include "ogr_p.h"
23 : #include "ogr_swq.h"
24 : #include "ogr_wkb.h"
25 :
26 : #include <algorithm>
27 : #include <cassert>
28 : #include <cinttypes>
29 : #include <limits>
30 : #include <string_view>
31 :
32 : #define SWQ_ISNOTNULL (-SWQ_ISNULL)
33 :
34 : #if defined(__clang__)
35 : #pragma clang diagnostic push
36 : #pragma clang diagnostic ignored "-Wweak-vtables"
37 : #endif
38 :
39 : inline IOGRArrowLayer::~IOGRArrowLayer() = default;
40 :
41 : /************************************************************************/
42 : /* OGRArrowLayer() */
43 : /************************************************************************/
44 :
45 1966 : inline OGRArrowLayer::OGRArrowLayer(OGRArrowDataset *poDS,
46 : const char *pszLayerName,
47 1966 : bool bListsAsStringJson)
48 : : m_poArrowDS(poDS), m_bListsAsStringJson(bListsAsStringJson),
49 1966 : m_poMemoryPool(poDS->GetMemoryPool())
50 : {
51 1966 : m_poFeatureDefn = new OGRFeatureDefn(pszLayerName);
52 1966 : m_poFeatureDefn->SetGeomType(wkbNone);
53 1966 : m_poFeatureDefn->Reference();
54 1966 : SetDescription(pszLayerName);
55 1966 : }
56 :
57 : /************************************************************************/
58 : /* ~OGRFeatherLayer() */
59 : /************************************************************************/
60 :
61 1963 : inline OGRArrowLayer::~OGRArrowLayer()
62 : {
63 1963 : if (m_sCachedSchema.release)
64 59 : m_sCachedSchema.release(&m_sCachedSchema);
65 :
66 1963 : CPLDebug("ARROW", "Memory pool: bytes_allocated = %" PRId64,
67 1963 : m_poMemoryPool->bytes_allocated());
68 1963 : CPLDebug("ARROW", "Memory pool: max_memory = %" PRId64,
69 1963 : m_poMemoryPool->max_memory());
70 1963 : m_poFeatureDefn->Release();
71 1963 : }
72 :
73 : /************************************************************************/
74 : /* LoadGDALSchema() */
75 : /************************************************************************/
76 :
77 : inline std::map<std::string, std::unique_ptr<OGRFieldDefn>>
78 1966 : OGRArrowLayer::LoadGDALSchema(const arrow::KeyValueMetadata *kv_metadata)
79 : {
80 : std::map<std::string, std::unique_ptr<OGRFieldDefn>>
81 1966 : oMapFieldNameToGDALSchemaFieldDefn;
82 2009 : if (kv_metadata && kv_metadata->Contains("gdal:schema") &&
83 43 : CPLTestBool(CPLGetConfigOption(
84 2009 : ("OGR_" + GetDriverUCName() + "_READ_GDAL_SCHEMA").c_str(), "YES")))
85 : {
86 86 : auto gdalSchema = kv_metadata->Get("gdal:schema");
87 43 : if (gdalSchema.ok())
88 : {
89 43 : CPLDebug(GetDriverUCName().c_str(), "gdal:schema = %s",
90 : gdalSchema->c_str());
91 86 : CPLJSONDocument oDoc;
92 43 : if (oDoc.LoadMemory(*gdalSchema))
93 : {
94 86 : auto oRoot = oDoc.GetRoot();
95 :
96 43 : m_osFIDColumn = oRoot.GetString("fid");
97 :
98 129 : auto oColumns = oRoot.GetObj("columns");
99 43 : if (oColumns.IsValid())
100 : {
101 432 : for (const auto &oColumn : oColumns.GetChildren())
102 : {
103 778 : const auto osName = oColumn.GetName();
104 1167 : const auto osType = oColumn.GetString("type");
105 1167 : const auto osSubType = oColumn.GetString("subtype");
106 : auto poFieldDefn = std::make_unique<OGRFieldDefn>(
107 778 : osName.c_str(), OFTString);
108 2327 : for (int iType = 0;
109 2327 : iType <= static_cast<int>(OFTMaxType); iType++)
110 : {
111 2327 : if (EQUAL(osType.c_str(),
112 : OGRFieldDefn::GetFieldTypeName(
113 : static_cast<OGRFieldType>(iType))))
114 : {
115 389 : poFieldDefn->SetType(
116 : static_cast<OGRFieldType>(iType));
117 389 : break;
118 : }
119 : }
120 389 : if (!osSubType.empty())
121 : {
122 500 : for (int iSubType = 0;
123 500 : iSubType <= static_cast<int>(OFSTMaxSubType);
124 : iSubType++)
125 : {
126 500 : if (EQUAL(osSubType.c_str(),
127 : OGRFieldDefn::GetFieldSubTypeName(
128 : static_cast<OGRFieldSubType>(
129 : iSubType))))
130 : {
131 112 : poFieldDefn->SetSubType(
132 : static_cast<OGRFieldSubType>(iSubType));
133 112 : break;
134 : }
135 : }
136 : }
137 389 : poFieldDefn->SetWidth(oColumn.GetInteger("width"));
138 389 : poFieldDefn->SetPrecision(
139 : oColumn.GetInteger("precision"));
140 :
141 : const auto osAlternativeName =
142 1167 : oColumn.GetString("alternative_name");
143 389 : if (!osAlternativeName.empty())
144 2 : poFieldDefn->SetAlternativeName(
145 : osAlternativeName.c_str());
146 :
147 1167 : const auto osComment = oColumn.GetString("comment");
148 389 : if (!osComment.empty())
149 3 : poFieldDefn->SetComment(osComment);
150 :
151 389 : oMapFieldNameToGDALSchemaFieldDefn[osName] =
152 778 : std::move(poFieldDefn);
153 : }
154 : }
155 : }
156 : }
157 : }
158 1966 : return oMapFieldNameToGDALSchemaFieldDefn;
159 : }
160 :
161 : /************************************************************************/
162 : /* LoadGDALMetadata() */
163 : /************************************************************************/
164 :
165 : inline void
166 1418 : OGRArrowLayer::LoadGDALMetadata(const arrow::KeyValueMetadata *kv_metadata)
167 : {
168 1418 : if (kv_metadata && kv_metadata->Contains("gdal:metadata"))
169 : {
170 22 : auto gdalMetadata = kv_metadata->Get("gdal:metadata");
171 11 : if (gdalMetadata.ok())
172 : {
173 22 : CPLJSONDocument oDoc;
174 11 : if (oDoc.LoadMemory(*gdalMetadata))
175 : {
176 22 : auto oRoot = oDoc.GetRoot();
177 24 : for (const auto &oDomain : oRoot.GetChildren())
178 : {
179 14 : if (STARTS_WITH(oDomain.GetName().c_str(), "json:") &&
180 1 : oDomain.GetType() == CPLJSONObject::Type::Object)
181 : {
182 1 : char **papszMD = nullptr;
183 1 : papszMD = CSLAddString(
184 : papszMD,
185 2 : oDomain.Format(CPLJSONObject::PrettyFormat::Plain)
186 : .c_str());
187 1 : SetMetadata(papszMD, oDomain.GetName().c_str());
188 1 : CSLDestroy(papszMD);
189 : }
190 13 : else if (STARTS_WITH(oDomain.GetName().c_str(), "xml:") &&
191 1 : oDomain.GetType() == CPLJSONObject::Type::String)
192 : {
193 1 : char **papszMD = nullptr;
194 : papszMD =
195 1 : CSLAddString(papszMD, oDomain.ToString().c_str());
196 1 : SetMetadata(papszMD, oDomain.GetName().c_str());
197 1 : CSLDestroy(papszMD);
198 : }
199 : else
200 : {
201 22 : for (const auto &oItem : oDomain.GetChildren())
202 : {
203 11 : if (oItem.GetType() == CPLJSONObject::Type::String)
204 : {
205 22 : SetMetadataItem(oItem.GetName().c_str(),
206 22 : oItem.ToString().c_str(),
207 22 : oDomain.GetName().c_str());
208 : }
209 : }
210 : }
211 : }
212 : }
213 : }
214 : }
215 1418 : }
216 :
217 : /************************************************************************/
218 : /* IsIntegerArrowType() */
219 : /************************************************************************/
220 :
221 9033 : inline bool OGRArrowLayer::IsIntegerArrowType(arrow::Type::type typeId)
222 : {
223 8630 : return typeId == arrow::Type::INT8 || typeId == arrow::Type::UINT8 ||
224 7824 : typeId == arrow::Type::INT16 || typeId == arrow::Type::UINT16 ||
225 6606 : typeId == arrow::Type::INT32 || typeId == arrow::Type::UINT32 ||
226 17663 : typeId == arrow::Type::INT64 || typeId == arrow::Type::UINT64;
227 : }
228 :
229 : /************************************************************************/
230 : /* IsHandledListOrMapType() */
231 : /************************************************************************/
232 :
233 9024 : inline bool OGRArrowLayer::IsHandledListOrMapType(
234 : const std::shared_ptr<arrow::DataType> &valueType)
235 : {
236 9024 : const auto itemTypeId = valueType->id();
237 8621 : return itemTypeId == arrow::Type::BOOL || IsIntegerArrowType(itemTypeId) ||
238 5349 : itemTypeId == arrow::Type::HALF_FLOAT ||
239 4946 : itemTypeId == arrow::Type::FLOAT ||
240 4543 : itemTypeId == arrow::Type::DOUBLE ||
241 : #if ARROW_VERSION_MAJOR >= 18
242 4543 : itemTypeId == arrow::Type::DECIMAL32 ||
243 4543 : itemTypeId == arrow::Type::DECIMAL64 ||
244 : #endif
245 3766 : itemTypeId == arrow::Type::DECIMAL128 ||
246 3741 : itemTypeId == arrow::Type::DECIMAL256 ||
247 1758 : itemTypeId == arrow::Type::STRING ||
248 1733 : itemTypeId == arrow::Type::LARGE_STRING ||
249 : #if ARROW_VERSION_MAJOR >= 15
250 1729 : itemTypeId == arrow::Type::STRING_VIEW ||
251 : #endif
252 1728 : itemTypeId == arrow::Type::BINARY ||
253 : #if ARROW_VERSION_MAJOR >= 15
254 1728 : itemTypeId == arrow::Type::BINARY_VIEW ||
255 : #endif
256 1268 : itemTypeId == arrow::Type::STRUCT ||
257 26 : (itemTypeId == arrow::Type::MAP &&
258 13 : IsHandledMapType(
259 27937 : std::static_pointer_cast<arrow::MapType>(valueType))) ||
260 50 : ((itemTypeId == arrow::Type::LIST ||
261 25 : itemTypeId == arrow::Type::LARGE_LIST ||
262 2510 : itemTypeId == arrow::Type::FIXED_SIZE_LIST) &&
263 1255 : IsHandledListType(
264 19303 : std::static_pointer_cast<arrow::BaseListType>(valueType)));
265 : }
266 :
267 : /************************************************************************/
268 : /* IsHandledListType() */
269 : /************************************************************************/
270 :
271 1760 : inline bool OGRArrowLayer::IsHandledListType(
272 : const std::shared_ptr<arrow::BaseListType> &listType)
273 : {
274 1760 : return IsHandledListOrMapType(listType->value_type());
275 : }
276 :
277 : /************************************************************************/
278 : /* IsHandledMapType() */
279 : /************************************************************************/
280 :
281 : inline bool
282 7264 : OGRArrowLayer::IsHandledMapType(const std::shared_ptr<arrow::MapType> &mapType)
283 : {
284 7264 : const auto typeId = mapType->key_type()->id();
285 : return (typeId == arrow::Type::STRING
286 : #if ARROW_VERSION_MAJOR >= 15
287 2 : || typeId == arrow::Type::STRING_VIEW
288 : #endif
289 14528 : ) &&
290 14528 : IsHandledListOrMapType(mapType->item_type());
291 : }
292 :
293 : /************************************************************************/
294 : /* GetFieldExtensionName() */
295 : /************************************************************************/
296 :
297 : inline static std::string
298 37521 : GetFieldExtensionName(const std::shared_ptr<arrow::Field> &field,
299 : std::shared_ptr<arrow::DataType> &type,
300 : const char *pszDebugKey)
301 : {
302 37521 : std::string osExtensionName;
303 37521 : if (type->id() == arrow::Type::EXTENSION)
304 : {
305 176 : auto extensionType = cpl::down_cast<arrow::ExtensionType *>(type.get());
306 176 : osExtensionName = extensionType->extension_name();
307 176 : type = extensionType->storage_type();
308 : }
309 37357 : else if (const auto &field_kv_metadata = field->metadata())
310 : {
311 24 : auto extension_name = field_kv_metadata->Get(ARROW_EXTENSION_NAME_KEY);
312 12 : if (extension_name.ok())
313 : {
314 10 : osExtensionName = *extension_name;
315 : }
316 : }
317 :
318 37707 : if (!osExtensionName.empty() &&
319 37707 : osExtensionName != EXTENSION_NAME_ARROW_JSON &&
320 10 : osExtensionName != EXTENSION_NAME_ARROW_TIMESTAMP_WITH_OFFSET)
321 : {
322 6 : CPLDebug(pszDebugKey,
323 : "Dealing with field %s of extension type %s as %s",
324 3 : field->name().c_str(), osExtensionName.c_str(),
325 6 : type->ToString().c_str());
326 : }
327 37521 : return osExtensionName;
328 : }
329 :
330 : /************************************************************************/
331 : /* MapArrowTypeToOGR() */
332 : /************************************************************************/
333 :
334 36638 : inline bool OGRArrowLayer::MapArrowTypeToOGR(
335 : const std::shared_ptr<arrow::DataType> &typeIn,
336 : const std::shared_ptr<arrow::Field> &field, OGRFieldDefn &oField,
337 : OGRFieldType &eType, OGRFieldSubType &eSubType,
338 : const std::vector<int> &path,
339 : const std::map<std::string, std::unique_ptr<OGRFieldDefn>>
340 : &oMapFieldNameToGDALSchemaFieldDefn)
341 : {
342 36638 : bool bTypeOK = false;
343 :
344 73276 : std::shared_ptr<arrow::DataType> type(typeIn);
345 : const std::string osExtensionName =
346 36638 : GetFieldExtensionName(field, type, GetDriverUCName().c_str());
347 36638 : switch (type->id())
348 : {
349 384 : case arrow::Type::NA:
350 384 : break;
351 :
352 414 : case arrow::Type::BOOL:
353 414 : bTypeOK = true;
354 414 : eType = OFTInteger;
355 414 : eSubType = OFSTBoolean;
356 414 : break;
357 1200 : case arrow::Type::UINT8:
358 : case arrow::Type::INT8:
359 : case arrow::Type::UINT16:
360 1200 : bTypeOK = true;
361 1200 : eType = OFTInteger;
362 1200 : break;
363 408 : case arrow::Type::INT16:
364 408 : bTypeOK = true;
365 408 : eType = OFTInteger;
366 408 : eSubType = OFSTInt16;
367 408 : break;
368 25 : case arrow::Type::UINT32:
369 25 : bTypeOK = true;
370 25 : eType = OFTInteger64;
371 25 : break;
372 1430 : case arrow::Type::INT32:
373 1430 : bTypeOK = true;
374 1430 : eType = OFTInteger;
375 1430 : break;
376 400 : case arrow::Type::UINT64:
377 400 : bTypeOK = true;
378 400 : eType = OFTReal; // potential loss
379 400 : break;
380 1744 : case arrow::Type::INT64:
381 1744 : bTypeOK = true;
382 1744 : eType = OFTInteger64;
383 1744 : break;
384 526 : case arrow::Type::HALF_FLOAT: // should use OFSTFloat16 if we had it
385 : case arrow::Type::FLOAT:
386 526 : bTypeOK = true;
387 526 : eType = OFTReal;
388 526 : eSubType = OFSTFloat32;
389 526 : break;
390 901 : case arrow::Type::DOUBLE:
391 901 : bTypeOK = true;
392 901 : eType = OFTReal;
393 901 : break;
394 2848 : case arrow::Type::STRING:
395 : case arrow::Type::LARGE_STRING:
396 : #if ARROW_VERSION_MAJOR >= 15
397 : case arrow::Type::STRING_VIEW:
398 : #endif
399 2848 : bTypeOK = true;
400 2848 : eType = OFTString;
401 2848 : if (osExtensionName == EXTENSION_NAME_ARROW_JSON)
402 176 : eSubType = OFSTJSON;
403 2848 : break;
404 841 : case arrow::Type::BINARY:
405 : case arrow::Type::LARGE_BINARY:
406 : #if ARROW_VERSION_MAJOR >= 15
407 : case arrow::Type::BINARY_VIEW:
408 : #endif
409 841 : bTypeOK = true;
410 841 : eType = OFTBinary;
411 841 : break;
412 408 : case arrow::Type::FIXED_SIZE_BINARY:
413 408 : bTypeOK = true;
414 408 : eType = OFTBinary;
415 408 : oField.SetWidth(
416 816 : std::static_pointer_cast<arrow::FixedSizeBinaryType>(type)
417 408 : ->byte_width());
418 408 : break;
419 :
420 861 : case arrow::Type::DATE32:
421 : case arrow::Type::DATE64:
422 861 : bTypeOK = true;
423 861 : eType = OFTDate;
424 861 : break;
425 :
426 2498 : case arrow::Type::TIMESTAMP:
427 : {
428 2498 : bTypeOK = true;
429 : const auto timestampType =
430 2498 : static_cast<arrow::TimestampType *>(type.get());
431 2498 : eType = OFTDateTime;
432 2498 : const auto &osTZ = timestampType->timezone();
433 2498 : int nTZFlag = OGRTimezoneToTZFlag(osTZ.c_str(), false);
434 2498 : if (nTZFlag == OGR_TZFLAG_UNKNOWN && !osTZ.empty())
435 : {
436 0 : CPLDebug(GetDriverUCName().c_str(),
437 : "Field %s has unrecognized timezone %s. "
438 : "UTC datetime will be used instead.",
439 0 : field->name().c_str(), osTZ.c_str());
440 0 : nTZFlag = OGR_TZFLAG_UTC;
441 : }
442 2498 : oField.SetTZFlag(nTZFlag);
443 2498 : break;
444 : }
445 :
446 816 : case arrow::Type::TIME32:
447 816 : bTypeOK = true;
448 816 : eType = OFTTime;
449 816 : break;
450 :
451 800 : case arrow::Type::TIME64:
452 800 : bTypeOK = true;
453 800 : eType = OFTInteger64; // our OFTTime doesn't have micro or
454 : // nanosecond accuracy
455 800 : break;
456 :
457 : #if ARROW_VERSION_MAJOR >= 18
458 828 : case arrow::Type::DECIMAL32:
459 : case arrow::Type::DECIMAL64:
460 : #endif
461 : case arrow::Type::DECIMAL128:
462 : case arrow::Type::DECIMAL256:
463 : {
464 828 : bTypeOK = true;
465 : const auto decimalType =
466 1656 : std::static_pointer_cast<arrow::DecimalType>(type);
467 828 : eType = OFTReal;
468 828 : oField.SetWidth(decimalType->precision());
469 828 : oField.SetPrecision(decimalType->scale());
470 828 : break;
471 : }
472 :
473 12055 : case arrow::Type::LIST:
474 : case arrow::Type::LARGE_LIST:
475 : case arrow::Type::FIXED_SIZE_LIST:
476 : {
477 12055 : bTypeOK = true;
478 24110 : auto listType = std::static_pointer_cast<arrow::BaseListType>(type);
479 12055 : switch (listType->value_type()->id())
480 : {
481 816 : case arrow::Type::BOOL:
482 816 : eType = OFTIntegerList;
483 816 : eSubType = OFSTBoolean;
484 816 : break;
485 4089 : case arrow::Type::UINT8:
486 : case arrow::Type::INT8:
487 : case arrow::Type::UINT16:
488 : case arrow::Type::INT16:
489 : case arrow::Type::INT32:
490 4089 : eType = OFTIntegerList;
491 4089 : break;
492 48 : case arrow::Type::UINT32:
493 48 : eType = OFTInteger64List;
494 48 : break;
495 800 : case arrow::Type::UINT64:
496 800 : eType = OFTRealList; // potential loss
497 800 : break;
498 1996 : case arrow::Type::INT64:
499 1996 : eType = OFTInteger64List;
500 1996 : break;
501 846 : case arrow::Type::HALF_FLOAT: // should use OFSTFloat16 if we
502 : // had it
503 : case arrow::Type::FLOAT:
504 846 : eType = OFTRealList;
505 846 : eSubType = OFSTFloat32;
506 846 : break;
507 1670 : case arrow::Type::DOUBLE:
508 : #if ARROW_VERSION_MAJOR >= 18
509 : case arrow::Type::DECIMAL32:
510 : case arrow::Type::DECIMAL64:
511 : #endif
512 : case arrow::Type::DECIMAL128:
513 : case arrow::Type::DECIMAL256:
514 1670 : eType = OFTRealList;
515 1670 : break;
516 1285 : case arrow::Type::STRING:
517 : case arrow::Type::LARGE_STRING:
518 : case arrow::Type::BINARY:
519 : #if ARROW_VERSION_MAJOR >= 15
520 : case arrow::Type::STRING_VIEW:
521 : #endif
522 1285 : eType = OFTStringList;
523 1285 : break;
524 505 : default:
525 : {
526 505 : if (IsHandledListType(listType))
527 : {
528 505 : eType = OFTString;
529 505 : eSubType = OFSTJSON;
530 : }
531 : else
532 : {
533 0 : bTypeOK = false;
534 0 : CPLError(CE_Warning, CPLE_AppDefined,
535 : "Field %s of unhandled type %s ignored",
536 0 : field->name().c_str(),
537 0 : type->ToString().c_str());
538 : }
539 505 : break;
540 : }
541 : }
542 :
543 12055 : if (bTypeOK && m_bListsAsStringJson)
544 : {
545 61 : eType = OFTString;
546 61 : eSubType = OFSTJSON;
547 : }
548 :
549 12055 : break;
550 : }
551 :
552 7251 : case arrow::Type::MAP:
553 : {
554 7251 : bTypeOK = true;
555 14502 : auto mapType = std::static_pointer_cast<arrow::MapType>(type);
556 7251 : if (IsHandledMapType(mapType))
557 : {
558 7251 : eType = OFTString;
559 7251 : eSubType = OFSTJSON;
560 : }
561 : else
562 : {
563 0 : bTypeOK = false;
564 0 : CPLError(CE_Warning, CPLE_AppDefined,
565 : "Field %s of unhandled type %s ignored",
566 0 : field->name().c_str(), type->ToString().c_str());
567 : }
568 7251 : break;
569 : }
570 :
571 0 : case arrow::Type::STRUCT:
572 : // should be handled by specialized code
573 0 : CPLAssert(false);
574 : break;
575 :
576 : // unhandled types
577 :
578 0 : case arrow::Type::INTERVAL_MONTHS:
579 : case arrow::Type::INTERVAL_DAY_TIME:
580 : case arrow::Type::SPARSE_UNION:
581 : case arrow::Type::DENSE_UNION:
582 : case arrow::Type::DICTIONARY:
583 : case arrow::Type::EXTENSION:
584 : case arrow::Type::DURATION:
585 : case arrow::Type::INTERVAL_MONTH_DAY_NANO:
586 : #if ARROW_VERSION_MAJOR >= 12
587 : case arrow::Type::RUN_END_ENCODED:
588 : #endif
589 : #if ARROW_VERSION_MAJOR >= 15
590 : case arrow::Type::LIST_VIEW:
591 : case arrow::Type::LARGE_LIST_VIEW:
592 : #endif
593 : case arrow::Type::MAX_ID:
594 : {
595 0 : CPLError(CE_Warning, CPLE_AppDefined,
596 : "Field %s of unhandled type %s ignored",
597 0 : field->name().c_str(), type->ToString().c_str());
598 0 : break;
599 : }
600 : }
601 :
602 36638 : if (bTypeOK)
603 : {
604 : const auto oIter =
605 36254 : oMapFieldNameToGDALSchemaFieldDefn.find(field->name());
606 36254 : oField.SetType(eType);
607 36254 : if (oIter != oMapFieldNameToGDALSchemaFieldDefn.end())
608 : {
609 389 : const auto &poGDALFieldDefn = oIter->second;
610 389 : if (poGDALFieldDefn->GetType() == eType)
611 : {
612 389 : if (eSubType == OFSTNone)
613 : {
614 277 : eSubType = poGDALFieldDefn->GetSubType();
615 : }
616 112 : else if (eSubType != poGDALFieldDefn->GetSubType())
617 : {
618 0 : CPLDebug(
619 0 : GetDriverUCName().c_str(),
620 : "Field subtype inferred from Parquet/Arrow schema is "
621 : "%s, "
622 : "whereas the one in gdal:schema is %s. "
623 : "Using the former one.",
624 : OGR_GetFieldSubTypeName(eSubType),
625 : OGR_GetFieldSubTypeName(poGDALFieldDefn->GetSubType()));
626 : }
627 : }
628 : else
629 : {
630 0 : CPLDebug(GetDriverUCName().c_str(),
631 : "Field type inferred from Parquet/Arrow schema is %s, "
632 : "whereas the one in gdal:schema is %s. "
633 : "Using the former one.",
634 : OGR_GetFieldTypeName(eType),
635 : OGR_GetFieldTypeName(poGDALFieldDefn->GetType()));
636 : }
637 389 : if (poGDALFieldDefn->GetWidth() > 0)
638 42 : oField.SetWidth(poGDALFieldDefn->GetWidth());
639 389 : if (poGDALFieldDefn->GetPrecision() > 0)
640 18 : oField.SetPrecision(poGDALFieldDefn->GetPrecision());
641 389 : if (poGDALFieldDefn->GetAlternativeNameRef()[0])
642 2 : oField.SetAlternativeName(
643 : poGDALFieldDefn->GetAlternativeNameRef());
644 389 : if (!poGDALFieldDefn->GetComment().empty())
645 3 : oField.SetComment(poGDALFieldDefn->GetComment());
646 : }
647 36254 : oField.SetSubType(eSubType);
648 36254 : oField.SetNullable(field->nullable());
649 36254 : m_poFeatureDefn->AddFieldDefn(&oField);
650 36254 : m_anMapFieldIndexToArrowColumn.push_back(path);
651 : }
652 :
653 73276 : return bTypeOK;
654 : }
655 :
656 : /************************************************************************/
657 : /* CreateFieldFromSchema() */
658 : /************************************************************************/
659 :
660 10459 : inline void OGRArrowLayer::CreateFieldFromSchema(
661 : const std::shared_ptr<arrow::Field> &field, const std::vector<int> &path,
662 : const std::map<std::string, std::unique_ptr<OGRFieldDefn>>
663 : &oMapFieldNameToGDALSchemaFieldDefn)
664 : {
665 20918 : OGRFieldDefn oField(field->name().c_str(), OFTString);
666 10459 : OGRFieldType eType = OFTString;
667 10459 : OGRFieldSubType eSubType = OFSTNone;
668 10459 : bool bTypeOK = true;
669 :
670 20918 : auto type = field->type();
671 10459 : if (type->id() == arrow::Type::DICTIONARY && path.size() == 1)
672 : {
673 : const auto dictionaryType =
674 220 : std::static_pointer_cast<arrow::DictionaryType>(field->type());
675 220 : auto indexType = dictionaryType->index_type();
676 220 : if (dictionaryType->value_type()->id() == arrow::Type::STRING &&
677 110 : IsIntegerArrowType(indexType->id()))
678 : {
679 220 : std::string osDomainName(field->name() + "Domain");
680 110 : m_poArrowDS->RegisterDomainName(osDomainName,
681 110 : m_poFeatureDefn->GetFieldCount());
682 110 : oField.SetDomainName(osDomainName);
683 110 : type = std::move(indexType);
684 : }
685 : else
686 : {
687 0 : bTypeOK = false;
688 : }
689 : }
690 :
691 10459 : if (type->id() == arrow::Type::STRUCT)
692 : {
693 424 : const auto subfields = field->Flatten();
694 : const std::string osExtensionName =
695 424 : GetFieldExtensionName(field, type, GetDriverUCName().c_str());
696 2 : if (osExtensionName == EXTENSION_NAME_ARROW_TIMESTAMP_WITH_OFFSET &&
697 4 : subfields.size() == 2 &&
698 2 : subfields[0]->name() ==
699 216 : field->name() + "." + ATSWO_TIMESTAMP_FIELD_NAME &&
700 4 : subfields[0]->type()->id() == arrow::Type::TIMESTAMP &&
701 2 : subfields[1]->name() ==
702 216 : field->name() + "." + ATSWO_OFFSET_MINUTES_FIELD_NAME &&
703 2 : subfields[1]->type()->id() == arrow::Type::INT16)
704 : {
705 2 : oField.SetType(OFTDateTime);
706 2 : oField.SetTZFlag(OGR_TZFLAG_MIXED_TZ);
707 2 : oField.SetNullable(field->nullable());
708 2 : m_poFeatureDefn->AddFieldDefn(&oField);
709 2 : m_anMapFieldIndexToArrowColumn.push_back(path);
710 : }
711 : else
712 : {
713 420 : auto newpath = path;
714 210 : newpath.push_back(0);
715 946 : for (int j = 0; j < static_cast<int>(subfields.size()); j++)
716 : {
717 736 : const auto &subfield = subfields[j];
718 736 : newpath.back() = j;
719 736 : CreateFieldFromSchema(subfield, newpath,
720 : oMapFieldNameToGDALSchemaFieldDefn);
721 : }
722 : }
723 : }
724 10247 : else if (bTypeOK)
725 : {
726 10247 : MapArrowTypeToOGR(type, field, oField, eType, eSubType, path,
727 : oMapFieldNameToGDALSchemaFieldDefn);
728 : }
729 10459 : }
730 :
731 : /************************************************************************/
732 : /* BuildDomainFromBatch() */
733 : /************************************************************************/
734 :
735 35 : inline std::unique_ptr<OGRFieldDomain> OGRArrowLayer::BuildDomainFromBatch(
736 : const std::string &osDomainName,
737 : const std::shared_ptr<arrow::RecordBatch> &poBatch, int iCol) const
738 : {
739 70 : const auto array = poBatch->column(iCol);
740 70 : auto castArray = std::static_pointer_cast<arrow::DictionaryArray>(array);
741 70 : auto dict = castArray->dictionary();
742 35 : CPLAssert(dict->type_id() == arrow::Type::STRING);
743 35 : OGRFieldType eType = OFTInteger;
744 35 : const auto indexTypeId = castArray->dict_type()->index_type()->id();
745 35 : if (indexTypeId == arrow::Type::UINT32 ||
746 35 : indexTypeId == arrow::Type::UINT64 || indexTypeId == arrow::Type::INT64)
747 0 : eType = OFTInteger64;
748 70 : auto values = std::static_pointer_cast<arrow::StringArray>(dict);
749 70 : std::vector<OGRCodedValue> asValues;
750 35 : if (values->length() > INT_MAX)
751 : {
752 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
753 : "BuildDomainFromBatch(): too many values");
754 0 : return nullptr;
755 : }
756 : try
757 : {
758 35 : asValues.reserve(static_cast<size_t>(values->length()));
759 : }
760 0 : catch (const std::bad_alloc &)
761 : {
762 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
763 : "BuildDomainFromBatch(): out of memory");
764 0 : return nullptr;
765 : }
766 140 : for (int i = 0; i < static_cast<int>(values->length()); ++i)
767 : {
768 105 : if (!values->IsNull(i))
769 : {
770 : OGRCodedValue val;
771 105 : val.pszCode = CPLStrdup(CPLSPrintf("%d", i));
772 105 : val.pszValue = CPLStrdup(values->GetString(i).c_str());
773 105 : asValues.emplace_back(val);
774 : }
775 : }
776 70 : return std::make_unique<OGRCodedFieldDomain>(
777 105 : osDomainName, std::string(), eType, OFSTNone, std::move(asValues));
778 : }
779 :
780 : /************************************************************************/
781 : /* GetStorageArray() */
782 : /************************************************************************/
783 :
784 191214 : static const arrow::Array *GetStorageArray(const arrow::Array *array)
785 : {
786 191214 : if (array->type_id() == arrow::Type::EXTENSION)
787 : {
788 : auto extensionArray =
789 1907 : cpl::down_cast<const arrow::ExtensionArray *>(array);
790 1907 : array = extensionArray->storage().get();
791 : }
792 191214 : return array;
793 : }
794 :
795 : /************************************************************************/
796 : /* ComputeGeometryColumnTypeProcessBatch() */
797 : /************************************************************************/
798 :
799 537 : inline OGRwkbGeometryType OGRArrowLayer::ComputeGeometryColumnTypeProcessBatch(
800 : const std::shared_ptr<arrow::RecordBatch> &poBatch, int iGeomCol,
801 : int iBatchCol, OGRwkbGeometryType eGeomType) const
802 : {
803 1074 : const auto array = poBatch->column(iBatchCol);
804 537 : const auto storageArray = GetStorageArray(array.get());
805 : const auto castBinaryArray =
806 537 : (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKB)
807 537 : ? dynamic_cast<const arrow::BinaryArray *>(storageArray)
808 537 : : nullptr;
809 : const auto castLargeBinaryArray =
810 537 : (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKB)
811 537 : ? dynamic_cast<const arrow::LargeBinaryArray *>(storageArray)
812 537 : : nullptr;
813 : const auto castStringArray =
814 537 : (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKT)
815 537 : ? dynamic_cast<const arrow::StringArray *>(storageArray)
816 537 : : nullptr;
817 : const auto castLargeStringArray =
818 537 : (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKT)
819 537 : ? dynamic_cast<const arrow::LargeStringArray *>(storageArray)
820 537 : : nullptr;
821 3220 : for (int64_t i = 0; i < poBatch->num_rows(); i++)
822 : {
823 2685 : if (!storageArray->IsNull(i))
824 : {
825 2160 : OGRwkbGeometryType eThisGeomType = wkbNone;
826 2160 : if (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKB &&
827 : castBinaryArray)
828 : {
829 2048 : arrow::BinaryArray::offset_type out_length = 0;
830 2048 : const uint8_t *data = castBinaryArray->GetValue(i, &out_length);
831 2048 : if (out_length >= 5)
832 : {
833 2048 : OGRReadWKBGeometryType(data, wkbVariantIso, &eThisGeomType);
834 : }
835 : }
836 112 : else if (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKB &&
837 : castLargeBinaryArray)
838 : {
839 0 : arrow::LargeBinaryArray::offset_type out_length = 0;
840 : const uint8_t *data =
841 0 : castLargeBinaryArray->GetValue(i, &out_length);
842 0 : if (out_length >= 5)
843 : {
844 0 : OGRReadWKBGeometryType(data, wkbVariantIso, &eThisGeomType);
845 : }
846 : }
847 112 : else if (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKT &&
848 : castStringArray)
849 : {
850 224 : const auto osWKT = castStringArray->GetString(i);
851 112 : if (!osWKT.empty())
852 : {
853 112 : OGRReadWKTGeometryType(osWKT.c_str(), &eThisGeomType);
854 : }
855 : }
856 0 : else if (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKT &&
857 : castLargeStringArray)
858 : {
859 0 : const auto osWKT = castLargeStringArray->GetString(i);
860 0 : if (!osWKT.empty())
861 : {
862 0 : OGRReadWKTGeometryType(osWKT.c_str(), &eThisGeomType);
863 : }
864 : }
865 :
866 2160 : if (eThisGeomType != wkbNone)
867 : {
868 2160 : if (eGeomType == wkbNone)
869 533 : eGeomType = eThisGeomType;
870 1627 : else if (wkbFlatten(eThisGeomType) == wkbFlatten(eGeomType))
871 : ;
872 6 : else if (wkbFlatten(eThisGeomType) == wkbMultiLineString &&
873 0 : wkbFlatten(eGeomType) == wkbLineString)
874 : {
875 0 : eGeomType = OGR_GT_SetModifier(
876 : wkbMultiLineString,
877 0 : OGR_GT_HasZ(eThisGeomType) || OGR_GT_HasZ(eGeomType),
878 0 : OGR_GT_HasM(eThisGeomType) || OGR_GT_HasM(eGeomType));
879 : }
880 8 : else if (wkbFlatten(eThisGeomType) == wkbLineString &&
881 2 : wkbFlatten(eGeomType) == wkbMultiLineString)
882 : ;
883 6 : else if (wkbFlatten(eThisGeomType) == wkbMultiPolygon &&
884 0 : wkbFlatten(eGeomType) == wkbPolygon)
885 : {
886 0 : eGeomType = OGR_GT_SetModifier(
887 : wkbMultiPolygon,
888 0 : OGR_GT_HasZ(eThisGeomType) || OGR_GT_HasZ(eGeomType),
889 0 : OGR_GT_HasM(eThisGeomType) || OGR_GT_HasM(eGeomType));
890 : }
891 10 : else if (wkbFlatten(eThisGeomType) == wkbPolygon &&
892 4 : wkbFlatten(eGeomType) == wkbMultiPolygon)
893 : ;
894 : else
895 2 : return wkbUnknown;
896 :
897 2158 : eGeomType = OGR_GT_SetModifier(
898 : eGeomType,
899 2158 : OGR_GT_HasZ(eThisGeomType) || OGR_GT_HasZ(eGeomType),
900 2158 : OGR_GT_HasM(eThisGeomType) || OGR_GT_HasM(eGeomType));
901 : }
902 : }
903 : }
904 535 : return eGeomType;
905 : }
906 :
907 : /************************************************************************/
908 : /* IsPointType() */
909 : /************************************************************************/
910 :
911 1055 : static bool IsPointType(const std::shared_ptr<arrow::DataType> &type,
912 : bool &bHasZOut, bool &bHasMOut)
913 : {
914 1055 : if (type->id() != arrow::Type::FIXED_SIZE_LIST)
915 725 : return false;
916 660 : auto poListType = std::static_pointer_cast<arrow::FixedSizeListType>(type);
917 330 : const int nOutDimensionality = poListType->list_size();
918 660 : const std::string osValueFieldName(poListType->value_field()->name());
919 330 : if (nOutDimensionality == 2)
920 : {
921 114 : bHasZOut = false;
922 114 : bHasMOut = false;
923 : }
924 216 : else if (nOutDimensionality == 3)
925 : {
926 156 : if (osValueFieldName == "xym")
927 : {
928 60 : bHasZOut = false;
929 60 : bHasMOut = true;
930 : }
931 : else /* if (osValueFieldName == "xyz" || osValueFieldName == "element") */
932 : {
933 96 : bHasMOut = false;
934 96 : bHasZOut = true;
935 : }
936 : }
937 60 : else if (nOutDimensionality == 4)
938 : {
939 60 : bHasMOut = true;
940 60 : bHasZOut = true;
941 : }
942 : else
943 : {
944 0 : return false;
945 : }
946 330 : return poListType->value_type()->id() == arrow::Type::DOUBLE;
947 : }
948 :
949 : /************************************************************************/
950 : /* IsListOfPointType() */
951 : /************************************************************************/
952 :
953 1672 : static bool IsListOfPointType(const std::shared_ptr<arrow::DataType> &type,
954 : int nDepth, bool &bHasZOut, bool &bHasMOut)
955 : {
956 1672 : if (type->id() != arrow::Type::LIST)
957 0 : return false;
958 1672 : auto poListType = std::static_pointer_cast<arrow::ListType>(type);
959 : return nDepth == 1
960 1672 : ? IsPointType(poListType->value_type(), bHasZOut, bHasMOut)
961 786 : : IsListOfPointType(poListType->value_type(), nDepth - 1,
962 1672 : bHasZOut, bHasMOut);
963 : }
964 :
965 : /************************************************************************/
966 : /* IsPointStructType() */
967 : /************************************************************************/
968 :
969 725 : static bool IsPointStructType(const std::shared_ptr<arrow::DataType> &type,
970 : bool &bHasZOut, bool &bHasMOut)
971 : {
972 725 : if (type->id() != arrow::Type::STRUCT)
973 0 : return false;
974 1450 : auto poStructType = std::static_pointer_cast<arrow::StructType>(type);
975 725 : const int nNumFields = poStructType->num_fields();
976 725 : if (nNumFields < 2 || nNumFields > 4)
977 0 : return false;
978 725 : bHasZOut = false;
979 725 : bHasMOut = false;
980 1450 : const auto poFieldX = poStructType->field(0);
981 1450 : if (poFieldX->name() != "x" ||
982 725 : poFieldX->type()->id() != arrow::Type::DOUBLE)
983 0 : return false;
984 1450 : const auto poFieldY = poStructType->field(1);
985 1450 : if (poFieldY->name() != "y" ||
986 725 : poFieldY->type()->id() != arrow::Type::DOUBLE)
987 0 : return false;
988 725 : if (nNumFields == 2)
989 419 : return true;
990 612 : const auto poField2 = poStructType->field(2);
991 306 : if (poField2->type()->id() != arrow::Type::DOUBLE)
992 0 : return false;
993 306 : if (poField2->name() == "z")
994 : {
995 300 : bHasZOut = true;
996 300 : if (nNumFields == 4)
997 : {
998 6 : const auto poField3 = poStructType->field(3);
999 12 : if (poField3->name() != "m" ||
1000 6 : poField3->type()->id() != arrow::Type::DOUBLE)
1001 0 : return false;
1002 6 : bHasMOut = true;
1003 : }
1004 : }
1005 6 : else if (poField2->name() == "m")
1006 : {
1007 6 : bHasMOut = true;
1008 : }
1009 : else
1010 : {
1011 0 : return false;
1012 : }
1013 306 : return true;
1014 : }
1015 :
1016 : /************************************************************************/
1017 : /* IsListOfPointStructType() */
1018 : /************************************************************************/
1019 :
1020 : static bool
1021 1164 : IsListOfPointStructType(const std::shared_ptr<arrow::DataType> &type,
1022 : int nDepth, bool &bHasZOut, bool &bHasMOut)
1023 : {
1024 1164 : if (type->id() != arrow::Type::LIST)
1025 0 : return false;
1026 1164 : auto poListType = std::static_pointer_cast<arrow::ListType>(type);
1027 : return nDepth == 1
1028 1164 : ? IsPointStructType(poListType->value_type(), bHasZOut, bHasMOut)
1029 555 : : IsListOfPointStructType(poListType->value_type(), nDepth - 1,
1030 1164 : bHasZOut, bHasMOut);
1031 : }
1032 :
1033 : /************************************************************************/
1034 : /* IsValidGeometryEncoding() */
1035 : /************************************************************************/
1036 :
1037 1897 : inline bool OGRArrowLayer::IsValidGeometryEncoding(
1038 : const std::shared_ptr<arrow::Field> &field, const std::string &osEncoding,
1039 : bool bWarnIfUnknownEncoding, OGRwkbGeometryType &eGeomTypeOut,
1040 : OGRArrowGeomEncoding &eOGRArrowGeomEncodingOut)
1041 : {
1042 1897 : const auto &fieldName = field->name();
1043 3794 : std::shared_ptr<arrow::DataType> fieldType = field->type();
1044 1897 : auto fieldTypeId = fieldType->id();
1045 :
1046 1897 : if (fieldTypeId == arrow::Type::EXTENSION)
1047 : {
1048 : auto extensionType =
1049 0 : cpl::down_cast<arrow::ExtensionType *>(fieldType.get());
1050 0 : fieldType = extensionType->storage_type();
1051 0 : fieldTypeId = fieldType->id();
1052 : }
1053 :
1054 1897 : eGeomTypeOut = wkbUnknown;
1055 :
1056 3739 : if (osEncoding == "WKT" || // As used in Parquet geo metadata
1057 1842 : osEncoding ==
1058 3739 : "ogc.wkt" || // As used in ARROW:extension:name field metadata
1059 1842 : osEncoding ==
1060 : "geoarrow.wkt" // As used in ARROW:extension:name field metadata
1061 : )
1062 : {
1063 55 : if (fieldTypeId != arrow::Type::LARGE_STRING &&
1064 : fieldTypeId != arrow::Type::STRING)
1065 : {
1066 0 : CPLError(CE_Warning, CPLE_AppDefined,
1067 : "Geometry column %s has a non String type: %s. "
1068 : "Handling it as a regular field",
1069 0 : fieldName.c_str(), fieldType->ToString().c_str());
1070 0 : return false;
1071 : }
1072 55 : eOGRArrowGeomEncodingOut = OGRArrowGeomEncoding::WKT;
1073 55 : return true;
1074 : }
1075 :
1076 2900 : if (osEncoding == "WKB" || // As used in Parquet geo metadata
1077 1058 : osEncoding ==
1078 2900 : "ogc.wkb" || // As used in ARROW:extension:name field metadata
1079 1058 : osEncoding ==
1080 : "geoarrow.wkb" // As used in ARROW:extension:name field metadata
1081 : )
1082 : {
1083 785 : if (fieldTypeId != arrow::Type::LARGE_BINARY &&
1084 : fieldTypeId != arrow::Type::BINARY)
1085 : {
1086 0 : CPLError(CE_Warning, CPLE_AppDefined,
1087 : "Geometry column %s has a non Binary type: %s. "
1088 : "Handling it as a regular field",
1089 0 : fieldName.c_str(), fieldType->ToString().c_str());
1090 0 : return false;
1091 : }
1092 785 : eOGRArrowGeomEncodingOut = OGRArrowGeomEncoding::WKB;
1093 785 : return true;
1094 : }
1095 :
1096 1057 : bool bHasZ = false;
1097 1057 : bool bHasM = false;
1098 1057 : if (osEncoding == "geoarrow.point" || osEncoding == "point")
1099 : {
1100 169 : if (IsPointType(fieldType, bHasZ, bHasM))
1101 : {
1102 53 : eOGRArrowGeomEncodingOut = OGRArrowGeomEncoding::GEOARROW_FSL_POINT;
1103 : }
1104 116 : else if (IsPointStructType(fieldType, bHasZ, bHasM))
1105 : {
1106 116 : eOGRArrowGeomEncodingOut =
1107 : OGRArrowGeomEncoding::GEOARROW_STRUCT_POINT;
1108 : }
1109 : else
1110 : {
1111 0 : CPLError(CE_Warning, CPLE_AppDefined,
1112 : "Geometry column %s has a type != fixed_size_list<xy: "
1113 : "double>[2]> and != struct<x: double, y: double>: %s. "
1114 : "Handling it as a regular field",
1115 0 : fieldName.c_str(), fieldType->name().c_str());
1116 0 : return false;
1117 : }
1118 169 : eGeomTypeOut = OGR_GT_SetModifier(wkbPoint, static_cast<int>(bHasZ),
1119 : static_cast<int>(bHasM));
1120 169 : return true;
1121 : }
1122 :
1123 888 : else if (osEncoding == "geoarrow.linestring" || osEncoding == "linestring")
1124 : {
1125 154 : if (IsListOfPointType(fieldType, 1, bHasZ, bHasM))
1126 : {
1127 52 : eOGRArrowGeomEncodingOut =
1128 : OGRArrowGeomEncoding::GEOARROW_FSL_LINESTRING;
1129 : }
1130 102 : else if (IsListOfPointStructType(fieldType, 1, bHasZ, bHasM))
1131 : {
1132 102 : eOGRArrowGeomEncodingOut =
1133 : OGRArrowGeomEncoding::GEOARROW_STRUCT_LINESTRING;
1134 : }
1135 : else
1136 : {
1137 0 : CPLError(CE_Warning, CPLE_AppDefined,
1138 : "Geometry column %s has a type != fixed_size_list<xy: "
1139 : "double>[2]> and != list<element: struct<x: double, y: "
1140 : "double>>: %s. "
1141 : "Handling it as a regular field",
1142 0 : fieldName.c_str(), fieldType->ToString().c_str());
1143 0 : return false;
1144 : }
1145 154 : eGeomTypeOut = OGR_GT_SetModifier(
1146 : wkbLineString, static_cast<int>(bHasZ), static_cast<int>(bHasM));
1147 154 : return true;
1148 : }
1149 :
1150 734 : else if (osEncoding == "geoarrow.polygon" || osEncoding == "polygon")
1151 : {
1152 216 : if (IsListOfPointType(fieldType, 2, bHasZ, bHasM))
1153 : {
1154 63 : eOGRArrowGeomEncodingOut =
1155 : OGRArrowGeomEncoding::GEOARROW_FSL_POLYGON;
1156 : }
1157 153 : else if (IsListOfPointStructType(fieldType, 2, bHasZ, bHasM))
1158 : {
1159 153 : eOGRArrowGeomEncodingOut =
1160 : OGRArrowGeomEncoding::GEOARROW_STRUCT_POLYGON;
1161 : }
1162 : else
1163 : {
1164 0 : CPLError(CE_Warning, CPLE_AppDefined,
1165 : "Geometry column %s has a type != list<vertices: "
1166 : "fixed_size_list<xy: double>[2]>> and != list<element: "
1167 : "list<element: struct<x: double, y: double>>>: %s. "
1168 : "Handling it as a regular field",
1169 0 : fieldName.c_str(), fieldType->ToString().c_str());
1170 0 : return false;
1171 : }
1172 216 : eGeomTypeOut = OGR_GT_SetModifier(wkbPolygon, static_cast<int>(bHasZ),
1173 : static_cast<int>(bHasM));
1174 216 : return true;
1175 : }
1176 :
1177 518 : else if (osEncoding == "geoarrow.multipoint" || osEncoding == "multipoint")
1178 : {
1179 154 : if (IsListOfPointType(fieldType, 1, bHasZ, bHasM))
1180 : {
1181 52 : eOGRArrowGeomEncodingOut =
1182 : OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOINT;
1183 : }
1184 102 : else if (IsListOfPointStructType(fieldType, 1, bHasZ, bHasM))
1185 : {
1186 102 : eOGRArrowGeomEncodingOut =
1187 : OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOINT;
1188 : }
1189 : else
1190 : {
1191 0 : CPLError(CE_Warning, CPLE_AppDefined,
1192 : "Geometry column %s has a type != fixed_size_list<xy: "
1193 : "double>[2]> and != list<element: struct<x: double, y: "
1194 : "double>>: %s. "
1195 : "Handling it as a regular field",
1196 0 : fieldName.c_str(), fieldType->ToString().c_str());
1197 0 : return false;
1198 : }
1199 154 : eGeomTypeOut = OGR_GT_SetModifier(
1200 : wkbMultiPoint, static_cast<int>(bHasZ), static_cast<int>(bHasM));
1201 154 : return true;
1202 : }
1203 :
1204 676 : else if (osEncoding == "geoarrow.multilinestring" ||
1205 312 : osEncoding == "multilinestring")
1206 : {
1207 154 : if (IsListOfPointType(fieldType, 2, bHasZ, bHasM))
1208 : {
1209 52 : eOGRArrowGeomEncodingOut =
1210 : OGRArrowGeomEncoding::GEOARROW_FSL_MULTILINESTRING;
1211 : }
1212 102 : else if (IsListOfPointStructType(fieldType, 2, bHasZ, bHasM))
1213 : {
1214 102 : eOGRArrowGeomEncodingOut =
1215 : OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTILINESTRING;
1216 : }
1217 : else
1218 : {
1219 0 : CPLError(CE_Warning, CPLE_AppDefined,
1220 : "Geometry column %s has a type != list<vertices: "
1221 : "fixed_size_list<xy: double>[2]>> and != list<element: "
1222 : "list<element: struct<x: double, y: double>>>: %s. "
1223 : "Handling it as a regular field",
1224 0 : fieldName.c_str(), fieldType->ToString().c_str());
1225 0 : return false;
1226 : }
1227 154 : eGeomTypeOut =
1228 154 : OGR_GT_SetModifier(wkbMultiLineString, static_cast<int>(bHasZ),
1229 : static_cast<int>(bHasM));
1230 154 : return true;
1231 : }
1232 :
1233 362 : else if (osEncoding == "geoarrow.multipolygon" ||
1234 152 : osEncoding == "multipolygon")
1235 : {
1236 208 : if (IsListOfPointType(fieldType, 3, bHasZ, bHasM))
1237 : {
1238 58 : eOGRArrowGeomEncodingOut =
1239 : OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOLYGON;
1240 : }
1241 150 : else if (IsListOfPointStructType(fieldType, 3, bHasZ, bHasM))
1242 : {
1243 150 : eOGRArrowGeomEncodingOut =
1244 : OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOLYGON;
1245 : }
1246 : else
1247 : {
1248 0 : CPLError(
1249 : CE_Warning, CPLE_AppDefined,
1250 : "Geometry column %s has a type != list<polygons: list<rings: "
1251 : "list<vertices: fixed_size_list<xy: double>[2]>>> and != "
1252 : "list<element: list<element: list<element: struct<x: double, "
1253 : "y: double>>>>: %s. "
1254 : "Handling it as a regular field",
1255 0 : fieldName.c_str(), fieldType->ToString().c_str());
1256 0 : return false;
1257 : }
1258 208 : eGeomTypeOut = OGR_GT_SetModifier(
1259 : wkbMultiPolygon, static_cast<int>(bHasZ), static_cast<int>(bHasM));
1260 208 : return true;
1261 : }
1262 :
1263 2 : if (bWarnIfUnknownEncoding)
1264 : {
1265 0 : CPLError(CE_Warning, CPLE_AppDefined,
1266 : "Geometry column %s uses a unhandled encoding: %s. "
1267 : "Handling it as a regular field",
1268 : fieldName.c_str(), osEncoding.c_str());
1269 : }
1270 2 : return false;
1271 : }
1272 :
1273 : /************************************************************************/
1274 : /* GetGeometryTypeFromString() */
1275 : /************************************************************************/
1276 :
1277 : inline OGRwkbGeometryType
1278 289 : OGRArrowLayer::GetGeometryTypeFromString(const std::string &osType)
1279 : {
1280 289 : OGRwkbGeometryType eGeomType = wkbUnknown;
1281 289 : OGRReadWKTGeometryType(osType.c_str(), &eGeomType);
1282 289 : if (eGeomType == wkbUnknown && !osType.empty())
1283 : {
1284 0 : CPLDebug("ARROW", "Unknown geometry type: %s", osType.c_str());
1285 : }
1286 289 : return eGeomType;
1287 : }
1288 :
1289 : static CPLJSONObject GetObjectAsJSON(const arrow::Array *array,
1290 : const size_t nIdx);
1291 :
1292 : /************************************************************************/
1293 : /* AddToArray() */
1294 : /************************************************************************/
1295 :
1296 : template <class Container>
1297 27798 : void AddToContainer(Container &oContainer, const arrow::Array *array,
1298 : const size_t nIdx)
1299 : {
1300 27798 : switch (array->type()->id())
1301 : {
1302 758 : case arrow::Type::BOOL:
1303 : {
1304 758 : oContainer.Add(
1305 758 : static_cast<const arrow::BooleanArray *>(array)->Value(nIdx));
1306 758 : break;
1307 : }
1308 765 : case arrow::Type::UINT8:
1309 : {
1310 765 : oContainer.Add(
1311 765 : static_cast<const arrow::UInt8Array *>(array)->Value(nIdx));
1312 765 : break;
1313 : }
1314 765 : case arrow::Type::INT8:
1315 : {
1316 765 : oContainer.Add(
1317 765 : static_cast<const arrow::Int8Array *>(array)->Value(nIdx));
1318 765 : break;
1319 : }
1320 765 : case arrow::Type::UINT16:
1321 : {
1322 765 : oContainer.Add(
1323 765 : static_cast<const arrow::UInt16Array *>(array)->Value(nIdx));
1324 765 : break;
1325 : }
1326 765 : case arrow::Type::INT16:
1327 : {
1328 765 : oContainer.Add(
1329 765 : static_cast<const arrow::Int16Array *>(array)->Value(nIdx));
1330 765 : break;
1331 : }
1332 765 : case arrow::Type::INT32:
1333 : {
1334 765 : oContainer.Add(
1335 736 : static_cast<const arrow::Int32Array *>(array)->Value(nIdx));
1336 765 : break;
1337 : }
1338 137 : case arrow::Type::UINT32:
1339 : {
1340 137 : oContainer.Add(static_cast<GInt64>(
1341 137 : static_cast<const arrow::UInt32Array *>(array)->Value(nIdx)));
1342 137 : break;
1343 : }
1344 4984 : case arrow::Type::INT64:
1345 : {
1346 4984 : oContainer.Add(static_cast<GInt64>(
1347 4984 : static_cast<const arrow::Int64Array *>(array)->Value(nIdx)));
1348 4984 : break;
1349 : }
1350 765 : case arrow::Type::UINT64:
1351 : {
1352 765 : oContainer.Add(static_cast<uint64_t>(
1353 765 : static_cast<const arrow::UInt64Array *>(array)->Value(nIdx)));
1354 765 : break;
1355 : }
1356 350 : case arrow::Type::HALF_FLOAT:
1357 : {
1358 : const uint16_t nFloat16 =
1359 350 : static_cast<const arrow::HalfFloatArray *>(array)->Value(nIdx);
1360 350 : uint32_t nFloat32 = CPLHalfToFloat(nFloat16);
1361 : float f;
1362 350 : memcpy(&f, &nFloat32, sizeof(nFloat32));
1363 350 : oContainer.Add(f);
1364 350 : break;
1365 : }
1366 768 : case arrow::Type::FLOAT:
1367 : {
1368 768 : oContainer.Add(
1369 768 : static_cast<const arrow::FloatArray *>(array)->Value(nIdx));
1370 768 : break;
1371 : }
1372 4338 : case arrow::Type::DOUBLE:
1373 : {
1374 4338 : oContainer.Add(
1375 4310 : static_cast<const arrow::DoubleArray *>(array)->Value(nIdx));
1376 4338 : break;
1377 : }
1378 :
1379 : #if ARROW_VERSION_MAJOR >= 18
1380 0 : case arrow::Type::DECIMAL32:
1381 : {
1382 0 : oContainer.Add(
1383 0 : CPLAtof(static_cast<const arrow::Decimal32Array *>(array)
1384 : ->FormatValue(nIdx)
1385 : .c_str()));
1386 0 : break;
1387 : }
1388 0 : case arrow::Type::DECIMAL64:
1389 : {
1390 0 : oContainer.Add(
1391 0 : CPLAtof(static_cast<const arrow::Decimal64Array *>(array)
1392 : ->FormatValue(nIdx)
1393 : .c_str()));
1394 0 : break;
1395 : }
1396 : #endif
1397 1364 : case arrow::Type::DECIMAL128:
1398 : {
1399 1364 : oContainer.Add(
1400 1356 : CPLAtof(static_cast<const arrow::Decimal128Array *>(array)
1401 : ->FormatValue(nIdx)
1402 : .c_str()));
1403 1364 : break;
1404 : }
1405 118 : case arrow::Type::DECIMAL256:
1406 : {
1407 118 : oContainer.Add(
1408 110 : CPLAtof(static_cast<const arrow::Decimal256Array *>(array)
1409 : ->FormatValue(nIdx)
1410 : .c_str()));
1411 118 : break;
1412 : }
1413 3692 : case arrow::Type::STRING:
1414 : {
1415 3692 : oContainer.Add(
1416 : static_cast<const arrow::StringArray *>(array)->GetString(
1417 : nIdx));
1418 3692 : break;
1419 : }
1420 118 : case arrow::Type::LARGE_STRING:
1421 : {
1422 118 : oContainer.Add(
1423 : static_cast<const arrow::LargeStringArray *>(array)->GetString(
1424 : nIdx));
1425 118 : break;
1426 : }
1427 : #if ARROW_VERSION_MAJOR >= 15
1428 4 : case arrow::Type::STRING_VIEW:
1429 : {
1430 4 : oContainer.Add(
1431 : static_cast<const arrow::StringViewArray *>(array)->GetString(
1432 : nIdx));
1433 4 : break;
1434 : }
1435 : #endif
1436 6573 : case arrow::Type::LIST:
1437 : case arrow::Type::LARGE_LIST:
1438 : case arrow::Type::FIXED_SIZE_LIST:
1439 : case arrow::Type::MAP:
1440 : case arrow::Type::STRUCT:
1441 : {
1442 6573 : oContainer.Add(GetObjectAsJSON(array, nIdx));
1443 6573 : break;
1444 : }
1445 :
1446 4 : case arrow::Type::BINARY:
1447 : {
1448 4 : const auto castArray =
1449 : static_cast<const arrow::BinaryArray *>(array);
1450 4 : int length = 0;
1451 4 : const uint8_t *data = castArray->GetValue(nIdx, &length);
1452 4 : bool bIsASCII = true;
1453 9 : for (int i = 0; i < length && bIsASCII; ++i)
1454 5 : bIsASCII = data[i] >= 32 && data[i] <= 127;
1455 4 : if (bIsASCII)
1456 : {
1457 3 : oContainer.Add(
1458 : std::string(reinterpret_cast<const char *>(data), length));
1459 : }
1460 : else
1461 : {
1462 1 : char *pszBase64 = CPLBase64Encode(length, data);
1463 1 : oContainer.Add(std::string("base64:").append(pszBase64));
1464 1 : CPLFree(pszBase64);
1465 : }
1466 4 : break;
1467 : }
1468 :
1469 : #if ARROW_VERSION_MAJOR >= 15
1470 0 : case arrow::Type::BINARY_VIEW:
1471 : {
1472 0 : const auto castArray =
1473 : static_cast<const arrow::BinaryViewArray *>(array);
1474 0 : const auto view = castArray->GetView(nIdx);
1475 0 : if (view.size() <= INT_MAX)
1476 : {
1477 0 : const int length = static_cast<int>(view.size());
1478 0 : const char *data = view.data();
1479 0 : bool bIsASCII = true;
1480 0 : for (int i = 0; i < length && bIsASCII; ++i)
1481 0 : bIsASCII =
1482 0 : data[i] >= 32 && static_cast<unsigned>(data[i]) <= 127;
1483 0 : if (bIsASCII)
1484 : {
1485 0 : oContainer.Add(std::string(view));
1486 : }
1487 : else
1488 : {
1489 0 : char *pszBase64 = CPLBase64Encode(
1490 : length, reinterpret_cast<const GByte *>(data));
1491 0 : oContainer.Add(std::string("base64:").append(pszBase64));
1492 0 : CPLFree(pszBase64);
1493 : }
1494 : }
1495 : else
1496 : {
1497 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too large binary view");
1498 : }
1499 0 : break;
1500 : }
1501 : #endif
1502 :
1503 0 : default:
1504 : {
1505 0 : CPLDebug("ARROW", "AddToContainer(): unexpected data type %s",
1506 0 : array->type()->ToString().c_str());
1507 0 : break;
1508 : }
1509 : }
1510 27798 : }
1511 :
1512 : /************************************************************************/
1513 : /* AddToArray() */
1514 : /************************************************************************/
1515 :
1516 6739 : static void AddToArray(CPLJSONArray &oArray, const arrow::Array *array,
1517 : const size_t nIdx)
1518 : {
1519 6739 : AddToContainer(oArray, array, nIdx);
1520 6739 : }
1521 :
1522 : /************************************************************************/
1523 : /* GetListAsJSON() */
1524 : /************************************************************************/
1525 :
1526 : template <class ArrowType>
1527 5194 : static CPLJSONArray GetListAsJSON(const ArrowType *array,
1528 : const size_t nIdxInArray)
1529 : {
1530 10388 : const auto values = array->values();
1531 5194 : const auto nIdxStart = array->value_offset(nIdxInArray);
1532 5194 : const auto nCount = array->value_length(nIdxInArray);
1533 5194 : CPLJSONArray oArray;
1534 14357 : for (auto k = decltype(nCount){0}; k < nCount; k++)
1535 : {
1536 9163 : if (values->IsNull(nIdxStart + k))
1537 2424 : oArray.AddNull();
1538 : else
1539 6739 : AddToArray(oArray, values.get(),
1540 6739 : static_cast<size_t>(nIdxStart + k));
1541 : }
1542 10388 : return oArray;
1543 : }
1544 :
1545 : /************************************************************************/
1546 : /* AddToDict() */
1547 : /************************************************************************/
1548 :
1549 : namespace
1550 : {
1551 : struct ContainerAdapter
1552 : {
1553 : CPLJSONObject &m_oDict;
1554 : const std::string m_osKey;
1555 :
1556 21059 : ContainerAdapter(CPLJSONObject &oDictIn, const std::string &osKeyIn)
1557 21059 : : m_oDict(oDictIn), m_osKey(osKeyIn)
1558 : {
1559 21059 : }
1560 :
1561 21059 : template <class T> void Add(const T &v)
1562 : {
1563 21059 : m_oDict.Add(m_osKey, v);
1564 21059 : }
1565 : };
1566 : } // namespace
1567 :
1568 21059 : static void AddToDict(CPLJSONObject &oDict, const std::string &osKey,
1569 : const arrow::Array *array, const size_t nIdx)
1570 : {
1571 42118 : ContainerAdapter dictAdapter(oDict, osKey);
1572 21059 : AddToContainer(dictAdapter, array, nIdx);
1573 21059 : }
1574 :
1575 : /************************************************************************/
1576 : /* GetMapAsJSON() */
1577 : /************************************************************************/
1578 :
1579 : template <class KeyArrayType>
1580 25331 : static CPLJSONObject GetMapAsJSON(const arrow::Array *array,
1581 : const size_t nIdxInArray)
1582 : {
1583 25331 : const auto mapArray = static_cast<const arrow::MapArray *>(array);
1584 50662 : const auto keys = std::static_pointer_cast<KeyArrayType>(mapArray->keys());
1585 50662 : const auto values = mapArray->items();
1586 25331 : const auto nIdxStart = mapArray->value_offset(nIdxInArray);
1587 25331 : const int nCount = mapArray->value_length(nIdxInArray);
1588 25331 : CPLJSONObject oRoot;
1589 46079 : for (int k = 0; k < nCount; k++)
1590 : {
1591 20748 : if (!keys->IsNull(nIdxStart + k))
1592 : {
1593 41496 : const auto osKey = keys->GetString(nIdxStart + k);
1594 20748 : if (!values->IsNull(nIdxStart + k))
1595 13807 : AddToDict(oRoot, osKey, values.get(), nIdxStart + k);
1596 : else
1597 6941 : oRoot.AddNull(osKey);
1598 : }
1599 : }
1600 50662 : return oRoot;
1601 : }
1602 :
1603 25331 : static CPLJSONObject GetMapAsJSON(const arrow::Array *array,
1604 : const size_t nIdxInArray)
1605 : {
1606 25331 : const auto mapArray = static_cast<const arrow::MapArray *>(array);
1607 25331 : const auto eKeyType = mapArray->keys()->type()->id();
1608 25331 : if (eKeyType == arrow::Type::STRING)
1609 25329 : return GetMapAsJSON<arrow::StringArray>(array, nIdxInArray);
1610 : #if ARROW_VERSION_MAJOR >= 15
1611 2 : else if (eKeyType == arrow::Type::STRING_VIEW)
1612 2 : return GetMapAsJSON<arrow::StringViewArray>(array, nIdxInArray);
1613 : #endif
1614 : else
1615 : {
1616 0 : CPLAssert(false);
1617 : return CPLJSONObject();
1618 : }
1619 : }
1620 :
1621 : /************************************************************************/
1622 : /* GetStructureAsJSON() */
1623 : /************************************************************************/
1624 :
1625 3610 : static CPLJSONObject GetStructureAsJSON(const arrow::Array *array,
1626 : const size_t nIdxInArray)
1627 : {
1628 3610 : CPLJSONObject oRoot;
1629 3610 : const auto structArray = static_cast<const arrow::StructArray *>(array);
1630 7220 : const auto structArrayType = structArray->type();
1631 14519 : for (int i = 0; i < structArrayType->num_fields(); ++i)
1632 : {
1633 21818 : const auto field = structArray->field(i);
1634 10909 : if (!field->IsNull(nIdxInArray))
1635 : {
1636 7252 : AddToDict(oRoot, structArrayType->field(i)->name(), field.get(),
1637 : nIdxInArray);
1638 : }
1639 : else
1640 3657 : oRoot.AddNull(structArrayType->field(i)->name());
1641 : }
1642 :
1643 7220 : return oRoot;
1644 : }
1645 :
1646 : /************************************************************************/
1647 : /* GetObjectAsJSON() */
1648 : /************************************************************************/
1649 :
1650 6573 : static CPLJSONObject GetObjectAsJSON(const arrow::Array *array,
1651 : const size_t nIdxInArray)
1652 : {
1653 6573 : switch (array->type()->id())
1654 : {
1655 26 : case arrow::Type::MAP:
1656 26 : return GetMapAsJSON(array, nIdxInArray);
1657 2641 : case arrow::Type::LIST:
1658 5282 : return GetListAsJSON(static_cast<const arrow::ListArray *>(array),
1659 2641 : nIdxInArray);
1660 113 : case arrow::Type::LARGE_LIST:
1661 226 : return GetListAsJSON(
1662 113 : static_cast<const arrow::LargeListArray *>(array), nIdxInArray);
1663 183 : case arrow::Type::FIXED_SIZE_LIST:
1664 366 : return GetListAsJSON(
1665 : static_cast<const arrow::FixedSizeListArray *>(array),
1666 183 : nIdxInArray);
1667 3610 : case arrow::Type::STRUCT:
1668 3610 : return GetStructureAsJSON(array, nIdxInArray);
1669 0 : default:
1670 : {
1671 0 : CPLError(CE_Failure, CPLE_AppDefined,
1672 : "GetObjectAsJSON(): unhandled value format: %s",
1673 0 : array->type()->ToString().c_str());
1674 0 : return CPLJSONObject();
1675 : }
1676 : }
1677 : }
1678 :
1679 : template <class ArrowType>
1680 48215 : static int GetListLength(const ArrowType *array, int64_t nIdxInArray)
1681 : {
1682 48215 : const auto nCount = array->value_length(nIdxInArray);
1683 : if constexpr (!std::is_same_v<decltype(nCount), int>)
1684 : {
1685 : // Should be >, but >= makes coverity scan happy
1686 48215 : if (nCount >= INT_MAX)
1687 : {
1688 0 : CPLError(CE_Failure, CPLE_NotSupported,
1689 : "More than %d values in list. Clamping to it", INT_MAX);
1690 0 : return INT_MAX;
1691 : }
1692 : }
1693 48215 : return static_cast<int>(nCount);
1694 : }
1695 :
1696 : template <class OGRType, class ArrowType, class ArrayType>
1697 32688 : static void ReadList(OGRFeature *poFeature, int i, int64_t nIdxInArray,
1698 : const ArrayType *array)
1699 : {
1700 65376 : const auto values = std::static_pointer_cast<ArrowType>(array->values());
1701 32688 : const auto nIdxStart = array->value_offset(nIdxInArray);
1702 32688 : const int nCount = GetListLength(array, nIdxInArray);
1703 65376 : std::vector<OGRType> aValues;
1704 32688 : aValues.reserve(nCount);
1705 97861 : for (int k = 0; k < nCount; k++)
1706 : {
1707 65173 : aValues.push_back(static_cast<OGRType>(values->Value(nIdxStart + k)));
1708 : }
1709 32688 : poFeature->SetField(i, nCount, aValues.data());
1710 32688 : }
1711 :
1712 : template <class ArrowType, class ArrayType>
1713 7299 : static void ReadListDouble(OGRFeature *poFeature, int i, int64_t nIdxInArray,
1714 : const ArrayType *array)
1715 : {
1716 14598 : const auto values = std::static_pointer_cast<ArrowType>(array->values());
1717 7299 : const auto rawValues = values->raw_values();
1718 7299 : const auto nIdxStart = array->value_offset(nIdxInArray);
1719 7299 : const int nCount = GetListLength(array, nIdxInArray);
1720 14598 : std::vector<double> aValues;
1721 7299 : aValues.reserve(nCount);
1722 21480 : for (int k = 0; k < nCount; k++)
1723 : {
1724 14181 : if (values->IsNull(nIdxStart + k))
1725 2982 : aValues.push_back(std::numeric_limits<double>::quiet_NaN());
1726 : else
1727 11199 : aValues.push_back(rawValues[nIdxStart + k]);
1728 : }
1729 7299 : poFeature->SetField(i, nCount, aValues.data());
1730 7299 : }
1731 :
1732 : template <class ArrayType>
1733 50304 : static void ReadList(OGRFeature *poFeature, int i, int64_t nIdxInArray,
1734 : const ArrayType *array, arrow::Type::type valueTypeId)
1735 : {
1736 50304 : switch (valueTypeId)
1737 : {
1738 3442 : case arrow::Type::BOOL:
1739 : {
1740 3442 : ReadList<int, arrow::BooleanArray>(poFeature, i, nIdxInArray,
1741 : array);
1742 3442 : break;
1743 : }
1744 3272 : case arrow::Type::UINT8:
1745 : {
1746 3272 : ReadList<int, arrow::UInt8Array>(poFeature, i, nIdxInArray, array);
1747 3272 : break;
1748 : }
1749 3272 : case arrow::Type::INT8:
1750 : {
1751 3272 : ReadList<int, arrow::Int8Array>(poFeature, i, nIdxInArray, array);
1752 3272 : break;
1753 : }
1754 3272 : case arrow::Type::UINT16:
1755 : {
1756 3272 : ReadList<int, arrow::UInt16Array>(poFeature, i, nIdxInArray, array);
1757 3272 : break;
1758 : }
1759 3272 : case arrow::Type::INT16:
1760 : {
1761 3272 : ReadList<int, arrow::Int16Array>(poFeature, i, nIdxInArray, array);
1762 3272 : break;
1763 : }
1764 4133 : case arrow::Type::INT32:
1765 : {
1766 4133 : ReadList<int, arrow::Int32Array>(poFeature, i, nIdxInArray, array);
1767 4133 : break;
1768 : }
1769 440 : case arrow::Type::UINT32:
1770 : {
1771 440 : ReadList<GIntBig, arrow::UInt32Array>(poFeature, i, nIdxInArray,
1772 : array);
1773 440 : break;
1774 : }
1775 8313 : case arrow::Type::INT64:
1776 : {
1777 8313 : ReadList<GIntBig, arrow::Int64Array>(poFeature, i, nIdxInArray,
1778 : array);
1779 8313 : break;
1780 : }
1781 3272 : case arrow::Type::UINT64:
1782 : {
1783 3272 : ReadList<double, arrow::UInt64Array>(poFeature, i, nIdxInArray,
1784 : array);
1785 3272 : break;
1786 : }
1787 199 : case arrow::Type::HALF_FLOAT:
1788 : {
1789 398 : const auto values = std::static_pointer_cast<arrow::HalfFloatArray>(
1790 : array->values());
1791 199 : const auto nIdxStart = array->value_offset(nIdxInArray);
1792 199 : const int nCount = GetListLength(array, nIdxInArray);
1793 398 : std::vector<double> aValues;
1794 199 : aValues.reserve(nCount);
1795 560 : for (int k = 0; k < nCount; k++)
1796 : {
1797 361 : if (values->IsNull(nIdxStart + k))
1798 131 : aValues.push_back(std::numeric_limits<double>::quiet_NaN());
1799 : else
1800 : {
1801 230 : const uint16_t nFloat16 = values->Value(nIdxStart + k);
1802 230 : uint32_t nFloat32 = CPLHalfToFloat(nFloat16);
1803 : float f;
1804 230 : memcpy(&f, &nFloat32, sizeof(nFloat32));
1805 230 : aValues.push_back(f);
1806 : }
1807 : }
1808 199 : poFeature->SetField(i, nCount, aValues.data());
1809 199 : break;
1810 : }
1811 3503 : case arrow::Type::FLOAT:
1812 : {
1813 3503 : ReadListDouble<arrow::FloatArray>(poFeature, i, nIdxInArray, array);
1814 3503 : break;
1815 : }
1816 3796 : case arrow::Type::DOUBLE:
1817 : {
1818 3796 : ReadListDouble<arrow::DoubleArray>(poFeature, i, nIdxInArray,
1819 : array);
1820 3796 : break;
1821 : }
1822 :
1823 : #if ARROW_VERSION_MAJOR >= 18
1824 0 : case arrow::Type::DECIMAL32:
1825 : {
1826 0 : const auto values = std::static_pointer_cast<arrow::Decimal32Array>(
1827 : array->values());
1828 0 : const auto nIdxStart = array->value_offset(nIdxInArray);
1829 0 : const int nCount = GetListLength(array, nIdxInArray);
1830 0 : std::vector<double> aValues;
1831 0 : aValues.reserve(nCount);
1832 0 : for (int k = 0; k < nCount; k++)
1833 : {
1834 0 : if (values->IsNull(nIdxStart + k))
1835 0 : aValues.push_back(std::numeric_limits<double>::quiet_NaN());
1836 : else
1837 0 : aValues.push_back(
1838 0 : CPLAtof(values->FormatValue(nIdxStart + k).c_str()));
1839 : }
1840 0 : poFeature->SetField(i, nCount, aValues.data());
1841 0 : break;
1842 : }
1843 :
1844 0 : case arrow::Type::DECIMAL64:
1845 : {
1846 0 : const auto values = std::static_pointer_cast<arrow::Decimal64Array>(
1847 : array->values());
1848 0 : const auto nIdxStart = array->value_offset(nIdxInArray);
1849 0 : const int nCount = GetListLength(array, nIdxInArray);
1850 0 : std::vector<double> aValues;
1851 0 : aValues.reserve(nCount);
1852 0 : for (int k = 0; k < nCount; k++)
1853 : {
1854 0 : if (values->IsNull(nIdxStart + k))
1855 0 : aValues.push_back(std::numeric_limits<double>::quiet_NaN());
1856 : else
1857 0 : aValues.push_back(
1858 0 : CPLAtof(values->FormatValue(nIdxStart + k).c_str()));
1859 : }
1860 0 : poFeature->SetField(i, nCount, aValues.data());
1861 0 : break;
1862 : }
1863 : #endif
1864 :
1865 1491 : case arrow::Type::DECIMAL128:
1866 : {
1867 2982 : const auto values =
1868 : std::static_pointer_cast<arrow::Decimal128Array>(
1869 : array->values());
1870 1491 : const auto nIdxStart = array->value_offset(nIdxInArray);
1871 1491 : const int nCount = GetListLength(array, nIdxInArray);
1872 2982 : std::vector<double> aValues;
1873 1491 : aValues.reserve(nCount);
1874 2982 : for (int k = 0; k < nCount; k++)
1875 : {
1876 1491 : if (values->IsNull(nIdxStart + k))
1877 348 : aValues.push_back(std::numeric_limits<double>::quiet_NaN());
1878 : else
1879 1143 : aValues.push_back(
1880 1143 : CPLAtof(values->FormatValue(nIdxStart + k).c_str()));
1881 : }
1882 1491 : poFeature->SetField(i, nCount, aValues.data());
1883 1491 : break;
1884 : }
1885 :
1886 1491 : case arrow::Type::DECIMAL256:
1887 : {
1888 2982 : const auto values =
1889 : std::static_pointer_cast<arrow::Decimal256Array>(
1890 : array->values());
1891 1491 : const auto nIdxStart = array->value_offset(nIdxInArray);
1892 1491 : const int nCount = GetListLength(array, nIdxInArray);
1893 2982 : std::vector<double> aValues;
1894 1491 : aValues.reserve(nCount);
1895 2982 : for (int k = 0; k < nCount; k++)
1896 : {
1897 1491 : if (values->IsNull(nIdxStart + k))
1898 348 : aValues.push_back(std::numeric_limits<double>::quiet_NaN());
1899 : else
1900 1143 : aValues.push_back(
1901 1143 : CPLAtof(values->FormatValue(nIdxStart + k).c_str()));
1902 : }
1903 1491 : poFeature->SetField(i, nCount, aValues.data());
1904 1491 : break;
1905 : }
1906 :
1907 3552 : case arrow::Type::STRING:
1908 : {
1909 7104 : const auto values =
1910 : std::static_pointer_cast<arrow::StringArray>(array->values());
1911 3552 : const auto nIdxStart = array->value_offset(nIdxInArray);
1912 3552 : const int nCount = GetListLength(array, nIdxInArray);
1913 7104 : CPLStringList aosList;
1914 9255 : for (int k = 0; k < nCount; k++)
1915 : {
1916 5703 : if (values->IsNull(nIdxStart + k))
1917 356 : aosList.AddString(
1918 : ""); // we cannot have null strings in a list
1919 : else
1920 5347 : aosList.AddString(values->GetString(nIdxStart + k).c_str());
1921 : }
1922 3552 : poFeature->SetField(i, aosList.List());
1923 3552 : break;
1924 : }
1925 :
1926 1491 : case arrow::Type::LARGE_STRING:
1927 : {
1928 2982 : const auto values =
1929 : std::static_pointer_cast<arrow::LargeStringArray>(
1930 : array->values());
1931 1491 : const auto nIdxStart = array->value_offset(nIdxInArray);
1932 1491 : const auto nCount = GetListLength(array, nIdxInArray);
1933 2982 : CPLStringList aosList;
1934 3251 : for (auto k = decltype(nCount){0}; k < nCount; k++)
1935 : {
1936 1760 : if (values->IsNull(nIdxStart + k))
1937 356 : aosList.AddString(
1938 : ""); // we cannot have null strings in a list
1939 : else
1940 1404 : aosList.AddString(values->GetString(nIdxStart + k).c_str());
1941 : }
1942 1491 : poFeature->SetField(i, aosList.List());
1943 1491 : break;
1944 : }
1945 :
1946 : #if ARROW_VERSION_MAJOR >= 15
1947 2 : case arrow::Type::STRING_VIEW:
1948 : {
1949 4 : const auto values =
1950 : std::static_pointer_cast<arrow::StringViewArray>(
1951 : array->values());
1952 2 : const auto nIdxStart = array->value_offset(nIdxInArray);
1953 2 : const int nCount = GetListLength(array, nIdxInArray);
1954 4 : CPLStringList aosList;
1955 6 : for (int k = 0; k < nCount; k++)
1956 : {
1957 4 : if (values->IsNull(nIdxStart + k))
1958 1 : aosList.AddString(
1959 : ""); // we cannot have null strings in a list
1960 : else
1961 3 : aosList.AddString(values->GetString(nIdxStart + k).c_str());
1962 : }
1963 2 : poFeature->SetField(i, aosList.List());
1964 2 : break;
1965 : }
1966 : #endif
1967 :
1968 2 : case arrow::Type::BINARY:
1969 : {
1970 4 : const auto values =
1971 : std::static_pointer_cast<arrow::BinaryArray>(array->values());
1972 2 : const auto nIdxStart = array->value_offset(nIdxInArray);
1973 2 : const auto nCount = GetListLength(array, nIdxInArray);
1974 4 : CPLStringList aosList;
1975 6 : for (auto k = decltype(nCount){0}; k < nCount; k++)
1976 : {
1977 4 : if (values->IsNull(nIdxStart + k))
1978 : {
1979 1 : aosList.AddString(
1980 : ""); // we cannot have null strings in a list
1981 : }
1982 : else
1983 : {
1984 3 : int length = 0;
1985 3 : const uint8_t *data =
1986 3 : values->GetValue(nIdxStart + k, &length);
1987 3 : bool bIsASCII = true;
1988 10 : for (int j = 0; j < length && bIsASCII; ++j)
1989 7 : bIsASCII = data[j] >= 32 && data[j] <= 127;
1990 3 : if (bIsASCII)
1991 : {
1992 2 : aosList.AddString(
1993 : std::string(reinterpret_cast<const char *>(data),
1994 : length)
1995 : .c_str());
1996 : }
1997 : else
1998 : {
1999 1 : char *pszBase64 = CPLBase64Encode(length, data);
2000 1 : aosList.AddString(
2001 2 : std::string("base64:").append(pszBase64).c_str());
2002 1 : CPLFree(pszBase64);
2003 : }
2004 : }
2005 : }
2006 2 : poFeature->SetField(i, aosList.List());
2007 2 : break;
2008 : }
2009 :
2010 2089 : case arrow::Type::LIST:
2011 : case arrow::Type::LARGE_LIST:
2012 : case arrow::Type::FIXED_SIZE_LIST:
2013 : case arrow::Type::MAP:
2014 : case arrow::Type::STRUCT:
2015 : {
2016 2089 : poFeature->SetField(
2017 : i, GetListAsJSON(array, static_cast<size_t>(nIdxInArray))
2018 : .Format(CPLJSONObject::PrettyFormat::Plain)
2019 : .c_str());
2020 2089 : break;
2021 : }
2022 :
2023 0 : default:
2024 : {
2025 0 : CPLDebug("ARROW", "ReadList(): unexpected data type %s",
2026 0 : array->values()->type()->ToString().c_str());
2027 0 : break;
2028 : }
2029 : }
2030 50304 : }
2031 :
2032 : /************************************************************************/
2033 : /* SetPointsOfLine() */
2034 : /************************************************************************/
2035 :
2036 : template <bool bHasZ, bool bHasM, int nDim>
2037 960 : void SetPointsOfLine(OGRLineString *poLS, const arrow::DoubleArray *pointValues,
2038 : size_t pointOffset, int numPoints)
2039 : {
2040 : if (!bHasZ && !bHasM)
2041 : {
2042 : static_assert(sizeof(OGRRawPoint) == 2 * sizeof(double),
2043 : "sizeof(OGRRawPoint) == 2 * sizeof(double)");
2044 1240 : poLS->setPoints(numPoints,
2045 : reinterpret_cast<const OGRRawPoint *>(
2046 620 : pointValues->raw_values() + pointOffset));
2047 620 : return;
2048 : }
2049 :
2050 340 : poLS->setNumPoints(numPoints, FALSE);
2051 1634 : for (int k = 0; k < numPoints; k++)
2052 : {
2053 : if constexpr (bHasZ)
2054 : {
2055 : if constexpr (bHasM)
2056 : {
2057 330 : poLS->setPoint(k, pointValues->Value(pointOffset + nDim * k),
2058 330 : pointValues->Value(pointOffset + nDim * k + 1),
2059 330 : pointValues->Value(pointOffset + nDim * k + 2),
2060 330 : pointValues->Value(pointOffset + nDim * k + 3));
2061 : }
2062 : else
2063 : {
2064 634 : poLS->setPoint(k, pointValues->Value(pointOffset + nDim * k),
2065 634 : pointValues->Value(pointOffset + nDim * k + 1),
2066 634 : pointValues->Value(pointOffset + nDim * k + 2));
2067 : }
2068 : }
2069 : else /* if( bHasM ) */
2070 : {
2071 330 : poLS->setPointM(k, pointValues->Value(pointOffset + nDim * k),
2072 330 : pointValues->Value(pointOffset + nDim * k + 1),
2073 330 : pointValues->Value(pointOffset + nDim * k + 2));
2074 : }
2075 : }
2076 : }
2077 :
2078 : typedef void (*SetPointsOfLineType)(OGRLineString *, const arrow::DoubleArray *,
2079 : size_t, int);
2080 :
2081 836 : static SetPointsOfLineType GetSetPointsOfLine(bool bHasZ, bool bHasM)
2082 : {
2083 836 : if (bHasZ && bHasM)
2084 66 : return SetPointsOfLine<true, true, 4>;
2085 770 : if (bHasZ)
2086 168 : return SetPointsOfLine<true, false, 3>;
2087 602 : if (bHasM)
2088 66 : return SetPointsOfLine<false, true, 3>;
2089 536 : return SetPointsOfLine<false, false, 2>;
2090 : }
2091 :
2092 : /************************************************************************/
2093 : /* SetPointsOfLineStruct() */
2094 : /************************************************************************/
2095 :
2096 : template <bool bHasZ, bool bHasM, int nDim>
2097 2116 : void SetPointsOfLineStruct(OGRLineString *poLS,
2098 : const arrow::StructArray *structArray,
2099 : size_t pointOffset, int numPoints)
2100 : {
2101 2116 : CPLAssert(structArray->num_fields() == nDim);
2102 2116 : const auto &fields = structArray->fields();
2103 2116 : const auto &fieldX = fields[0];
2104 2116 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
2105 2116 : const auto fieldXDouble = static_cast<arrow::DoubleArray *>(fieldX.get());
2106 2116 : const auto &fieldY = fields[1];
2107 2116 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
2108 2116 : const auto fieldYDouble = static_cast<arrow::DoubleArray *>(fieldY.get());
2109 2116 : const arrow::DoubleArray *fieldZDouble = nullptr;
2110 2116 : const arrow::DoubleArray *fieldMDouble = nullptr;
2111 2116 : int iField = 2;
2112 : if constexpr (bHasZ)
2113 : {
2114 686 : const auto &field = fields[iField];
2115 686 : ++iField;
2116 686 : CPLAssert(field->type_id() == arrow::Type::DOUBLE);
2117 686 : fieldZDouble = static_cast<arrow::DoubleArray *>(field.get());
2118 : }
2119 : if constexpr (bHasM)
2120 : {
2121 26 : const auto &field = fields[iField];
2122 26 : CPLAssert(field->type_id() == arrow::Type::DOUBLE);
2123 26 : fieldMDouble = static_cast<arrow::DoubleArray *>(field.get());
2124 : }
2125 :
2126 2116 : poLS->setNumPoints(numPoints, FALSE);
2127 9680 : for (int k = 0; k < numPoints; k++)
2128 : {
2129 : if constexpr (bHasZ)
2130 : {
2131 : if constexpr (bHasM)
2132 : {
2133 55 : poLS->setPoint(k, fieldXDouble->Value(pointOffset + k),
2134 55 : fieldYDouble->Value(pointOffset + k),
2135 55 : fieldZDouble->Value(pointOffset + k),
2136 55 : fieldMDouble->Value(pointOffset + k));
2137 : }
2138 : else
2139 : {
2140 1975 : poLS->setPoint(k, fieldXDouble->Value(pointOffset + k),
2141 1975 : fieldYDouble->Value(pointOffset + k),
2142 1975 : fieldZDouble->Value(pointOffset + k));
2143 : }
2144 : }
2145 : else if constexpr (bHasM)
2146 : {
2147 55 : poLS->setPointM(k, fieldXDouble->Value(pointOffset + k),
2148 55 : fieldYDouble->Value(pointOffset + k),
2149 55 : fieldMDouble->Value(pointOffset + k));
2150 : }
2151 : else
2152 : {
2153 5479 : poLS->setPoint(k, fieldXDouble->Value(pointOffset + k),
2154 5479 : fieldYDouble->Value(pointOffset + k));
2155 : }
2156 : }
2157 2116 : }
2158 :
2159 : typedef void (*SetPointsOfLineStructType)(OGRLineString *,
2160 : const arrow::StructArray *, size_t,
2161 : int);
2162 :
2163 1628 : static SetPointsOfLineStructType GetSetPointsOfLineStruct(bool bHasZ,
2164 : bool bHasM)
2165 : {
2166 1628 : if (bHasZ && bHasM)
2167 11 : return SetPointsOfLineStruct<true, true, 4>;
2168 1617 : if (bHasZ)
2169 623 : return SetPointsOfLineStruct<true, false, 3>;
2170 994 : if (bHasM)
2171 11 : return SetPointsOfLineStruct<false, true, 3>;
2172 983 : return SetPointsOfLineStruct<false, false, 2>;
2173 : }
2174 :
2175 : /************************************************************************/
2176 : /* TimestampToOGR() */
2177 : /************************************************************************/
2178 :
2179 : inline void
2180 11300 : OGRArrowLayer::TimestampToOGR(int64_t timestamp,
2181 : const arrow::TimestampType *timestampType,
2182 : int nTZFlag, OGRField *psField)
2183 : {
2184 11300 : const auto unit = timestampType->unit();
2185 11300 : double floatingPart = 0;
2186 11300 : if (unit == arrow::TimeUnit::MILLI)
2187 : {
2188 7478 : floatingPart = (timestamp % 1000) / 1e3;
2189 7478 : timestamp /= 1000;
2190 : }
2191 3822 : else if (unit == arrow::TimeUnit::MICRO)
2192 : {
2193 3330 : floatingPart = (timestamp % (1000 * 1000)) / 1e6;
2194 3330 : timestamp /= 1000 * 1000;
2195 : }
2196 492 : else if (unit == arrow::TimeUnit::NANO)
2197 : {
2198 246 : floatingPart = (timestamp % (1000 * 1000 * 1000)) / 1e9;
2199 246 : timestamp /= 1000 * 1000 * 1000;
2200 : }
2201 11300 : if (nTZFlag > OGR_TZFLAG_MIXED_TZ)
2202 : {
2203 5660 : const int TZOffset = (nTZFlag - OGR_TZFLAG_UTC) * 15;
2204 5660 : timestamp += TZOffset * 60;
2205 : }
2206 : struct tm dt;
2207 11300 : CPLUnixTimeToYMDHMS(timestamp, &dt);
2208 11300 : psField->Date.Year = static_cast<GInt16>(dt.tm_year + 1900);
2209 11300 : psField->Date.Month = static_cast<GByte>(dt.tm_mon + 1);
2210 11300 : psField->Date.Day = static_cast<GByte>(dt.tm_mday);
2211 11300 : psField->Date.Hour = static_cast<GByte>(dt.tm_hour);
2212 11300 : psField->Date.Minute = static_cast<GByte>(dt.tm_min);
2213 11300 : psField->Date.TZFlag = static_cast<GByte>(nTZFlag);
2214 11300 : psField->Date.Second = static_cast<float>(dt.tm_sec + floatingPart);
2215 11300 : }
2216 :
2217 : /************************************************************************/
2218 : /* ReadFeature() */
2219 : /************************************************************************/
2220 :
2221 10899 : inline OGRFeature *OGRArrowLayer::ReadFeature(
2222 : int64_t nIdxInBatch,
2223 : const std::vector<std::shared_ptr<arrow::Array>> &poColumnArrays) const
2224 : {
2225 10899 : OGRFeature *poFeature = new OGRFeature(m_poFeatureDefn);
2226 :
2227 10899 : if (m_iFIDArrowColumn >= 0)
2228 : {
2229 3400 : const int iCol =
2230 3400 : m_bIgnoredFields ? m_nRequestedFIDColumn : m_iFIDArrowColumn;
2231 3400 : const arrow::Array *array = poColumnArrays[iCol].get();
2232 3400 : if (!array->IsNull(nIdxInBatch))
2233 : {
2234 3400 : if (array->type_id() == arrow::Type::INT64)
2235 : {
2236 3400 : const auto castArray =
2237 : static_cast<const arrow::Int64Array *>(array);
2238 3400 : poFeature->SetFID(
2239 3400 : static_cast<GIntBig>(castArray->Value(nIdxInBatch)));
2240 : }
2241 0 : else if (array->type_id() == arrow::Type::INT32)
2242 : {
2243 0 : const auto castArray =
2244 : static_cast<const arrow::Int32Array *>(array);
2245 0 : poFeature->SetFID(castArray->Value(nIdxInBatch));
2246 : }
2247 : }
2248 : }
2249 :
2250 : const auto ReadList =
2251 151416 : [this, poFeature, nIdxInBatch](const auto *array, int iField)
2252 : {
2253 : const auto listType =
2254 50472 : static_cast<const arrow::ListType *>(array->data()->type.get());
2255 :
2256 50472 : if (m_bListsAsStringJson)
2257 : {
2258 168 : poFeature->SetField(
2259 : iField, GetListAsJSON(array, static_cast<size_t>(nIdxInBatch))
2260 : .Format(CPLJSONObject::PrettyFormat::Plain)
2261 : .c_str());
2262 : }
2263 : else
2264 : {
2265 50304 : ::ReadList(poFeature, iField, nIdxInBatch, array,
2266 50304 : listType->value_field()->type()->id());
2267 : }
2268 61371 : };
2269 :
2270 10899 : const int nFieldCount = m_poFeatureDefn->GetFieldCount();
2271 179878 : for (int i = 0; i < nFieldCount; ++i)
2272 : {
2273 : int iCol;
2274 168979 : if (m_bIgnoredFields)
2275 : {
2276 17032 : iCol = m_anMapFieldIndexToArrayIndex[i];
2277 17032 : if (iCol < 0)
2278 5434 : continue;
2279 : }
2280 : else
2281 : {
2282 151947 : iCol = m_anMapFieldIndexToArrowColumn[i][0];
2283 : }
2284 :
2285 163545 : const arrow::Array *array = GetStorageArray(poColumnArrays[iCol].get());
2286 163545 : if (array->IsNull(nIdxInBatch))
2287 : {
2288 17440 : poFeature->SetFieldNull(i);
2289 17440 : continue;
2290 : }
2291 :
2292 146105 : int j = 1;
2293 146105 : bool bSkipToNextField = false;
2294 156898 : if (array->type_id() == arrow::Type::STRUCT &&
2295 156898 : m_poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDateTime &&
2296 19 : m_poFeatureDefn->GetFieldDefn(i)->GetTZFlag() ==
2297 : OGR_TZFLAG_MIXED_TZ)
2298 : {
2299 19 : const auto structArray =
2300 : static_cast<const arrow::StructArray *>(array);
2301 19 : const auto &subArrays = structArray->fields();
2302 19 : CPLAssert(subArrays.size() == 2);
2303 : const auto timestampType = static_cast<arrow::TimestampType *>(
2304 19 : subArrays[0]->data()->type.get());
2305 : const auto timestampArray =
2306 19 : static_cast<const arrow::TimestampArray *>(subArrays[0].get());
2307 19 : const int64_t timestamp = timestampArray->Value(nIdxInBatch);
2308 : const auto offsetMinutesArray =
2309 19 : static_cast<const arrow::Int16Array *>(subArrays[1].get());
2310 19 : const int nOffsetMinutes = offsetMinutesArray->Value(nIdxInBatch);
2311 19 : const int MAX_TIME_ZONE_HOUR = 14;
2312 19 : const int nTZFlag =
2313 17 : nOffsetMinutes >= -MAX_TIME_ZONE_HOUR * 60 &&
2314 : nOffsetMinutes <= MAX_TIME_ZONE_HOUR * 60
2315 36 : ? OGR_TZFLAG_UTC + nOffsetMinutes / 15
2316 : : OGR_TZFLAG_UTC;
2317 : OGRField sField;
2318 19 : sField.Set.nMarker1 = OGRUnsetMarker;
2319 19 : sField.Set.nMarker2 = OGRUnsetMarker;
2320 19 : sField.Set.nMarker3 = OGRUnsetMarker;
2321 19 : TimestampToOGR(timestamp, timestampType, nTZFlag, &sField);
2322 19 : poFeature->SetField(i, &sField);
2323 19 : continue;
2324 : }
2325 : else
2326 : {
2327 160491 : while (array->type_id() == arrow::Type::STRUCT)
2328 : {
2329 14416 : const auto castArray =
2330 : static_cast<const arrow::StructArray *>(array);
2331 14416 : const auto &subArrays = castArray->fields();
2332 14416 : CPLAssert(j < static_cast<int>(
2333 : m_anMapFieldIndexToArrowColumn[i].size()));
2334 14416 : const int iArrowSubcol = m_anMapFieldIndexToArrowColumn[i][j];
2335 14416 : j++;
2336 14416 : CPLAssert(iArrowSubcol < static_cast<int>(subArrays.size()));
2337 14416 : array = GetStorageArray(subArrays[iArrowSubcol].get());
2338 14416 : if (array->IsNull(nIdxInBatch))
2339 : {
2340 11 : poFeature->SetFieldNull(i);
2341 11 : bSkipToNextField = true;
2342 11 : break;
2343 : }
2344 : }
2345 : }
2346 146086 : if (bSkipToNextField)
2347 11 : continue;
2348 :
2349 146075 : if (array->type_id() == arrow::Type::DICTIONARY)
2350 : {
2351 1516 : const auto castArray =
2352 : static_cast<const arrow::DictionaryArray *>(array);
2353 : m_poReadFeatureTmpArray =
2354 1516 : castArray->indices(); // does not return a const reference
2355 1516 : array = GetStorageArray(m_poReadFeatureTmpArray.get());
2356 1516 : if (array->IsNull(nIdxInBatch))
2357 : {
2358 0 : poFeature->SetFieldNull(i);
2359 0 : continue;
2360 : }
2361 : }
2362 :
2363 146075 : switch (array->type_id())
2364 : {
2365 0 : case arrow::Type::NA:
2366 0 : break;
2367 :
2368 1565 : case arrow::Type::BOOL:
2369 : {
2370 1565 : const auto castArray =
2371 : static_cast<const arrow::BooleanArray *>(array);
2372 1565 : poFeature->SetFieldSameTypeUnsafe(
2373 1565 : i, castArray->Value(nIdxInBatch));
2374 1565 : break;
2375 : }
2376 1500 : case arrow::Type::UINT8:
2377 : {
2378 1500 : const auto castArray =
2379 : static_cast<const arrow::UInt8Array *>(array);
2380 1500 : poFeature->SetFieldSameTypeUnsafe(
2381 1500 : i, castArray->Value(nIdxInBatch));
2382 1500 : break;
2383 : }
2384 1496 : case arrow::Type::INT8:
2385 : {
2386 1496 : const auto castArray =
2387 : static_cast<const arrow::Int8Array *>(array);
2388 1496 : poFeature->SetFieldSameTypeUnsafe(
2389 1496 : i, castArray->Value(nIdxInBatch));
2390 1496 : break;
2391 : }
2392 1496 : case arrow::Type::UINT16:
2393 : {
2394 1496 : const auto castArray =
2395 : static_cast<const arrow::UInt16Array *>(array);
2396 1496 : poFeature->SetFieldSameTypeUnsafe(
2397 1496 : i, castArray->Value(nIdxInBatch));
2398 1496 : break;
2399 : }
2400 1574 : case arrow::Type::INT16:
2401 : {
2402 1574 : const auto castArray =
2403 : static_cast<const arrow::Int16Array *>(array);
2404 1574 : poFeature->SetFieldSameTypeUnsafe(
2405 1574 : i, castArray->Value(nIdxInBatch));
2406 1574 : break;
2407 : }
2408 203 : case arrow::Type::UINT32:
2409 : {
2410 203 : const auto castArray =
2411 : static_cast<const arrow::UInt32Array *>(array);
2412 203 : poFeature->SetFieldSameTypeUnsafe(
2413 203 : i, static_cast<GIntBig>(castArray->Value(nIdxInBatch)));
2414 203 : break;
2415 : }
2416 8565 : case arrow::Type::INT32:
2417 : {
2418 8565 : const auto castArray =
2419 : static_cast<const arrow::Int32Array *>(array);
2420 8565 : poFeature->SetFieldSameTypeUnsafe(
2421 : i, castArray->Value(nIdxInBatch));
2422 8565 : break;
2423 : }
2424 1496 : case arrow::Type::UINT64:
2425 : {
2426 1496 : const auto castArray =
2427 : static_cast<const arrow::UInt64Array *>(array);
2428 1496 : poFeature->SetFieldSameTypeUnsafe(
2429 1496 : i, static_cast<double>(castArray->Value(nIdxInBatch)));
2430 1496 : break;
2431 : }
2432 7612 : case arrow::Type::INT64:
2433 : {
2434 7612 : const auto castArray =
2435 : static_cast<const arrow::Int64Array *>(array);
2436 7612 : poFeature->SetFieldSameTypeUnsafe(
2437 7612 : i, static_cast<GIntBig>(castArray->Value(nIdxInBatch)));
2438 7612 : break;
2439 : }
2440 203 : case arrow::Type::HALF_FLOAT:
2441 : {
2442 203 : const auto castArray =
2443 : static_cast<const arrow::HalfFloatArray *>(array);
2444 203 : const uint16_t nFloat16 = castArray->Value(nIdxInBatch);
2445 203 : uint32_t nFloat32 = CPLHalfToFloat(nFloat16);
2446 : float f;
2447 203 : memcpy(&f, &nFloat32, sizeof(nFloat32));
2448 203 : poFeature->SetFieldSameTypeUnsafe(i, f);
2449 203 : break;
2450 : }
2451 1655 : case arrow::Type::FLOAT:
2452 : {
2453 1655 : const auto castArray =
2454 : static_cast<const arrow::FloatArray *>(array);
2455 1655 : poFeature->SetFieldSameTypeUnsafe(
2456 1655 : i, castArray->Value(nIdxInBatch));
2457 1655 : break;
2458 : }
2459 3679 : case arrow::Type::DOUBLE:
2460 : {
2461 3679 : const auto castArray =
2462 : static_cast<const arrow::DoubleArray *>(array);
2463 3679 : poFeature->SetFieldSameTypeUnsafe(
2464 : i, castArray->Value(nIdxInBatch));
2465 3679 : break;
2466 : }
2467 7638 : case arrow::Type::STRING:
2468 : {
2469 7638 : const auto castArray =
2470 : static_cast<const arrow::StringArray *>(array);
2471 7638 : int out_length = 0;
2472 : const uint8_t *data =
2473 7638 : castArray->GetValue(nIdxInBatch, &out_length);
2474 : char *pszString =
2475 7638 : static_cast<char *>(CPLMalloc(out_length + 1));
2476 7638 : memcpy(pszString, data, out_length);
2477 7638 : pszString[out_length] = 0;
2478 7638 : poFeature->SetFieldSameTypeUnsafe(i, pszString);
2479 7638 : break;
2480 : }
2481 1975 : case arrow::Type::BINARY:
2482 : {
2483 1975 : const auto castArray =
2484 : static_cast<const arrow::BinaryArray *>(array);
2485 1975 : int out_length = 0;
2486 : const uint8_t *data =
2487 1975 : castArray->GetValue(nIdxInBatch, &out_length);
2488 1975 : poFeature->SetField(i, out_length, data);
2489 1975 : break;
2490 : }
2491 : #if ARROW_VERSION_MAJOR >= 15
2492 3 : case arrow::Type::BINARY_VIEW:
2493 : {
2494 3 : const auto castArray =
2495 : static_cast<const arrow::BinaryViewArray *>(array);
2496 3 : const auto view = castArray->GetView(nIdxInBatch);
2497 3 : poFeature->SetField(i, static_cast<int>(view.size()),
2498 3 : view.data());
2499 3 : break;
2500 : }
2501 : #endif
2502 : #if ARROW_VERSION_MAJOR >= 15
2503 3 : case arrow::Type::STRING_VIEW:
2504 : {
2505 3 : const auto castArray =
2506 : static_cast<const arrow::StringViewArray *>(array);
2507 3 : const auto strView = castArray->GetView(nIdxInBatch);
2508 : char *pszString =
2509 3 : static_cast<char *>(CPLMalloc(strView.length() + 1));
2510 3 : memcpy(pszString, strView.data(), strView.length());
2511 3 : pszString[strView.length()] = 0;
2512 3 : poFeature->SetFieldSameTypeUnsafe(i, pszString);
2513 3 : break;
2514 : }
2515 : #endif
2516 1879 : case arrow::Type::FIXED_SIZE_BINARY:
2517 : {
2518 1879 : const auto castArray =
2519 : static_cast<const arrow::FixedSizeBinaryArray *>(array);
2520 1879 : const uint8_t *data = castArray->GetValue(nIdxInBatch);
2521 1879 : poFeature->SetField(i, castArray->byte_width(), data);
2522 1879 : break;
2523 : }
2524 3512 : case arrow::Type::DATE32:
2525 : {
2526 : // number of days since Epoch
2527 3512 : const auto castArray =
2528 : static_cast<const arrow::Date32Array *>(array);
2529 : int64_t timestamp =
2530 3512 : static_cast<int64_t>(castArray->Value(nIdxInBatch)) * 3600 *
2531 3512 : 24;
2532 : struct tm dt;
2533 3512 : CPLUnixTimeToYMDHMS(timestamp, &dt);
2534 3512 : poFeature->SetField(i, dt.tm_year + 1900, dt.tm_mon + 1,
2535 : dt.tm_mday, 0, 0, 0);
2536 3512 : break;
2537 : }
2538 246 : case arrow::Type::DATE64:
2539 : {
2540 : // number of milliseconds since Epoch
2541 246 : const auto castArray =
2542 : static_cast<const arrow::Date64Array *>(array);
2543 : int64_t timestamp =
2544 246 : static_cast<int64_t>(castArray->Value(nIdxInBatch)) / 1000;
2545 : struct tm dt;
2546 246 : CPLUnixTimeToYMDHMS(timestamp, &dt);
2547 246 : poFeature->SetField(i, dt.tm_year + 1900, dt.tm_mon + 1,
2548 : dt.tm_mday, 0, 0, 0);
2549 246 : break;
2550 : }
2551 11279 : case arrow::Type::TIMESTAMP:
2552 : {
2553 : const auto timestampType = static_cast<arrow::TimestampType *>(
2554 11279 : array->data()->type.get());
2555 11279 : const auto castArray =
2556 : static_cast<const arrow::TimestampArray *>(array);
2557 11279 : const int64_t timestamp = castArray->Value(nIdxInBatch);
2558 : OGRField sField;
2559 11279 : sField.Set.nMarker1 = OGRUnsetMarker;
2560 11279 : sField.Set.nMarker2 = OGRUnsetMarker;
2561 11279 : sField.Set.nMarker3 = OGRUnsetMarker;
2562 11279 : TimestampToOGR(timestamp, timestampType,
2563 11279 : m_poFeatureDefn->GetFieldDefn(i)->GetTZFlag(),
2564 : &sField);
2565 11279 : poFeature->SetField(i, &sField);
2566 11279 : break;
2567 : }
2568 3383 : case arrow::Type::TIME32:
2569 : {
2570 : const auto timestampType =
2571 3383 : static_cast<arrow::Time32Type *>(array->data()->type.get());
2572 3383 : const auto castArray =
2573 : static_cast<const arrow::Time32Array *>(array);
2574 3383 : const auto unit = timestampType->unit();
2575 3383 : int value = castArray->Value(nIdxInBatch);
2576 3383 : double floatingPart = 0;
2577 3383 : if (unit == arrow::TimeUnit::MILLI)
2578 : {
2579 3178 : floatingPart = (value % 1000) / 1e3;
2580 3178 : value /= 1000;
2581 : }
2582 3383 : const int nHour = value / 3600;
2583 3383 : const int nMinute = (value / 60) % 60;
2584 3383 : const int nSecond = value % 60;
2585 3383 : poFeature->SetField(i, 0, 0, 0, nHour, nMinute,
2586 3383 : static_cast<float>(nSecond + floatingPart));
2587 3383 : break;
2588 : }
2589 3087 : case arrow::Type::TIME64:
2590 : {
2591 3087 : const auto castArray =
2592 : static_cast<const arrow::Time64Array *>(array);
2593 3087 : poFeature->SetField(
2594 3087 : i, static_cast<GIntBig>(castArray->Value(nIdxInBatch)));
2595 3087 : break;
2596 : }
2597 :
2598 : #if ARROW_VERSION_MAJOR >= 18
2599 0 : case arrow::Type::DECIMAL32:
2600 : {
2601 0 : const auto castArray =
2602 : static_cast<const arrow::Decimal32Array *>(array);
2603 0 : poFeature->SetField(
2604 0 : i, CPLAtof(castArray->FormatValue(nIdxInBatch).c_str()));
2605 0 : break;
2606 : }
2607 :
2608 0 : case arrow::Type::DECIMAL64:
2609 : {
2610 0 : const auto castArray =
2611 : static_cast<const arrow::Decimal64Array *>(array);
2612 0 : poFeature->SetField(
2613 0 : i, CPLAtof(castArray->FormatValue(nIdxInBatch).c_str()));
2614 0 : break;
2615 : }
2616 : #endif
2617 :
2618 1575 : case arrow::Type::DECIMAL128:
2619 : {
2620 1575 : const auto castArray =
2621 : static_cast<const arrow::Decimal128Array *>(array);
2622 1575 : poFeature->SetField(
2623 3150 : i, CPLAtof(castArray->FormatValue(nIdxInBatch).c_str()));
2624 1575 : break;
2625 : }
2626 :
2627 1391 : case arrow::Type::DECIMAL256:
2628 : {
2629 1391 : const auto castArray =
2630 : static_cast<const arrow::Decimal256Array *>(array);
2631 1391 : poFeature->SetField(
2632 2782 : i, CPLAtof(castArray->FormatValue(nIdxInBatch).c_str()));
2633 1391 : break;
2634 : }
2635 :
2636 28993 : case arrow::Type::LIST:
2637 : {
2638 28993 : ReadList(static_cast<const arrow::ListArray *>(array), i);
2639 28993 : break;
2640 : }
2641 :
2642 2 : case arrow::Type::LARGE_LIST:
2643 : {
2644 2 : ReadList(static_cast<const arrow::LargeListArray *>(array), i);
2645 2 : break;
2646 : }
2647 :
2648 21477 : case arrow::Type::FIXED_SIZE_LIST:
2649 : {
2650 21477 : ReadList(static_cast<const arrow::FixedSizeListArray *>(array),
2651 : i);
2652 21477 : break;
2653 : }
2654 :
2655 1496 : case arrow::Type::LARGE_STRING:
2656 : {
2657 1496 : const auto castArray =
2658 : static_cast<const arrow::LargeStringArray *>(array);
2659 1496 : poFeature->SetField(i,
2660 2992 : castArray->GetString(nIdxInBatch).c_str());
2661 1496 : break;
2662 : }
2663 1787 : case arrow::Type::LARGE_BINARY:
2664 : {
2665 1787 : const auto castArray =
2666 : static_cast<const arrow::LargeBinaryArray *>(array);
2667 1787 : arrow::LargeBinaryArray::offset_type out_length = 0;
2668 : const uint8_t *data =
2669 1787 : castArray->GetValue(nIdxInBatch, &out_length);
2670 1787 : if (out_length >= 0 && out_length <= INT_MAX - 1)
2671 : {
2672 : // coverity[overflow_sink]
2673 1787 : poFeature->SetField(i, static_cast<int>(out_length), data);
2674 : }
2675 : else
2676 : {
2677 : // this is probably the most likely code path if people use
2678 : // LargeBinary...
2679 0 : CPLError(CE_Warning, CPLE_AppDefined,
2680 : "Too large binary: " CPL_FRMT_GUIB " bytes",
2681 : static_cast<GUIntBig>(out_length));
2682 : }
2683 1787 : break;
2684 : }
2685 :
2686 25305 : case arrow::Type::MAP:
2687 : {
2688 25305 : const auto castArray =
2689 : static_cast<const arrow::MapArray *>(array);
2690 25305 : poFeature->SetField(
2691 25305 : i, GetMapAsJSON(castArray, static_cast<size_t>(nIdxInBatch))
2692 50610 : .Format(CPLJSONObject::PrettyFormat::Plain)
2693 : .c_str());
2694 25305 : break;
2695 : }
2696 :
2697 : // unhandled types
2698 0 : case arrow::Type::STRUCT: // should not happen
2699 : case arrow::Type::INTERVAL_MONTHS:
2700 : case arrow::Type::INTERVAL_DAY_TIME:
2701 : case arrow::Type::SPARSE_UNION:
2702 : case arrow::Type::DENSE_UNION:
2703 : case arrow::Type::DICTIONARY:
2704 : case arrow::Type::EXTENSION:
2705 : case arrow::Type::DURATION:
2706 : case arrow::Type::INTERVAL_MONTH_DAY_NANO:
2707 : #if ARROW_VERSION_MAJOR >= 12
2708 : case arrow::Type::RUN_END_ENCODED:
2709 : #endif
2710 : #if ARROW_VERSION_MAJOR >= 15
2711 : case arrow::Type::LIST_VIEW:
2712 : case arrow::Type::LARGE_LIST_VIEW:
2713 : #endif
2714 : case arrow::Type::MAX_ID:
2715 : {
2716 : // Shouldn't happen normally as we should have discarded those
2717 : // fields when creating OGR field definitions
2718 0 : CPLError(CE_Warning, CPLE_AppDefined,
2719 : "Cannot read content for field %s",
2720 0 : m_poFeatureDefn->GetFieldDefn(i)->GetNameRef());
2721 0 : break;
2722 : }
2723 : }
2724 : }
2725 :
2726 10899 : const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
2727 21446 : for (int i = 0; i < nGeomFieldCount; ++i)
2728 : {
2729 : int iCol;
2730 10547 : if (m_bIgnoredFields)
2731 : {
2732 1513 : iCol = m_anMapGeomFieldIndexToArrayIndex[i];
2733 1513 : if (iCol < 0)
2734 54 : continue;
2735 : }
2736 : else
2737 : {
2738 9034 : iCol = m_anMapGeomFieldIndexToArrowColumn[i];
2739 : }
2740 :
2741 10493 : const auto array = GetStorageArray(poColumnArrays[iCol].get());
2742 10493 : auto poGeometry = ReadGeometry(i, array, nIdxInBatch);
2743 10493 : if (poGeometry)
2744 : {
2745 7791 : const auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(i);
2746 8288 : if (wkbFlatten(poGeometry->getGeometryType()) == wkbLineString &&
2747 497 : wkbFlatten(poGeomFieldDefn->GetType()) == wkbMultiLineString)
2748 : {
2749 : poGeometry =
2750 3 : OGRGeometryFactory::forceToMultiLineString(poGeometry);
2751 : }
2752 8840 : else if (wkbFlatten(poGeometry->getGeometryType()) == wkbPolygon &&
2753 1052 : wkbFlatten(poGeomFieldDefn->GetType()) == wkbMultiPolygon)
2754 : {
2755 : poGeometry =
2756 33 : OGRGeometryFactory::forceToMultiPolygon(poGeometry);
2757 : }
2758 7791 : if (OGR_GT_HasZ(poGeomFieldDefn->GetType()) && !poGeometry->Is3D())
2759 : {
2760 63 : poGeometry->set3D(true);
2761 : }
2762 7791 : poFeature->SetGeomFieldDirectly(i, poGeometry);
2763 : }
2764 : }
2765 :
2766 10899 : return poFeature;
2767 : }
2768 :
2769 : /************************************************************************/
2770 : /* ReadGeometry() */
2771 : /************************************************************************/
2772 :
2773 10713 : inline OGRGeometry *OGRArrowLayer::ReadGeometry(int iGeomField,
2774 : const arrow::Array *array,
2775 : int64_t nIdxInBatch) const
2776 : {
2777 10713 : if (array->IsNull(nIdxInBatch))
2778 : {
2779 2751 : return nullptr;
2780 : }
2781 7962 : OGRGeometry *poGeometry = nullptr;
2782 7962 : const auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(iGeomField);
2783 7962 : const auto eGeomType = poGeomFieldDefn->GetType();
2784 7962 : const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
2785 7962 : const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
2786 7962 : const int nDim = 2 + (bHasZ ? 1 : 0) + (bHasM ? 1 : 0);
2787 :
2788 : const auto CreatePoint =
2789 798 : [bHasZ, bHasM](const arrow::DoubleArray *pointValues, int pointOffset)
2790 : {
2791 399 : if (bHasZ)
2792 : {
2793 199 : if (bHasM)
2794 : {
2795 66 : return new OGRPoint(pointValues->Value(pointOffset),
2796 66 : pointValues->Value(pointOffset + 1),
2797 66 : pointValues->Value(pointOffset + 2),
2798 66 : pointValues->Value(pointOffset + 3));
2799 : }
2800 : else
2801 : {
2802 133 : return new OGRPoint(pointValues->Value(pointOffset),
2803 133 : pointValues->Value(pointOffset + 1),
2804 133 : pointValues->Value(pointOffset + 2));
2805 : }
2806 : }
2807 200 : else if (bHasM)
2808 : {
2809 66 : return OGRPoint::createXYM(pointValues->Value(pointOffset),
2810 66 : pointValues->Value(pointOffset + 1),
2811 132 : pointValues->Value(pointOffset + 2));
2812 : }
2813 : else
2814 : {
2815 134 : return new OGRPoint(pointValues->Value(pointOffset),
2816 134 : pointValues->Value(pointOffset + 1));
2817 : }
2818 7962 : };
2819 :
2820 : const auto CreateStructPoint =
2821 923 : [nDim, bHasZ, bHasM](const arrow::StructArray *structArray,
2822 2769 : int64_t pointOffset)
2823 : {
2824 923 : CPL_IGNORE_RET_VAL(nDim);
2825 923 : CPLAssert(structArray->num_fields() == nDim);
2826 923 : const auto &fieldX = structArray->field(0);
2827 923 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
2828 : const auto fieldXDouble =
2829 923 : static_cast<arrow::DoubleArray *>(fieldX.get());
2830 923 : const auto &fieldY = structArray->field(1);
2831 923 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
2832 : const auto fieldYDouble =
2833 923 : static_cast<arrow::DoubleArray *>(fieldY.get());
2834 923 : if (bHasZ)
2835 : {
2836 406 : const auto &fieldZ = structArray->field(2);
2837 406 : CPLAssert(fieldZ->type_id() == arrow::Type::DOUBLE);
2838 : const auto fieldZDouble =
2839 406 : static_cast<arrow::DoubleArray *>(fieldZ.get());
2840 406 : if (bHasM)
2841 : {
2842 11 : const auto &fieldM = structArray->field(3);
2843 11 : CPLAssert(fieldM->type_id() == arrow::Type::DOUBLE);
2844 : const auto fieldMDouble =
2845 11 : static_cast<arrow::DoubleArray *>(fieldM.get());
2846 11 : return new OGRPoint(fieldXDouble->Value(pointOffset),
2847 11 : fieldYDouble->Value(pointOffset),
2848 11 : fieldZDouble->Value(pointOffset),
2849 11 : fieldMDouble->Value(pointOffset));
2850 : }
2851 : else
2852 : {
2853 395 : return new OGRPoint(fieldXDouble->Value(pointOffset),
2854 395 : fieldYDouble->Value(pointOffset),
2855 395 : fieldZDouble->Value(pointOffset));
2856 : }
2857 : }
2858 517 : else if (bHasM)
2859 : {
2860 11 : const auto &fieldM = structArray->field(2);
2861 11 : CPLAssert(fieldM->type_id() == arrow::Type::DOUBLE);
2862 : const auto fieldMDouble =
2863 11 : static_cast<arrow::DoubleArray *>(fieldM.get());
2864 11 : return OGRPoint::createXYM(fieldXDouble->Value(pointOffset),
2865 : fieldYDouble->Value(pointOffset),
2866 11 : fieldMDouble->Value(pointOffset));
2867 : }
2868 : else
2869 : {
2870 506 : return new OGRPoint(fieldXDouble->Value(pointOffset),
2871 506 : fieldYDouble->Value(pointOffset));
2872 : }
2873 7962 : };
2874 :
2875 : // Arrow 14 since https://github.com/apache/arrow/commit/95a8bfb319b2729c8f6daa069433caba3b4ddddd
2876 : // returns reference to shared pointers, so we can safely take the raw pointer
2877 : // and cast it.
2878 : // Earlier versions returned a non-reference shared pointer, so formally it
2879 : // is safer to use static_pointer_cast (although in practice given that
2880 : // "values" is a member variable), the Arrow >= 14 path might work...
2881 : #if ARROW_VERSION_MAJOR >= 14
2882 : #define GET_PTR_FROM_VALUES(var, type, values) \
2883 : const auto var = static_cast<const type *>((values).get())
2884 : #else
2885 : #define GET_PTR_FROM_VALUES(var, type, values) \
2886 : const auto var##tmp = std::static_pointer_cast<type>(values); \
2887 : const auto var = var##tmp.get()
2888 : #endif
2889 :
2890 7962 : switch (m_aeGeomEncoding[iGeomField])
2891 : {
2892 4270 : case OGRArrowGeomEncoding::WKB:
2893 : {
2894 4270 : int out_length = 0;
2895 : const uint8_t *data;
2896 4270 : if (array->type_id() == arrow::Type::BINARY)
2897 : {
2898 4259 : const auto castArray =
2899 : static_cast<const arrow::BinaryArray *>(array);
2900 4259 : data = castArray->GetValue(nIdxInBatch, &out_length);
2901 : }
2902 : else
2903 : {
2904 11 : CPLAssert(array->type_id() == arrow::Type::LARGE_BINARY);
2905 11 : const auto castArray =
2906 : static_cast<const arrow::LargeBinaryArray *>(array);
2907 11 : int64_t out_length64 = 0;
2908 11 : data = castArray->GetValue(nIdxInBatch, &out_length64);
2909 11 : if (out_length64 > INT_MAX)
2910 : {
2911 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too large geometry");
2912 0 : return nullptr;
2913 : }
2914 11 : out_length = static_cast<int>(out_length64);
2915 : }
2916 4270 : if (OGRGeometryFactory::createFromWkb(
2917 4270 : data, poGeomFieldDefn->GetSpatialRef(), &poGeometry,
2918 4270 : out_length) == OGRERR_NONE)
2919 : {
2920 : #ifdef DEBUG_ReadWKBBoundingBox
2921 : OGREnvelope sEnvelopeFromWKB;
2922 : bool bRet =
2923 : OGRWKBGetBoundingBox(data, out_length, sEnvelopeFromWKB);
2924 : CPLAssert(bRet);
2925 : OGREnvelope sEnvelopeFromGeom;
2926 : poGeometry->getEnvelope(&sEnvelopeFromGeom);
2927 : CPLAssert(sEnvelopeFromWKB == sEnvelopeFromGeom);
2928 : #endif
2929 : }
2930 4270 : break;
2931 : }
2932 :
2933 120 : case OGRArrowGeomEncoding::WKT:
2934 : {
2935 120 : if (array->type_id() == arrow::Type::STRING)
2936 : {
2937 119 : const auto castArray =
2938 : static_cast<const arrow::StringArray *>(array);
2939 238 : const auto osWKT = castArray->GetString(nIdxInBatch);
2940 119 : OGRGeometryFactory::createFromWkt(
2941 119 : osWKT.c_str(), poGeomFieldDefn->GetSpatialRef(),
2942 : &poGeometry);
2943 : }
2944 : else
2945 : {
2946 1 : CPLAssert(array->type_id() == arrow::Type::LARGE_STRING);
2947 1 : const auto castArray =
2948 : static_cast<const arrow::LargeStringArray *>(array);
2949 2 : const auto osWKT = castArray->GetString(nIdxInBatch);
2950 1 : OGRGeometryFactory::createFromWkt(
2951 1 : osWKT.c_str(), poGeomFieldDefn->GetSpatialRef(),
2952 : &poGeometry);
2953 : }
2954 120 : break;
2955 : }
2956 :
2957 0 : case OGRArrowGeomEncoding::GEOARROW_FSL_GENERIC:
2958 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_GENERIC:
2959 : {
2960 0 : CPLAssert(false);
2961 : break;
2962 : }
2963 :
2964 113 : case OGRArrowGeomEncoding::GEOARROW_FSL_POINT:
2965 : {
2966 113 : CPLAssert(array->type_id() == arrow::Type::FIXED_SIZE_LIST);
2967 113 : const auto listArray =
2968 : static_cast<const arrow::FixedSizeListArray *>(array);
2969 113 : CPLAssert(listArray->values()->type_id() == arrow::Type::DOUBLE);
2970 113 : GET_PTR_FROM_VALUES(pointValues, arrow::DoubleArray,
2971 : listArray->values());
2972 113 : if (!pointValues->IsNull(nDim * nIdxInBatch))
2973 : {
2974 113 : poGeometry = CreatePoint(pointValues,
2975 : static_cast<int>(nDim * nIdxInBatch));
2976 113 : poGeometry->assignSpatialReference(
2977 113 : poGeomFieldDefn->GetSpatialRef());
2978 : }
2979 113 : break;
2980 : }
2981 :
2982 100 : case OGRArrowGeomEncoding::GEOARROW_FSL_LINESTRING:
2983 : {
2984 100 : CPLAssert(array->type_id() == arrow::Type::LIST);
2985 100 : const auto listArray = static_cast<const arrow::ListArray *>(array);
2986 100 : CPLAssert(listArray->values()->type_id() ==
2987 : arrow::Type::FIXED_SIZE_LIST);
2988 100 : GET_PTR_FROM_VALUES(listOfPointsValues, arrow::FixedSizeListArray,
2989 : listArray->values());
2990 100 : CPLAssert(listOfPointsValues->values()->type_id() ==
2991 : arrow::Type::DOUBLE);
2992 100 : GET_PTR_FROM_VALUES(pointValues, arrow::DoubleArray,
2993 : listOfPointsValues->values());
2994 100 : const auto nPoints = listArray->value_length(nIdxInBatch);
2995 : const auto nPointOffset =
2996 100 : listArray->value_offset(nIdxInBatch) * nDim;
2997 100 : auto poLineString = new OGRLineString();
2998 100 : poGeometry = poLineString;
2999 100 : poGeometry->assignSpatialReference(
3000 100 : poGeomFieldDefn->GetSpatialRef());
3001 100 : if (nPoints)
3002 : {
3003 64 : GetSetPointsOfLine(bHasZ, bHasM)(poLineString, pointValues,
3004 : nPointOffset, nPoints);
3005 : }
3006 : else
3007 : {
3008 36 : poGeometry->set3D(bHasZ);
3009 36 : poGeometry->setMeasured(bHasM);
3010 : }
3011 100 : break;
3012 : }
3013 :
3014 468 : case OGRArrowGeomEncoding::GEOARROW_FSL_POLYGON:
3015 : {
3016 468 : CPLAssert(array->type_id() == arrow::Type::LIST);
3017 468 : const auto listOfRingsArray =
3018 : static_cast<const arrow::ListArray *>(array);
3019 468 : CPLAssert(listOfRingsArray->values()->type_id() ==
3020 : arrow::Type::LIST);
3021 468 : GET_PTR_FROM_VALUES(listOfRingsValues, arrow::ListArray,
3022 : listOfRingsArray->values());
3023 468 : CPLAssert(listOfRingsValues->values()->type_id() ==
3024 : arrow::Type::FIXED_SIZE_LIST);
3025 468 : GET_PTR_FROM_VALUES(listOfPointsValues, arrow::FixedSizeListArray,
3026 : listOfRingsValues->values());
3027 468 : CPLAssert(listOfPointsValues->values()->type_id() ==
3028 : arrow::Type::DOUBLE);
3029 468 : GET_PTR_FROM_VALUES(pointValues, arrow::DoubleArray,
3030 : listOfPointsValues->values());
3031 468 : const auto setPointsFun = GetSetPointsOfLine(bHasZ, bHasM);
3032 468 : const auto nRings = listOfRingsArray->value_length(nIdxInBatch);
3033 : const auto nRingOffset =
3034 468 : listOfRingsArray->value_offset(nIdxInBatch);
3035 468 : auto poPoly = new OGRPolygon();
3036 468 : poGeometry = poPoly;
3037 468 : poGeometry->assignSpatialReference(
3038 468 : poGeomFieldDefn->GetSpatialRef());
3039 954 : for (auto k = decltype(nRings){0}; k < nRings; k++)
3040 : {
3041 : const auto nPoints =
3042 486 : listOfRingsValues->value_length(nRingOffset + k);
3043 : const auto nPointOffset =
3044 486 : listOfRingsValues->value_offset(nRingOffset + k) * nDim;
3045 486 : auto poRing = new OGRLinearRing();
3046 486 : if (nPoints)
3047 : {
3048 486 : setPointsFun(poRing, pointValues, nPointOffset, nPoints);
3049 : }
3050 486 : poPoly->addRingDirectly(poRing);
3051 : }
3052 468 : if (poGeometry->IsEmpty())
3053 : {
3054 122 : poGeometry->set3D(bHasZ);
3055 122 : poGeometry->setMeasured(bHasM);
3056 : }
3057 468 : break;
3058 : }
3059 :
3060 148 : case OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOINT:
3061 : {
3062 148 : CPLAssert(array->type_id() == arrow::Type::LIST);
3063 148 : const auto listArray = static_cast<const arrow::ListArray *>(array);
3064 148 : CPLAssert(listArray->values()->type_id() ==
3065 : arrow::Type::FIXED_SIZE_LIST);
3066 148 : GET_PTR_FROM_VALUES(listOfPointsValues, arrow::FixedSizeListArray,
3067 : listArray->values());
3068 148 : CPLAssert(listOfPointsValues->values()->type_id() ==
3069 : arrow::Type::DOUBLE);
3070 148 : GET_PTR_FROM_VALUES(pointValues, arrow::DoubleArray,
3071 : listOfPointsValues->values());
3072 148 : const auto nPoints = listArray->value_length(nIdxInBatch);
3073 : const auto nPointOffset =
3074 148 : listArray->value_offset(nIdxInBatch) * nDim;
3075 148 : auto poMultiPoint = new OGRMultiPoint();
3076 148 : poGeometry = poMultiPoint;
3077 148 : poGeometry->assignSpatialReference(
3078 148 : poGeomFieldDefn->GetSpatialRef());
3079 434 : for (auto k = decltype(nPoints){0}; k < nPoints; k++)
3080 : {
3081 572 : poMultiPoint->addGeometryDirectly(
3082 286 : CreatePoint(pointValues, nPointOffset + k * nDim));
3083 : }
3084 148 : if (poGeometry->IsEmpty())
3085 : {
3086 34 : poGeometry->set3D(bHasZ);
3087 34 : poGeometry->setMeasured(bHasM);
3088 : }
3089 148 : break;
3090 : }
3091 :
3092 144 : case OGRArrowGeomEncoding::GEOARROW_FSL_MULTILINESTRING:
3093 : {
3094 144 : CPLAssert(array->type_id() == arrow::Type::LIST);
3095 144 : const auto listOfStringsArray =
3096 : static_cast<const arrow::ListArray *>(array);
3097 144 : CPLAssert(listOfStringsArray->values()->type_id() ==
3098 : arrow::Type::LIST);
3099 144 : GET_PTR_FROM_VALUES(listOfStringsValues, arrow::ListArray,
3100 : listOfStringsArray->values());
3101 144 : CPLAssert(listOfStringsValues->values()->type_id() ==
3102 : arrow::Type::FIXED_SIZE_LIST);
3103 144 : GET_PTR_FROM_VALUES(listOfPointsValues, arrow::FixedSizeListArray,
3104 : listOfStringsValues->values());
3105 144 : CPLAssert(listOfPointsValues->values()->type_id() ==
3106 : arrow::Type::DOUBLE);
3107 144 : GET_PTR_FROM_VALUES(pointValues, arrow::DoubleArray,
3108 : listOfPointsValues->values());
3109 144 : const auto setPointsFun = GetSetPointsOfLine(bHasZ, bHasM);
3110 144 : const auto nStrings = listOfStringsArray->value_length(nIdxInBatch);
3111 : const auto nRingOffset =
3112 144 : listOfStringsArray->value_offset(nIdxInBatch);
3113 144 : auto poMLS = new OGRMultiLineString();
3114 144 : poGeometry = poMLS;
3115 144 : poGeometry->assignSpatialReference(
3116 144 : poGeomFieldDefn->GetSpatialRef());
3117 316 : for (auto k = decltype(nStrings){0}; k < nStrings; k++)
3118 : {
3119 : const auto nPoints =
3120 172 : listOfStringsValues->value_length(nRingOffset + k);
3121 : const auto nPointOffset =
3122 172 : listOfStringsValues->value_offset(nRingOffset + k) * nDim;
3123 172 : auto poLS = new OGRLineString();
3124 172 : if (nPoints)
3125 : {
3126 172 : setPointsFun(poLS, pointValues, nPointOffset, nPoints);
3127 : }
3128 172 : poMLS->addGeometryDirectly(poLS);
3129 : }
3130 144 : if (poGeometry->IsEmpty())
3131 : {
3132 36 : poGeometry->set3D(bHasZ);
3133 36 : poGeometry->setMeasured(bHasM);
3134 : }
3135 144 : break;
3136 : }
3137 :
3138 160 : case OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOLYGON:
3139 : {
3140 160 : CPLAssert(array->type_id() == arrow::Type::LIST);
3141 160 : const auto listOfPartsArray =
3142 : static_cast<const arrow::ListArray *>(array);
3143 160 : CPLAssert(listOfPartsArray->values()->type_id() ==
3144 : arrow::Type::LIST);
3145 160 : GET_PTR_FROM_VALUES(listOfPartsValues, arrow::ListArray,
3146 : listOfPartsArray->values());
3147 160 : CPLAssert(listOfPartsValues->values()->type_id() ==
3148 : arrow::Type::LIST);
3149 160 : GET_PTR_FROM_VALUES(listOfRingsValues, arrow::ListArray,
3150 : listOfPartsValues->values());
3151 160 : CPLAssert(listOfRingsValues->values()->type_id() ==
3152 : arrow::Type::FIXED_SIZE_LIST);
3153 160 : GET_PTR_FROM_VALUES(listOfPointsValues, arrow::FixedSizeListArray,
3154 : listOfRingsValues->values());
3155 160 : CPLAssert(listOfPointsValues->values()->type_id() ==
3156 : arrow::Type::DOUBLE);
3157 160 : GET_PTR_FROM_VALUES(pointValues, arrow::DoubleArray,
3158 : listOfPointsValues->values());
3159 160 : auto poMP = new OGRMultiPolygon();
3160 160 : poGeometry = poMP;
3161 160 : poGeometry->assignSpatialReference(
3162 160 : poGeomFieldDefn->GetSpatialRef());
3163 160 : const auto setPointsFun = GetSetPointsOfLine(bHasZ, bHasM);
3164 160 : const auto nParts = listOfPartsArray->value_length(nIdxInBatch);
3165 : const auto nPartOffset =
3166 160 : listOfPartsArray->value_offset(nIdxInBatch);
3167 356 : for (auto j = decltype(nParts){0}; j < nParts; j++)
3168 : {
3169 : const auto nRings =
3170 196 : listOfPartsValues->value_length(nPartOffset + j);
3171 : const auto nRingOffset =
3172 196 : listOfPartsValues->value_offset(nPartOffset + j);
3173 196 : auto poPoly = new OGRPolygon();
3174 434 : for (auto k = decltype(nRings){0}; k < nRings; k++)
3175 : {
3176 : const auto nPoints =
3177 238 : listOfRingsValues->value_length(nRingOffset + k);
3178 : const auto nPointOffset =
3179 238 : listOfRingsValues->value_offset(nRingOffset + k) * nDim;
3180 238 : auto poRing = new OGRLinearRing();
3181 238 : if (nPoints)
3182 : {
3183 238 : setPointsFun(poRing, pointValues, nPointOffset,
3184 : nPoints);
3185 : }
3186 238 : poPoly->addRingDirectly(poRing);
3187 : }
3188 196 : poMP->addGeometryDirectly(poPoly);
3189 : }
3190 160 : if (poGeometry->IsEmpty())
3191 : {
3192 36 : poGeometry->set3D(bHasZ);
3193 36 : poGeometry->setMeasured(bHasM);
3194 : }
3195 160 : break;
3196 : }
3197 :
3198 407 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_POINT:
3199 : {
3200 407 : CPLAssert(array->type_id() == arrow::Type::STRUCT);
3201 407 : const auto structArray =
3202 : static_cast<const arrow::StructArray *>(array);
3203 407 : if (!structArray->IsNull(nIdxInBatch))
3204 : {
3205 407 : poGeometry = CreateStructPoint(structArray, nIdxInBatch);
3206 407 : poGeometry->assignSpatialReference(
3207 407 : poGeomFieldDefn->GetSpatialRef());
3208 : }
3209 407 : break;
3210 : }
3211 :
3212 296 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_LINESTRING:
3213 : {
3214 296 : CPLAssert(array->type_id() == arrow::Type::LIST);
3215 296 : const auto listArray = static_cast<const arrow::ListArray *>(array);
3216 296 : CPLAssert(listArray->values()->type_id() == arrow::Type::STRUCT);
3217 296 : GET_PTR_FROM_VALUES(pointValues, arrow::StructArray,
3218 : listArray->values());
3219 296 : const auto nPoints = listArray->value_length(nIdxInBatch);
3220 296 : const auto nPointOffset = listArray->value_offset(nIdxInBatch);
3221 296 : auto poLineString = new OGRLineString();
3222 296 : poGeometry = poLineString;
3223 296 : poGeometry->assignSpatialReference(
3224 296 : poGeomFieldDefn->GetSpatialRef());
3225 296 : if (nPoints)
3226 : {
3227 244 : GetSetPointsOfLineStruct(bHasZ, bHasM)(
3228 : poLineString, pointValues, nPointOffset, nPoints);
3229 : }
3230 : else
3231 : {
3232 52 : poGeometry->set3D(bHasZ);
3233 52 : poGeometry->setMeasured(bHasM);
3234 : }
3235 296 : break;
3236 : }
3237 :
3238 468 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_POLYGON:
3239 : {
3240 468 : CPLAssert(array->type_id() == arrow::Type::LIST);
3241 468 : const auto listOfRingsArray =
3242 : static_cast<const arrow::ListArray *>(array);
3243 468 : CPLAssert(listOfRingsArray->values()->type_id() ==
3244 : arrow::Type::LIST);
3245 468 : GET_PTR_FROM_VALUES(listOfRingsValues, arrow::ListArray,
3246 : listOfRingsArray->values());
3247 468 : CPLAssert(listOfRingsValues->values()->type_id() ==
3248 : arrow::Type::STRUCT);
3249 468 : GET_PTR_FROM_VALUES(pointValues, arrow::StructArray,
3250 : listOfRingsValues->values());
3251 468 : const auto setPointsFun = GetSetPointsOfLineStruct(bHasZ, bHasM);
3252 468 : const auto nRings = listOfRingsArray->value_length(nIdxInBatch);
3253 : const auto nRingOffset =
3254 468 : listOfRingsArray->value_offset(nIdxInBatch);
3255 468 : auto poPoly = new OGRPolygon();
3256 468 : poGeometry = poPoly;
3257 468 : poGeometry->assignSpatialReference(
3258 468 : poGeomFieldDefn->GetSpatialRef());
3259 960 : for (auto k = decltype(nRings){0}; k < nRings; k++)
3260 : {
3261 : const auto nPoints =
3262 492 : listOfRingsValues->value_length(nRingOffset + k);
3263 : const auto nPointOffset =
3264 492 : listOfRingsValues->value_offset(nRingOffset + k);
3265 492 : auto poRing = new OGRLinearRing();
3266 492 : if (nPoints)
3267 : {
3268 492 : setPointsFun(poRing, pointValues, nPointOffset, nPoints);
3269 : }
3270 492 : poPoly->addRingDirectly(poRing);
3271 : }
3272 468 : if (poGeometry->IsEmpty())
3273 : {
3274 76 : poGeometry->set3D(bHasZ);
3275 76 : poGeometry->setMeasured(bHasM);
3276 : }
3277 468 : break;
3278 : }
3279 :
3280 352 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOINT:
3281 : {
3282 352 : CPLAssert(array->type_id() == arrow::Type::LIST);
3283 352 : const auto listArray = static_cast<const arrow::ListArray *>(array);
3284 352 : CPLAssert(listArray->values()->type_id() == arrow::Type::STRUCT);
3285 352 : GET_PTR_FROM_VALUES(pointValues, arrow::StructArray,
3286 : listArray->values());
3287 352 : const auto nPoints = listArray->value_length(nIdxInBatch);
3288 352 : const auto nPointOffset = listArray->value_offset(nIdxInBatch);
3289 352 : auto poMultiPoint = new OGRMultiPoint();
3290 352 : poGeometry = poMultiPoint;
3291 352 : poGeometry->assignSpatialReference(
3292 352 : poGeomFieldDefn->GetSpatialRef());
3293 868 : for (auto k = decltype(nPoints){0}; k < nPoints; k++)
3294 : {
3295 1032 : poMultiPoint->addGeometryDirectly(
3296 516 : CreateStructPoint(pointValues, nPointOffset + k));
3297 : }
3298 352 : if (poGeometry->IsEmpty())
3299 : {
3300 52 : poGeometry->set3D(bHasZ);
3301 52 : poGeometry->setMeasured(bHasM);
3302 : }
3303 352 : break;
3304 : }
3305 :
3306 420 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTILINESTRING:
3307 : {
3308 420 : CPLAssert(array->type_id() == arrow::Type::LIST);
3309 420 : const auto listOfStringsArray =
3310 : static_cast<const arrow::ListArray *>(array);
3311 420 : CPLAssert(listOfStringsArray->values()->type_id() ==
3312 : arrow::Type::LIST);
3313 420 : GET_PTR_FROM_VALUES(listOfStringsValues, arrow::ListArray,
3314 : listOfStringsArray->values());
3315 420 : CPLAssert(listOfStringsValues->values()->type_id() ==
3316 : arrow::Type::STRUCT);
3317 420 : GET_PTR_FROM_VALUES(pointValues, arrow::StructArray,
3318 : listOfStringsValues->values());
3319 420 : const auto setPointsFun = GetSetPointsOfLineStruct(bHasZ, bHasM);
3320 420 : const auto nStrings = listOfStringsArray->value_length(nIdxInBatch);
3321 : const auto nRingOffset =
3322 420 : listOfStringsArray->value_offset(nIdxInBatch);
3323 420 : auto poMLS = new OGRMultiLineString();
3324 420 : poGeometry = poMLS;
3325 420 : poGeometry->assignSpatialReference(
3326 420 : poGeomFieldDefn->GetSpatialRef());
3327 1032 : for (auto k = decltype(nStrings){0}; k < nStrings; k++)
3328 : {
3329 : const auto nPoints =
3330 612 : listOfStringsValues->value_length(nRingOffset + k);
3331 : const auto nPointOffset =
3332 612 : listOfStringsValues->value_offset(nRingOffset + k);
3333 612 : auto poLS = new OGRLineString();
3334 612 : if (nPoints)
3335 : {
3336 612 : setPointsFun(poLS, pointValues, nPointOffset, nPoints);
3337 : }
3338 612 : poMLS->addGeometryDirectly(poLS);
3339 : }
3340 420 : if (poGeometry->IsEmpty())
3341 : {
3342 52 : poGeometry->set3D(bHasZ);
3343 52 : poGeometry->setMeasured(bHasM);
3344 : }
3345 420 : break;
3346 : }
3347 :
3348 496 : case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOLYGON:
3349 : {
3350 496 : CPLAssert(array->type_id() == arrow::Type::LIST);
3351 496 : const auto listOfPartsArray =
3352 : static_cast<const arrow::ListArray *>(array);
3353 496 : CPLAssert(listOfPartsArray->values()->type_id() ==
3354 : arrow::Type::LIST);
3355 496 : GET_PTR_FROM_VALUES(listOfPartsValues, arrow::ListArray,
3356 : listOfPartsArray->values());
3357 496 : CPLAssert(listOfPartsValues->values()->type_id() ==
3358 : arrow::Type::LIST);
3359 496 : GET_PTR_FROM_VALUES(listOfRingsValues, arrow::ListArray,
3360 : listOfPartsValues->values());
3361 496 : CPLAssert(listOfRingsValues->values()->type_id() ==
3362 : arrow::Type::STRUCT);
3363 496 : GET_PTR_FROM_VALUES(pointValues, arrow::StructArray,
3364 : listOfRingsValues->values());
3365 496 : auto poMP = new OGRMultiPolygon();
3366 496 : poGeometry = poMP;
3367 496 : poGeometry->assignSpatialReference(
3368 496 : poGeomFieldDefn->GetSpatialRef());
3369 496 : const auto setPointsFun = GetSetPointsOfLineStruct(bHasZ, bHasM);
3370 496 : const auto nParts = listOfPartsArray->value_length(nIdxInBatch);
3371 : const auto nPartOffset =
3372 496 : listOfPartsArray->value_offset(nIdxInBatch);
3373 1116 : for (auto j = decltype(nParts){0}; j < nParts; j++)
3374 : {
3375 : const auto nRings =
3376 620 : listOfPartsValues->value_length(nPartOffset + j);
3377 : const auto nRingOffset =
3378 620 : listOfPartsValues->value_offset(nPartOffset + j);
3379 620 : auto poPoly = new OGRPolygon();
3380 1388 : for (auto k = decltype(nRings){0}; k < nRings; k++)
3381 : {
3382 : const auto nPoints =
3383 768 : listOfRingsValues->value_length(nRingOffset + k);
3384 : const auto nPointOffset =
3385 768 : listOfRingsValues->value_offset(nRingOffset + k);
3386 768 : auto poRing = new OGRLinearRing();
3387 768 : if (nPoints)
3388 : {
3389 768 : setPointsFun(poRing, pointValues, nPointOffset,
3390 : nPoints);
3391 : }
3392 768 : poPoly->addRingDirectly(poRing);
3393 : }
3394 620 : poMP->addGeometryDirectly(poPoly);
3395 : }
3396 496 : if (poGeometry->IsEmpty())
3397 : {
3398 76 : poGeometry->set3D(bHasZ);
3399 76 : poGeometry->setMeasured(bHasM);
3400 : }
3401 496 : break;
3402 : }
3403 : }
3404 7962 : return poGeometry;
3405 : }
3406 :
3407 : /************************************************************************/
3408 : /* ResetReading() */
3409 : /************************************************************************/
3410 :
3411 8891 : inline void OGRArrowLayer::ResetReading()
3412 : {
3413 8891 : m_bEOF = false;
3414 8891 : m_nFeatureIdx = 0;
3415 8891 : m_nIdxInBatch = 0;
3416 8891 : m_poReadFeatureTmpArray.reset();
3417 8891 : if (m_iRecordBatch != 0)
3418 : {
3419 7922 : m_iRecordBatch = -1;
3420 7922 : m_poBatch.reset();
3421 7922 : m_poBatchColumns.clear();
3422 : }
3423 8891 : }
3424 :
3425 : /************************************************************************/
3426 : /* GetColumnSubNode() */
3427 : /************************************************************************/
3428 :
3429 : /* static*/
3430 : inline const swq_expr_node *
3431 290 : OGRArrowLayer::GetColumnSubNode(const swq_expr_node *poNode)
3432 : {
3433 290 : if (poNode->eNodeType == SNT_OPERATION && poNode->nSubExprCount == 2)
3434 : {
3435 290 : if (poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN)
3436 208 : return poNode->papoSubExpr[0];
3437 82 : if (poNode->papoSubExpr[1]->eNodeType == SNT_COLUMN)
3438 12 : return poNode->papoSubExpr[1];
3439 : }
3440 70 : return nullptr;
3441 : }
3442 :
3443 : /************************************************************************/
3444 : /* GetConstantSubNode() */
3445 : /************************************************************************/
3446 :
3447 : /* static */
3448 : inline const swq_expr_node *
3449 290 : OGRArrowLayer::GetConstantSubNode(const swq_expr_node *poNode)
3450 : {
3451 290 : if (poNode->eNodeType == SNT_OPERATION && poNode->nSubExprCount == 2)
3452 : {
3453 290 : if (poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT)
3454 276 : return poNode->papoSubExpr[1];
3455 14 : if (poNode->papoSubExpr[0]->eNodeType == SNT_CONSTANT)
3456 12 : return poNode->papoSubExpr[0];
3457 : }
3458 2 : return nullptr;
3459 : }
3460 :
3461 : /************************************************************************/
3462 : /* IsComparisonOp() */
3463 : /************************************************************************/
3464 :
3465 : /* static*/
3466 440 : inline bool OGRArrowLayer::IsComparisonOp(int op)
3467 : {
3468 166 : return (op == SWQ_EQ || op == SWQ_NE || op == SWQ_LT || op == SWQ_LE ||
3469 606 : op == SWQ_GT || op == SWQ_GE);
3470 : }
3471 :
3472 : /************************************************************************/
3473 : /* FillTargetValueFromSrcExpr() */
3474 : /************************************************************************/
3475 :
3476 218 : static bool FillTargetValueFromSrcExpr(const OGRFieldDefn *poFieldDefn,
3477 : OGRArrowLayer::Constraint *psConstraint,
3478 : const swq_expr_node *poSrcValue)
3479 : {
3480 218 : switch (poFieldDefn->GetType())
3481 : {
3482 112 : case OFTInteger:
3483 112 : psConstraint->eType = OGRArrowLayer::Constraint::Type::Integer;
3484 112 : if (poSrcValue->field_type == SWQ_FLOAT)
3485 2 : psConstraint->sValue.Integer =
3486 2 : static_cast<int>(poSrcValue->float_value);
3487 : else
3488 110 : psConstraint->sValue.Integer =
3489 110 : static_cast<int>(poSrcValue->int_value);
3490 : psConstraint->osValue =
3491 112 : std::to_string(psConstraint->sValue.Integer);
3492 112 : break;
3493 :
3494 30 : case OFTInteger64:
3495 30 : psConstraint->eType = OGRArrowLayer::Constraint::Type::Integer64;
3496 30 : if (poSrcValue->field_type == SWQ_FLOAT)
3497 0 : psConstraint->sValue.Integer64 =
3498 0 : static_cast<GIntBig>(poSrcValue->float_value);
3499 : else
3500 30 : psConstraint->sValue.Integer64 = poSrcValue->int_value;
3501 : psConstraint->osValue =
3502 30 : std::to_string(psConstraint->sValue.Integer64);
3503 30 : break;
3504 :
3505 25 : case OFTReal:
3506 25 : psConstraint->eType = OGRArrowLayer::Constraint::Type::Real;
3507 25 : psConstraint->sValue.Real = poSrcValue->float_value;
3508 25 : psConstraint->osValue = std::to_string(psConstraint->sValue.Real);
3509 25 : break;
3510 :
3511 26 : case OFTString:
3512 26 : psConstraint->eType = OGRArrowLayer::Constraint::Type::String;
3513 26 : psConstraint->sValue.String = poSrcValue->string_value;
3514 26 : psConstraint->osValue = psConstraint->sValue.String;
3515 26 : break;
3516 : #ifdef not_yet_handled
3517 : case OFTDate:
3518 : case OFTTime:
3519 : case OFTDateTime:
3520 : if (poSrcValue->field_type == SWQ_TIMESTAMP ||
3521 : poSrcValue->field_type == SWQ_DATE ||
3522 : poSrcValue->field_type == SWQ_TIME)
3523 : {
3524 : int nYear = 0, nMonth = 0, nDay = 0, nHour = 0, nMin = 0,
3525 : nSec = 0;
3526 : if (sscanf(poSrcValue->string_value,
3527 : "%04d/%02d/%02d %02d:%02d:%02d", &nYear, &nMonth,
3528 : &nDay, &nHour, &nMin, &nSec) == 6 ||
3529 : sscanf(poSrcValue->string_value, "%04d/%02d/%02d", &nYear,
3530 : &nMonth, &nDay) == 3 ||
3531 : sscanf(poSrcValue->string_value, "%02d:%02d:%02d", &nHour,
3532 : &nMin, &nSec) == 3)
3533 : {
3534 : psConstraint->eType =
3535 : OGRArrowLayer::Constraint::Type::DateTime;
3536 : psConstraint->sValue.Date.Year = (GInt16)nYear;
3537 : psConstraint->sValue.Date.Month = (GByte)nMonth;
3538 : psConstraint->sValue.Date.Day = (GByte)nDay;
3539 : psConstraint->sValue.Date.Hour = (GByte)nHour;
3540 : psConstraint->sValue.Date.Minute = (GByte)nMin;
3541 : psConstraint->sValue.Date.Second = (GByte)nSec;
3542 : psConstraint->sValue.Date.TZFlag = 0;
3543 : psConstraint->sValue.Date.Reserved = 0;
3544 : }
3545 : else
3546 : return false;
3547 : }
3548 : else
3549 : return false;
3550 : break;
3551 : #endif
3552 25 : default:
3553 25 : return false;
3554 : }
3555 193 : return true;
3556 : }
3557 :
3558 : /************************************************************************/
3559 : /* ComputeConstraintsArrayIdx() */
3560 : /************************************************************************/
3561 :
3562 576 : inline void OGRArrowLayer::ComputeConstraintsArrayIdx()
3563 : {
3564 783 : for (auto &constraint : m_asAttributeFilterConstraints)
3565 : {
3566 208 : if (m_bIgnoredFields)
3567 : {
3568 25 : if (constraint.iField == m_poFeatureDefn->GetFieldCount() + SPF_FID)
3569 : {
3570 1 : constraint.iArrayIdx = m_nRequestedFIDColumn;
3571 1 : if (constraint.iArrayIdx < 0 && m_osFIDColumn.empty())
3572 1 : return;
3573 : }
3574 : else
3575 : {
3576 24 : constraint.iArrayIdx =
3577 24 : m_anMapFieldIndexToArrayIndex[constraint.iField];
3578 : }
3579 24 : if (constraint.iArrayIdx < 0)
3580 : {
3581 1 : CPLError(CE_Failure, CPLE_AppDefined,
3582 : "Constraint on field %s cannot be applied due to "
3583 : "it being ignored",
3584 1 : constraint.iField ==
3585 1 : m_poFeatureDefn->GetFieldCount() + SPF_FID
3586 0 : ? m_osFIDColumn.c_str()
3587 1 : : m_poFeatureDefn->GetFieldDefn(constraint.iField)
3588 1 : ->GetNameRef());
3589 : }
3590 : }
3591 : else
3592 : {
3593 183 : if (constraint.iField == m_poFeatureDefn->GetFieldCount() + SPF_FID)
3594 : {
3595 8 : constraint.iArrayIdx = m_iFIDArrowColumn;
3596 8 : if (constraint.iArrayIdx < 0 && !m_osFIDColumn.empty())
3597 : {
3598 0 : CPLDebug(GetDriverUCName().c_str(),
3599 : "Constraint on field %s cannot be applied",
3600 : m_osFIDColumn.c_str());
3601 : }
3602 : }
3603 : else
3604 : {
3605 175 : constraint.iArrayIdx =
3606 175 : m_anMapFieldIndexToArrowColumn[constraint.iField][0];
3607 : }
3608 : }
3609 : }
3610 : }
3611 :
3612 : /************************************************************************/
3613 : /* ExploreExprNode() */
3614 : /************************************************************************/
3615 :
3616 354 : inline void OGRArrowLayer::ExploreExprNode(const swq_expr_node *poNode)
3617 : {
3618 205 : const auto AddConstraint = [this](Constraint &constraint)
3619 205 : { m_asAttributeFilterConstraints.emplace_back(constraint); };
3620 :
3621 354 : if (poNode->eNodeType == SNT_OPERATION && poNode->nOperation == SWQ_AND &&
3622 19 : poNode->nSubExprCount == 2)
3623 : {
3624 19 : ExploreExprNode(poNode->papoSubExpr[0]);
3625 19 : ExploreExprNode(poNode->papoSubExpr[1]);
3626 : }
3627 :
3628 335 : else if (poNode->eNodeType == SNT_OPERATION &&
3629 670 : IsComparisonOp(poNode->nOperation) && poNode->nSubExprCount == 2)
3630 : {
3631 290 : const swq_expr_node *poColumn = GetColumnSubNode(poNode);
3632 290 : const swq_expr_node *poValue = GetConstantSubNode(poNode);
3633 508 : if (poColumn != nullptr && poValue != nullptr &&
3634 218 : (poColumn->field_index < m_poFeatureDefn->GetFieldCount() ||
3635 8 : poColumn->field_index ==
3636 8 : m_poFeatureDefn->GetFieldCount() + SPF_FID))
3637 : {
3638 : const OGRFieldDefn oDummyFIDFieldDefn(m_osFIDColumn.c_str(),
3639 436 : OFTInteger64);
3640 : const OGRFieldDefn *poFieldDefn =
3641 218 : (poColumn->field_index ==
3642 218 : m_poFeatureDefn->GetFieldCount() + SPF_FID)
3643 218 : ? &oDummyFIDFieldDefn
3644 210 : : m_poFeatureDefn->GetFieldDefn(poColumn->field_index);
3645 :
3646 436 : Constraint constraint;
3647 218 : constraint.iField = poColumn->field_index;
3648 218 : constraint.nOperation = poNode->nOperation;
3649 :
3650 218 : if (FillTargetValueFromSrcExpr(poFieldDefn, &constraint, poValue))
3651 : {
3652 193 : if (poColumn == poNode->papoSubExpr[0])
3653 : {
3654 : // nothing to do
3655 : }
3656 : else
3657 : {
3658 : /* If "constant op column", then we must reverse */
3659 : /* the operator for LE, LT, GE, GT */
3660 12 : switch (poNode->nOperation)
3661 : {
3662 2 : case SWQ_LE:
3663 2 : constraint.nOperation = SWQ_GE;
3664 2 : break;
3665 2 : case SWQ_LT:
3666 2 : constraint.nOperation = SWQ_GT;
3667 2 : break;
3668 2 : case SWQ_NE: /* do nothing */;
3669 2 : break;
3670 2 : case SWQ_EQ: /* do nothing */;
3671 2 : break;
3672 2 : case SWQ_GE:
3673 2 : constraint.nOperation = SWQ_LE;
3674 2 : break;
3675 2 : case SWQ_GT:
3676 2 : constraint.nOperation = SWQ_LT;
3677 2 : break;
3678 0 : default:
3679 0 : CPLAssert(false);
3680 : break;
3681 : }
3682 : }
3683 :
3684 193 : AddConstraint(constraint);
3685 : }
3686 : }
3687 : }
3688 :
3689 45 : else if (poNode->eNodeType == SNT_OPERATION &&
3690 45 : poNode->nOperation == SWQ_ISNULL && poNode->nSubExprCount == 1)
3691 : {
3692 4 : const swq_expr_node *poColumn = poNode->papoSubExpr[0];
3693 8 : if (poColumn->eNodeType == SNT_COLUMN &&
3694 4 : poColumn->field_index < m_poFeatureDefn->GetFieldCount())
3695 : {
3696 8 : Constraint constraint;
3697 4 : constraint.iField = poColumn->field_index;
3698 4 : constraint.nOperation = poNode->nOperation;
3699 4 : AddConstraint(constraint);
3700 4 : }
3701 : }
3702 :
3703 41 : else if (poNode->eNodeType == SNT_OPERATION &&
3704 41 : poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1 &&
3705 20 : poNode->papoSubExpr[0]->eNodeType == SNT_OPERATION &&
3706 20 : poNode->papoSubExpr[0]->nOperation == SWQ_ISNULL &&
3707 16 : poNode->papoSubExpr[0]->nSubExprCount == 1)
3708 : {
3709 16 : const swq_expr_node *poColumn = poNode->papoSubExpr[0]->papoSubExpr[0];
3710 32 : if (poColumn->eNodeType == SNT_COLUMN &&
3711 16 : poColumn->field_index < m_poFeatureDefn->GetFieldCount())
3712 : {
3713 16 : Constraint constraint;
3714 8 : constraint.iField = poColumn->field_index;
3715 8 : constraint.nOperation = SWQ_ISNOTNULL;
3716 8 : AddConstraint(constraint);
3717 : }
3718 : }
3719 354 : }
3720 :
3721 : /************************************************************************/
3722 : /* SetAttributeFilter() */
3723 : /************************************************************************/
3724 :
3725 755 : inline OGRErr OGRArrowLayer::SetAttributeFilter(const char *pszFilter)
3726 : {
3727 755 : m_asAttributeFilterConstraints.clear();
3728 :
3729 : // When changing filters, we need to invalidate cached batches, as
3730 : // PostFilterArrowArray() has potentially modified array contents
3731 755 : if (m_poAttrQuery)
3732 111 : InvalidateCachedBatches();
3733 :
3734 755 : OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter);
3735 755 : if (eErr != OGRERR_NONE)
3736 0 : return eErr;
3737 :
3738 755 : if (m_poAttrQuery != nullptr)
3739 : {
3740 466 : if (m_nUseOptimizedAttributeFilter < 0)
3741 : {
3742 368 : m_nUseOptimizedAttributeFilter = CPLTestBool(CPLGetConfigOption(
3743 736 : ("OGR_" + GetDriverUCName() + "_OPTIMIZED_ATTRIBUTE_FILTER")
3744 : .c_str(),
3745 : "YES"));
3746 : }
3747 466 : if (m_nUseOptimizedAttributeFilter)
3748 : {
3749 : swq_expr_node *poNode =
3750 316 : static_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
3751 316 : poNode->ReplaceBetweenByGEAndLERecurse();
3752 316 : ExploreExprNode(poNode);
3753 316 : ComputeConstraintsArrayIdx();
3754 : }
3755 : }
3756 :
3757 755 : return OGRERR_NONE;
3758 : }
3759 :
3760 : /************************************************************************/
3761 : /* ConstraintEvaluator() */
3762 : /************************************************************************/
3763 :
3764 : namespace
3765 : {
3766 : template <class T, class U> struct CompareGeneric
3767 : {
3768 289 : static inline bool get(int op, const T &val1, const U &val2)
3769 : {
3770 289 : switch (op)
3771 : {
3772 8 : case SWQ_LE:
3773 8 : return val1 <= val2;
3774 8 : case SWQ_LT:
3775 8 : return val1 < val2;
3776 90 : case SWQ_NE:
3777 90 : return val1 != val2;
3778 163 : case SWQ_EQ:
3779 163 : return val1 == val2;
3780 12 : case SWQ_GE:
3781 12 : return val1 >= val2;
3782 8 : case SWQ_GT:
3783 8 : return val1 > val2;
3784 : break;
3785 0 : default:
3786 0 : CPLAssert(false);
3787 : }
3788 : return true;
3789 : }
3790 : };
3791 :
3792 : template <class T, class U> struct Compare
3793 : {
3794 : };
3795 :
3796 : template <class T> struct Compare<T, T> : public CompareGeneric<T, T>
3797 : {
3798 : };
3799 :
3800 : template <> struct Compare<int, GIntBig> : public CompareGeneric<int, GIntBig>
3801 : {
3802 : };
3803 :
3804 : template <> struct Compare<double, GIntBig>
3805 : {
3806 0 : static inline bool get(int op, double val1, GIntBig val2)
3807 : {
3808 0 : return CompareGeneric<double, double>::get(op, val1,
3809 0 : static_cast<double>(val2));
3810 : }
3811 : };
3812 :
3813 : template <> struct Compare<GIntBig, int> : public CompareGeneric<GIntBig, int>
3814 : {
3815 : };
3816 :
3817 : template <> struct Compare<double, int> : public CompareGeneric<double, int>
3818 : {
3819 : };
3820 :
3821 : template <class T>
3822 289 : static bool ConstraintEvaluator(const OGRArrowLayer::Constraint &constraint,
3823 : const T value)
3824 : {
3825 289 : bool b = false;
3826 289 : switch (constraint.eType)
3827 : {
3828 204 : case OGRArrowLayer::Constraint::Type::Integer:
3829 408 : b = Compare<T, int>::get(constraint.nOperation, value,
3830 204 : constraint.sValue.Integer);
3831 204 : break;
3832 43 : case OGRArrowLayer::Constraint::Type::Integer64:
3833 86 : b = Compare<T, GIntBig>::get(constraint.nOperation, value,
3834 43 : constraint.sValue.Integer64);
3835 43 : break;
3836 42 : case OGRArrowLayer::Constraint::Type::Real:
3837 84 : b = Compare<double, double>::get(constraint.nOperation,
3838 0 : static_cast<double>(value),
3839 42 : constraint.sValue.Real);
3840 42 : break;
3841 0 : case OGRArrowLayer::Constraint::Type::String:
3842 0 : b = Compare<std::string, std::string>::get(constraint.nOperation,
3843 : std::to_string(value),
3844 0 : constraint.osValue);
3845 0 : break;
3846 : }
3847 289 : return b;
3848 : }
3849 :
3850 45 : inline bool CompareStr(int op, const std::string_view &val1,
3851 : const std::string &val2)
3852 : {
3853 45 : if (op == SWQ_EQ)
3854 : {
3855 14 : return val1 == val2;
3856 : }
3857 31 : const int cmpRes = val2.compare(val1);
3858 31 : switch (op)
3859 : {
3860 4 : case SWQ_LE:
3861 4 : return cmpRes >= 0;
3862 4 : case SWQ_LT:
3863 4 : return cmpRes > 0;
3864 23 : case SWQ_NE:
3865 23 : return cmpRes != 0;
3866 : // case SWQ_EQ: return cmpRes == 0;
3867 0 : case SWQ_GE:
3868 0 : return cmpRes <= 0;
3869 0 : case SWQ_GT:
3870 0 : return cmpRes < 0;
3871 : break;
3872 0 : default:
3873 0 : CPLAssert(false);
3874 : }
3875 : return true;
3876 : }
3877 :
3878 45 : inline bool ConstraintEvaluator(const OGRArrowLayer::Constraint &constraint,
3879 : const std::string_view &value)
3880 : {
3881 45 : return CompareStr(constraint.nOperation, value, constraint.osValue);
3882 : }
3883 :
3884 : } // namespace
3885 :
3886 : /************************************************************************/
3887 : /* SkipToNextFeatureDueToAttributeFilter() */
3888 : /************************************************************************/
3889 :
3890 448 : inline bool OGRArrowLayer::SkipToNextFeatureDueToAttributeFilter() const
3891 : {
3892 693 : for (const auto &constraint : m_asAttributeFilterConstraints)
3893 : {
3894 448 : if (constraint.iArrayIdx < 0)
3895 : {
3896 42 : if (constraint.iField ==
3897 34 : m_poFeatureDefn->GetFieldCount() + SPF_FID &&
3898 16 : m_osFIDColumn.empty())
3899 : {
3900 16 : if (!ConstraintEvaluator(constraint,
3901 16 : static_cast<GIntBig>(m_nFeatureIdx)))
3902 : {
3903 203 : return true;
3904 : }
3905 4 : continue;
3906 : }
3907 : else
3908 : {
3909 : // can happen if ignoring a field that is needed by the
3910 : // attribute filter. ComputeConstraintsArrayIdx() will have
3911 : // warned about that
3912 2 : continue;
3913 : }
3914 : }
3915 :
3916 : const arrow::Array *array =
3917 430 : m_poBatchColumns[constraint.iArrayIdx].get();
3918 :
3919 430 : const bool bIsNull = array->IsNull(m_nIdxInBatch);
3920 430 : if (constraint.nOperation == SWQ_ISNULL)
3921 : {
3922 7 : if (bIsNull)
3923 : {
3924 3 : continue;
3925 : }
3926 4 : return true;
3927 : }
3928 423 : else if (constraint.nOperation == SWQ_ISNOTNULL)
3929 : {
3930 22 : if (!bIsNull)
3931 : {
3932 18 : continue;
3933 : }
3934 4 : return true;
3935 : }
3936 401 : else if (bIsNull)
3937 : {
3938 65 : return true;
3939 : }
3940 :
3941 336 : switch (array->type_id())
3942 : {
3943 0 : case arrow::Type::NA:
3944 0 : break;
3945 :
3946 92 : case arrow::Type::BOOL:
3947 : {
3948 92 : const auto castArray =
3949 : static_cast<const arrow::BooleanArray *>(array);
3950 92 : if (!ConstraintEvaluator(
3951 : constraint,
3952 92 : static_cast<int>(castArray->Value(m_nIdxInBatch))))
3953 : {
3954 42 : return true;
3955 : }
3956 50 : break;
3957 : }
3958 7 : case arrow::Type::UINT8:
3959 : {
3960 7 : const auto castArray =
3961 : static_cast<const arrow::UInt8Array *>(array);
3962 7 : if (!ConstraintEvaluator(
3963 : constraint,
3964 7 : static_cast<int>(castArray->Value(m_nIdxInBatch))))
3965 : {
3966 3 : return true;
3967 : }
3968 4 : break;
3969 : }
3970 30 : case arrow::Type::INT8:
3971 : {
3972 30 : const auto castArray =
3973 : static_cast<const arrow::Int8Array *>(array);
3974 30 : if (!ConstraintEvaluator(
3975 : constraint,
3976 30 : static_cast<int>(castArray->Value(m_nIdxInBatch))))
3977 : {
3978 2 : return true;
3979 : }
3980 28 : break;
3981 : }
3982 4 : case arrow::Type::UINT16:
3983 : {
3984 4 : const auto castArray =
3985 : static_cast<const arrow::UInt16Array *>(array);
3986 4 : if (!ConstraintEvaluator(
3987 : constraint,
3988 4 : static_cast<int>(castArray->Value(m_nIdxInBatch))))
3989 : {
3990 2 : return true;
3991 : }
3992 2 : break;
3993 : }
3994 0 : case arrow::Type::INT16:
3995 : {
3996 0 : const auto castArray =
3997 : static_cast<const arrow::Int16Array *>(array);
3998 0 : if (!ConstraintEvaluator(
3999 : constraint,
4000 0 : static_cast<int>(castArray->Value(m_nIdxInBatch))))
4001 : {
4002 0 : return true;
4003 : }
4004 0 : break;
4005 : }
4006 0 : case arrow::Type::UINT32:
4007 : {
4008 0 : const auto castArray =
4009 : static_cast<const arrow::UInt32Array *>(array);
4010 0 : if (!ConstraintEvaluator(
4011 : constraint,
4012 0 : static_cast<GIntBig>(castArray->Value(m_nIdxInBatch))))
4013 : {
4014 0 : return true;
4015 : }
4016 0 : break;
4017 : }
4018 71 : case arrow::Type::INT32:
4019 : {
4020 71 : const auto castArray =
4021 : static_cast<const arrow::Int32Array *>(array);
4022 71 : if (!ConstraintEvaluator(constraint,
4023 71 : castArray->Value(m_nIdxInBatch)))
4024 : {
4025 28 : return true;
4026 : }
4027 43 : break;
4028 : }
4029 8 : case arrow::Type::UINT64:
4030 : {
4031 8 : const auto castArray =
4032 : static_cast<const arrow::UInt64Array *>(array);
4033 8 : if (!ConstraintEvaluator(
4034 : constraint,
4035 8 : static_cast<double>(castArray->Value(m_nIdxInBatch))))
4036 : {
4037 6 : return true;
4038 : }
4039 2 : break;
4040 : }
4041 27 : case arrow::Type::INT64:
4042 : {
4043 27 : const auto castArray =
4044 : static_cast<const arrow::Int64Array *>(array);
4045 27 : if (!ConstraintEvaluator(
4046 : constraint,
4047 27 : static_cast<GIntBig>(castArray->Value(m_nIdxInBatch))))
4048 : {
4049 10 : return true;
4050 : }
4051 17 : break;
4052 : }
4053 0 : case arrow::Type::HALF_FLOAT:
4054 : {
4055 0 : const auto castArray =
4056 : static_cast<const arrow::HalfFloatArray *>(array);
4057 0 : const uint16_t nFloat16 = castArray->Value(m_nIdxInBatch);
4058 0 : uint32_t nFloat32 = CPLHalfToFloat(nFloat16);
4059 : float f;
4060 0 : memcpy(&f, &nFloat32, sizeof(nFloat32));
4061 0 : if (!ConstraintEvaluator(constraint, static_cast<double>(f)))
4062 : {
4063 0 : return true;
4064 : }
4065 0 : break;
4066 : }
4067 4 : case arrow::Type::FLOAT:
4068 : {
4069 4 : const auto castArray =
4070 : static_cast<const arrow::FloatArray *>(array);
4071 4 : if (!ConstraintEvaluator(
4072 : constraint,
4073 4 : static_cast<double>(castArray->Value(m_nIdxInBatch))))
4074 : {
4075 2 : return true;
4076 : }
4077 2 : break;
4078 : }
4079 22 : case arrow::Type::DOUBLE:
4080 : {
4081 22 : const auto castArray =
4082 : static_cast<const arrow::DoubleArray *>(array);
4083 22 : if (!ConstraintEvaluator(constraint,
4084 22 : castArray->Value(m_nIdxInBatch)))
4085 : {
4086 6 : return true;
4087 : }
4088 16 : break;
4089 : }
4090 45 : case arrow::Type::STRING:
4091 : {
4092 45 : const auto castArray =
4093 : static_cast<const arrow::StringArray *>(array);
4094 45 : int out_length = 0;
4095 : const uint8_t *data =
4096 45 : castArray->GetValue(m_nIdxInBatch, &out_length);
4097 45 : if (!ConstraintEvaluator(
4098 : constraint,
4099 90 : std::string_view(reinterpret_cast<const char *>(data),
4100 : out_length)))
4101 : {
4102 13 : return true;
4103 : }
4104 32 : break;
4105 : }
4106 :
4107 : #if ARROW_VERSION_MAJOR >= 18
4108 0 : case arrow::Type::DECIMAL32:
4109 : {
4110 0 : const auto castArray =
4111 : static_cast<const arrow::Decimal32Array *>(array);
4112 0 : if (!ConstraintEvaluator(
4113 : constraint,
4114 0 : CPLAtof(castArray->FormatValue(m_nIdxInBatch).c_str())))
4115 : {
4116 0 : return true;
4117 : }
4118 0 : break;
4119 : }
4120 :
4121 0 : case arrow::Type::DECIMAL64:
4122 : {
4123 0 : const auto castArray =
4124 : static_cast<const arrow::Decimal64Array *>(array);
4125 0 : if (!ConstraintEvaluator(
4126 : constraint,
4127 0 : CPLAtof(castArray->FormatValue(m_nIdxInBatch).c_str())))
4128 : {
4129 0 : return true;
4130 : }
4131 0 : break;
4132 : }
4133 : #endif
4134 :
4135 4 : case arrow::Type::DECIMAL128:
4136 : {
4137 4 : const auto castArray =
4138 : static_cast<const arrow::Decimal128Array *>(array);
4139 4 : if (!ConstraintEvaluator(
4140 : constraint,
4141 8 : CPLAtof(castArray->FormatValue(m_nIdxInBatch).c_str())))
4142 : {
4143 2 : return true;
4144 : }
4145 2 : break;
4146 : }
4147 :
4148 4 : case arrow::Type::DECIMAL256:
4149 : {
4150 4 : const auto castArray =
4151 : static_cast<const arrow::Decimal256Array *>(array);
4152 4 : if (!ConstraintEvaluator(
4153 : constraint,
4154 8 : CPLAtof(castArray->FormatValue(m_nIdxInBatch).c_str())))
4155 : {
4156 2 : return true;
4157 : }
4158 2 : break;
4159 : }
4160 :
4161 18 : default:
4162 18 : break;
4163 : }
4164 : }
4165 245 : return false;
4166 : }
4167 :
4168 : /************************************************************************/
4169 : /* SetBatch() */
4170 : /************************************************************************/
4171 :
4172 : inline void
4173 4344 : OGRArrowLayer::SetBatch(const std::shared_ptr<arrow::RecordBatch> &poBatch)
4174 : {
4175 4344 : m_poBatch = poBatch;
4176 4344 : m_poBatchColumns.clear();
4177 4344 : m_poArrayWKB = nullptr;
4178 4344 : m_poArrayWKBLarge = nullptr;
4179 4344 : m_poArrayBBOX = nullptr;
4180 4344 : m_poArrayXMinDouble = nullptr;
4181 4344 : m_poArrayYMinDouble = nullptr;
4182 4344 : m_poArrayXMaxDouble = nullptr;
4183 4344 : m_poArrayYMaxDouble = nullptr;
4184 4344 : m_poArrayXMinFloat = nullptr;
4185 4344 : m_poArrayYMinFloat = nullptr;
4186 4344 : m_poArrayXMaxFloat = nullptr;
4187 4344 : m_poArrayYMaxFloat = nullptr;
4188 :
4189 4344 : if (m_poBatch)
4190 : {
4191 2996 : m_poBatchColumns = m_poBatch->columns();
4192 2996 : SanityCheckOfSetBatch();
4193 : }
4194 :
4195 4344 : if (m_poBatch && m_poFilterGeom && !m_bBaseArrowIgnoreSpatialFilterRect)
4196 : {
4197 : int iCol;
4198 672 : if (m_bIgnoredFields)
4199 : {
4200 204 : iCol = m_anMapGeomFieldIndexToArrayIndex[m_iGeomFieldFilter];
4201 : }
4202 : else
4203 : {
4204 468 : iCol = m_anMapGeomFieldIndexToArrowColumn[m_iGeomFieldFilter];
4205 : }
4206 1344 : if (iCol >= 0 &&
4207 672 : m_aeGeomEncoding[m_iGeomFieldFilter] == OGRArrowGeomEncoding::WKB)
4208 : {
4209 : const arrow::Array *poArrayWKB =
4210 117 : GetStorageArray(m_poBatchColumns[iCol].get());
4211 117 : if (poArrayWKB->type_id() == arrow::Type::BINARY)
4212 117 : m_poArrayWKB =
4213 : static_cast<const arrow::BinaryArray *>(poArrayWKB);
4214 : else
4215 : {
4216 0 : CPLAssert(poArrayWKB->type_id() == arrow::Type::LARGE_BINARY);
4217 0 : m_poArrayWKBLarge =
4218 : static_cast<const arrow::LargeBinaryArray *>(poArrayWKB);
4219 : }
4220 : }
4221 :
4222 1344 : if (iCol >= 0 &&
4223 672 : CPLTestBool(CPLGetConfigOption(
4224 1344 : ("OGR_" + GetDriverUCName() + "_USE_BBOX").c_str(), "YES")))
4225 : {
4226 : const auto oIter =
4227 672 : m_oMapGeomFieldIndexToGeomColBBOX.find(m_iGeomFieldFilter);
4228 672 : if (oIter != m_oMapGeomFieldIndexToGeomColBBOX.end())
4229 : {
4230 422 : const int idx = m_bIgnoredFields ? oIter->second.iArrayIdx
4231 155 : : oIter->second.iArrowCol;
4232 267 : if (idx >= 0)
4233 : {
4234 211 : CPLAssert(static_cast<size_t>(idx) <
4235 : m_poBatchColumns.size());
4236 211 : m_poArrayBBOX = m_poBatchColumns[idx].get();
4237 211 : CPLAssert(m_poArrayBBOX->type_id() == arrow::Type::STRUCT);
4238 211 : const auto castArray =
4239 : static_cast<const arrow::StructArray *>(m_poArrayBBOX);
4240 211 : const auto &subArrays = castArray->fields();
4241 211 : CPLAssert(
4242 : static_cast<size_t>(oIter->second.iArrowSubfieldXMin) <
4243 : subArrays.size());
4244 : const auto xminArray =
4245 211 : subArrays[oIter->second.iArrowSubfieldXMin].get();
4246 211 : CPLAssert(
4247 : static_cast<size_t>(oIter->second.iArrowSubfieldYMin) <
4248 : subArrays.size());
4249 : const auto yminArray =
4250 211 : subArrays[oIter->second.iArrowSubfieldYMin].get();
4251 211 : CPLAssert(
4252 : static_cast<size_t>(oIter->second.iArrowSubfieldXMax) <
4253 : subArrays.size());
4254 : const auto xmaxArray =
4255 211 : subArrays[oIter->second.iArrowSubfieldXMax].get();
4256 211 : CPLAssert(
4257 : static_cast<size_t>(oIter->second.iArrowSubfieldYMax) <
4258 : subArrays.size());
4259 : const auto ymaxArray =
4260 211 : subArrays[oIter->second.iArrowSubfieldYMax].get();
4261 211 : if (oIter->second.bIsFloat)
4262 : {
4263 210 : CPLAssert(xminArray->type_id() == arrow::Type::FLOAT);
4264 210 : m_poArrayXMinFloat =
4265 : static_cast<const arrow::FloatArray *>(xminArray);
4266 210 : CPLAssert(yminArray->type_id() == arrow::Type::FLOAT);
4267 210 : m_poArrayYMinFloat =
4268 : static_cast<const arrow::FloatArray *>(yminArray);
4269 210 : CPLAssert(xmaxArray->type_id() == arrow::Type::FLOAT);
4270 210 : m_poArrayXMaxFloat =
4271 : static_cast<const arrow::FloatArray *>(xmaxArray);
4272 210 : CPLAssert(ymaxArray->type_id() == arrow::Type::FLOAT);
4273 210 : m_poArrayYMaxFloat =
4274 : static_cast<const arrow::FloatArray *>(ymaxArray);
4275 : }
4276 : else
4277 : {
4278 1 : CPLAssert(xminArray->type_id() == arrow::Type::DOUBLE);
4279 1 : m_poArrayXMinDouble =
4280 : static_cast<const arrow::DoubleArray *>(xminArray);
4281 1 : CPLAssert(yminArray->type_id() == arrow::Type::DOUBLE);
4282 1 : m_poArrayYMinDouble =
4283 : static_cast<const arrow::DoubleArray *>(yminArray);
4284 1 : CPLAssert(xmaxArray->type_id() == arrow::Type::DOUBLE);
4285 1 : m_poArrayXMaxDouble =
4286 : static_cast<const arrow::DoubleArray *>(xmaxArray);
4287 1 : CPLAssert(ymaxArray->type_id() == arrow::Type::DOUBLE);
4288 1 : m_poArrayYMaxDouble =
4289 : static_cast<const arrow::DoubleArray *>(ymaxArray);
4290 : }
4291 : }
4292 : }
4293 : }
4294 : }
4295 4344 : }
4296 :
4297 : /************************************************************************/
4298 : /* SanityCheckOfSetBatch() */
4299 : /************************************************************************/
4300 :
4301 2996 : inline void OGRArrowLayer::SanityCheckOfSetBatch() const
4302 : {
4303 : #ifdef DEBUG
4304 2996 : CPLAssert(m_poBatch);
4305 :
4306 2996 : const auto &poColumns = m_poBatch->columns();
4307 :
4308 : // Sanity checks
4309 2996 : CPLAssert(m_poBatch->num_columns() == (m_bIgnoredFields
4310 : ? m_nExpectedBatchColumns
4311 : : m_poSchema->num_fields()));
4312 2996 : const auto &fields = m_poSchema->fields();
4313 :
4314 96083 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i)
4315 : {
4316 : int iCol;
4317 93087 : if (m_bIgnoredFields)
4318 : {
4319 15253 : iCol = m_anMapFieldIndexToArrayIndex[i];
4320 15253 : if (iCol < 0)
4321 3894 : continue;
4322 : }
4323 : else
4324 : {
4325 77834 : iCol = m_anMapFieldIndexToArrowColumn[i][0];
4326 : }
4327 89193 : CPL_IGNORE_RET_VAL(iCol); // to make cppcheck happy
4328 :
4329 89193 : CPLAssert(iCol < static_cast<int>(poColumns.size()));
4330 89193 : CPLAssert(fields[m_anMapFieldIndexToArrowColumn[i][0]]->type()->id() ==
4331 : poColumns[iCol]->type_id());
4332 : }
4333 :
4334 5880 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
4335 : {
4336 : int iCol;
4337 2884 : if (m_bIgnoredFields)
4338 : {
4339 653 : iCol = m_anMapGeomFieldIndexToArrayIndex[i];
4340 653 : if (iCol < 0)
4341 30 : continue;
4342 : }
4343 : else
4344 : {
4345 2231 : iCol = m_anMapGeomFieldIndexToArrowColumn[i];
4346 : }
4347 2854 : CPL_IGNORE_RET_VAL(iCol); // to make cppcheck happy
4348 :
4349 2854 : CPLAssert(iCol < static_cast<int>(poColumns.size()));
4350 2854 : CPLAssert(fields[m_anMapGeomFieldIndexToArrowColumn[i]]->type()->id() ==
4351 : poColumns[iCol]->type_id());
4352 : }
4353 : #else
4354 : CPL_IGNORE_RET_VAL(m_nExpectedBatchColumns);
4355 : #endif
4356 2996 : }
4357 :
4358 : /************************************************************************/
4359 : /* GetNextRawFeature() */
4360 : /************************************************************************/
4361 :
4362 13280 : inline OGRFeature *OGRArrowLayer::GetNextRawFeature()
4363 : {
4364 13280 : if (m_bEOF || !m_bSpatialFilterIntersectsLayerExtent)
4365 533 : return nullptr;
4366 :
4367 12747 : if (m_poBatch == nullptr || m_nIdxInBatch == m_poBatch->num_rows())
4368 : {
4369 4028 : m_bEOF = !ReadNextBatch();
4370 4028 : if (m_bEOF)
4371 1686 : return nullptr;
4372 : }
4373 :
4374 : // Evaluate spatial filter by computing the bounding box of each geometry
4375 : // but without creating a OGRGeometry
4376 11061 : if (m_poFilterGeom && !m_bBaseArrowIgnoreSpatialFilterRect)
4377 : {
4378 : int iCol;
4379 1167 : if (m_bIgnoredFields)
4380 : {
4381 276 : iCol = m_anMapGeomFieldIndexToArrayIndex[m_iGeomFieldFilter];
4382 : }
4383 : else
4384 : {
4385 891 : iCol = m_anMapGeomFieldIndexToArrowColumn[m_iGeomFieldFilter];
4386 : }
4387 :
4388 1167 : if (iCol >= 0 && (m_poArrayXMinFloat || m_poArrayXMinDouble))
4389 : {
4390 463 : OGREnvelope sEnvelopeSkipToNextFeatureDueToBBOX;
4391 : const auto IntersectsBBOX =
4392 3459 : [this, &sEnvelopeSkipToNextFeatureDueToBBOX]()
4393 : {
4394 987 : if (m_poArrayXMinFloat &&
4395 493 : !m_poArrayXMinFloat->IsNull(m_nIdxInBatch))
4396 : {
4397 493 : sEnvelopeSkipToNextFeatureDueToBBOX.MinX =
4398 493 : m_poArrayXMinFloat->Value(m_nIdxInBatch);
4399 493 : sEnvelopeSkipToNextFeatureDueToBBOX.MinY =
4400 493 : m_poArrayYMinFloat->Value(m_nIdxInBatch);
4401 493 : sEnvelopeSkipToNextFeatureDueToBBOX.MaxX =
4402 493 : m_poArrayXMaxFloat->Value(m_nIdxInBatch);
4403 493 : sEnvelopeSkipToNextFeatureDueToBBOX.MaxY =
4404 493 : m_poArrayYMaxFloat->Value(m_nIdxInBatch);
4405 493 : if (m_sFilterEnvelope.Intersects(
4406 493 : sEnvelopeSkipToNextFeatureDueToBBOX))
4407 : {
4408 450 : return true;
4409 : }
4410 : }
4411 2 : else if (m_poArrayXMinDouble &&
4412 1 : !m_poArrayXMinDouble->IsNull(m_nIdxInBatch))
4413 : {
4414 1 : sEnvelopeSkipToNextFeatureDueToBBOX.MinX =
4415 1 : m_poArrayXMinDouble->Value(m_nIdxInBatch);
4416 1 : sEnvelopeSkipToNextFeatureDueToBBOX.MinY =
4417 1 : m_poArrayYMinDouble->Value(m_nIdxInBatch);
4418 1 : sEnvelopeSkipToNextFeatureDueToBBOX.MaxX =
4419 1 : m_poArrayXMaxDouble->Value(m_nIdxInBatch);
4420 1 : sEnvelopeSkipToNextFeatureDueToBBOX.MaxY =
4421 1 : m_poArrayYMaxDouble->Value(m_nIdxInBatch);
4422 1 : if (m_sFilterEnvelope.Intersects(
4423 1 : sEnvelopeSkipToNextFeatureDueToBBOX))
4424 : {
4425 1 : return true;
4426 : }
4427 : }
4428 43 : return false;
4429 463 : };
4430 :
4431 : while (true)
4432 : {
4433 1321 : if (!m_poArrayBBOX->IsNull(m_nIdxInBatch) && IntersectsBBOX() &&
4434 451 : (m_asAttributeFilterConstraints.empty() ||
4435 2 : !SkipToNextFeatureDueToAttributeFilter()))
4436 : {
4437 450 : break;
4438 : }
4439 :
4440 418 : IncrFeatureIdx();
4441 418 : m_nIdxInBatch++;
4442 418 : if (m_nIdxInBatch == m_poBatch->num_rows())
4443 : {
4444 22 : m_bEOF = !ReadNextBatch();
4445 22 : if (m_bEOF)
4446 13 : return nullptr;
4447 : }
4448 450 : }
4449 : }
4450 704 : else if (iCol >= 0 && m_aeGeomEncoding[m_iGeomFieldFilter] ==
4451 : OGRArrowGeomEncoding::WKB)
4452 : {
4453 131 : CPLAssert(m_poArrayWKB || m_poArrayWKBLarge);
4454 131 : OGREnvelope sEnvelope;
4455 :
4456 : while (true)
4457 : {
4458 220 : bool bMatchBBOX = false;
4459 417 : if ((m_poArrayWKB && m_poArrayWKB->IsNull(m_nIdxInBatch)) ||
4460 197 : (m_poArrayWKBLarge &&
4461 0 : m_poArrayWKBLarge->IsNull(m_nIdxInBatch)))
4462 : {
4463 : // nothing to do
4464 : }
4465 : else
4466 : {
4467 197 : if (m_poArrayWKB)
4468 : {
4469 197 : int out_length = 0;
4470 : const uint8_t *data =
4471 197 : m_poArrayWKB->GetValue(m_nIdxInBatch, &out_length);
4472 394 : if (OGRWKBGetBoundingBox(data, out_length, sEnvelope) &&
4473 197 : m_sFilterEnvelope.Intersects(sEnvelope))
4474 : {
4475 115 : bMatchBBOX = true;
4476 : }
4477 : }
4478 : else
4479 : {
4480 0 : CPLAssert(m_poArrayWKBLarge);
4481 0 : int64_t out_length64 = 0;
4482 0 : const uint8_t *data = m_poArrayWKBLarge->GetValue(
4483 : m_nIdxInBatch, &out_length64);
4484 0 : if (out_length64 < INT_MAX &&
4485 0 : OGRWKBGetBoundingBox(data,
4486 : static_cast<int>(out_length64),
4487 0 : sEnvelope) &&
4488 0 : m_sFilterEnvelope.Intersects(sEnvelope))
4489 : {
4490 0 : bMatchBBOX = true;
4491 : }
4492 : }
4493 : }
4494 221 : if (bMatchBBOX && (m_asAttributeFilterConstraints.empty() ||
4495 1 : !SkipToNextFeatureDueToAttributeFilter()))
4496 : {
4497 115 : break;
4498 : }
4499 :
4500 105 : IncrFeatureIdx();
4501 105 : m_nIdxInBatch++;
4502 105 : if (m_nIdxInBatch == m_poBatch->num_rows())
4503 : {
4504 25 : m_bEOF = !ReadNextBatch();
4505 25 : if (m_bEOF)
4506 16 : return nullptr;
4507 : }
4508 89 : }
4509 : }
4510 1146 : else if (iCol >= 0 &&
4511 573 : m_aeGeomEncoding[m_iGeomFieldFilter] ==
4512 : OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOLYGON)
4513 : {
4514 : const auto poGeomFieldDefn =
4515 10 : m_poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
4516 10 : const auto eGeomType = poGeomFieldDefn->GetType();
4517 10 : const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
4518 10 : const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
4519 10 : const int nDim = 2 + (bHasZ ? 1 : 0) + (bHasM ? 1 : 0);
4520 :
4521 : bool bReturnFeature;
4522 0 : do
4523 : {
4524 10 : bReturnFeature = false;
4525 10 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
4526 10 : CPLAssert(array->type_id() == arrow::Type::LIST);
4527 10 : auto listOfPartsArray =
4528 : static_cast<const arrow::ListArray *>(array);
4529 10 : CPLAssert(listOfPartsArray->values()->type_id() ==
4530 : arrow::Type::LIST);
4531 : auto listOfPartsValues =
4532 : std::static_pointer_cast<arrow::ListArray>(
4533 10 : listOfPartsArray->values());
4534 10 : CPLAssert(listOfPartsValues->values()->type_id() ==
4535 : arrow::Type::LIST);
4536 : auto listOfRingsValues =
4537 : std::static_pointer_cast<arrow::ListArray>(
4538 10 : listOfPartsValues->values());
4539 10 : CPLAssert(listOfRingsValues->values()->type_id() ==
4540 : arrow::Type::FIXED_SIZE_LIST);
4541 : auto listOfPointsValues =
4542 : std::static_pointer_cast<arrow::FixedSizeListArray>(
4543 10 : listOfRingsValues->values());
4544 10 : CPLAssert(listOfPointsValues->values()->type_id() ==
4545 : arrow::Type::DOUBLE);
4546 : auto pointValues = std::static_pointer_cast<arrow::DoubleArray>(
4547 10 : listOfPointsValues->values());
4548 :
4549 : while (true)
4550 : {
4551 18 : bool bMatchBBOX = false;
4552 18 : if (!listOfPartsArray->IsNull(m_nIdxInBatch))
4553 : {
4554 14 : OGREnvelope sEnvelope;
4555 : const auto nParts =
4556 14 : listOfPartsArray->value_length(m_nIdxInBatch);
4557 : const auto nPartOffset =
4558 14 : listOfPartsArray->value_offset(m_nIdxInBatch);
4559 28 : for (auto j = decltype(nParts){0}; j < nParts; j++)
4560 : {
4561 14 : const auto nRings = listOfPartsValues->value_length(
4562 14 : nPartOffset + j);
4563 : const auto nRingOffset =
4564 14 : listOfPartsValues->value_offset(nPartOffset +
4565 : j);
4566 14 : if (nRings >= 1)
4567 : {
4568 : const auto nPoints =
4569 14 : listOfRingsValues->value_length(
4570 : nRingOffset);
4571 : const auto nPointOffset =
4572 14 : listOfRingsValues->value_offset(
4573 : nRingOffset) *
4574 14 : nDim;
4575 : const double *padfRawValue =
4576 14 : pointValues->raw_values() + nPointOffset;
4577 73 : for (auto l = decltype(nPoints){0}; l < nPoints;
4578 : ++l)
4579 : {
4580 59 : sEnvelope.Merge(padfRawValue[nDim * l],
4581 59 : padfRawValue[nDim * l + 1]);
4582 : }
4583 : // for bounding box, only the first ring matters
4584 : }
4585 : }
4586 :
4587 24 : if (nParts != 0 &&
4588 10 : m_sFilterEnvelope.Intersects(sEnvelope))
4589 : {
4590 10 : bMatchBBOX = true;
4591 : }
4592 : }
4593 28 : if (bMatchBBOX &&
4594 10 : (m_asAttributeFilterConstraints.empty() ||
4595 0 : !SkipToNextFeatureDueToAttributeFilter()))
4596 : {
4597 10 : bReturnFeature = true;
4598 10 : break;
4599 : }
4600 :
4601 8 : IncrFeatureIdx();
4602 8 : m_nIdxInBatch++;
4603 8 : if (m_nIdxInBatch == m_poBatch->num_rows())
4604 : {
4605 0 : m_bEOF = !ReadNextBatch();
4606 0 : if (m_bEOF)
4607 0 : return nullptr;
4608 0 : break;
4609 : }
4610 8 : }
4611 10 : } while (!bReturnFeature);
4612 : }
4613 563 : else if (iCol >= 0 && m_aeGeomEncoding[m_iGeomFieldFilter] ==
4614 : OGRArrowGeomEncoding::GEOARROW_STRUCT_POINT)
4615 : {
4616 : bool bReturnFeature;
4617 10 : do
4618 : {
4619 44 : bReturnFeature = false;
4620 44 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
4621 44 : CPLAssert(array->type_id() == arrow::Type::STRUCT);
4622 44 : auto pointValues =
4623 : static_cast<const arrow::StructArray *>(array);
4624 44 : const auto &fields = pointValues->fields();
4625 44 : const auto &fieldX = fields[0];
4626 44 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
4627 : const auto fieldXDouble =
4628 44 : static_cast<arrow::DoubleArray *>(fieldX.get());
4629 44 : const auto &fieldY = fields[1];
4630 44 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
4631 : const auto fieldYDouble =
4632 44 : static_cast<arrow::DoubleArray *>(fieldY.get());
4633 :
4634 : while (true)
4635 : {
4636 98 : bool bMatchBBOX = false;
4637 98 : if (!array->IsNull(m_nIdxInBatch))
4638 : {
4639 76 : const double dfX = fieldXDouble->Value(m_nIdxInBatch);
4640 76 : const double dfY = fieldYDouble->Value(m_nIdxInBatch);
4641 76 : if (dfX >= m_sFilterEnvelope.MinX &&
4642 46 : dfY >= m_sFilterEnvelope.MinY &&
4643 46 : dfX <= m_sFilterEnvelope.MaxX &&
4644 30 : dfY <= m_sFilterEnvelope.MaxY)
4645 : {
4646 30 : bMatchBBOX = true;
4647 : }
4648 : }
4649 128 : if (bMatchBBOX &&
4650 30 : (m_asAttributeFilterConstraints.empty() ||
4651 0 : !SkipToNextFeatureDueToAttributeFilter()))
4652 : {
4653 30 : bReturnFeature = true;
4654 30 : break;
4655 : }
4656 :
4657 68 : IncrFeatureIdx();
4658 68 : m_nIdxInBatch++;
4659 68 : if (m_nIdxInBatch == m_poBatch->num_rows())
4660 : {
4661 14 : m_bEOF = !ReadNextBatch();
4662 14 : if (m_bEOF)
4663 4 : return nullptr;
4664 10 : break;
4665 : }
4666 54 : }
4667 40 : } while (!bReturnFeature);
4668 : }
4669 1058 : else if (iCol >= 0 &&
4670 529 : m_aeGeomEncoding[m_iGeomFieldFilter] ==
4671 : OGRArrowGeomEncoding::GEOARROW_STRUCT_LINESTRING)
4672 : {
4673 : bool bReturnFeature;
4674 0 : do
4675 : {
4676 64 : bReturnFeature = false;
4677 64 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
4678 64 : CPLAssert(array->type_id() == arrow::Type::LIST);
4679 64 : const auto listArray =
4680 : static_cast<const arrow::ListArray *>(array);
4681 64 : CPLAssert(listArray->values()->type_id() ==
4682 : arrow::Type::STRUCT);
4683 : auto pointValues = std::static_pointer_cast<arrow::StructArray>(
4684 64 : listArray->values());
4685 64 : const auto &fields = pointValues->fields();
4686 64 : const auto &fieldX = fields[0];
4687 64 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
4688 : const auto fieldXDouble =
4689 64 : static_cast<arrow::DoubleArray *>(fieldX.get());
4690 64 : const auto &fieldY = fields[1];
4691 64 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
4692 : const auto fieldYDouble =
4693 64 : static_cast<arrow::DoubleArray *>(fieldY.get());
4694 :
4695 : while (true)
4696 : {
4697 160 : bool bMatchBBOX = false;
4698 160 : if (!listArray->IsNull(m_nIdxInBatch))
4699 : {
4700 120 : OGREnvelope sEnvelope;
4701 : const auto nPoints =
4702 120 : listArray->value_length(m_nIdxInBatch);
4703 : const auto nPointOffset =
4704 120 : listArray->value_offset(m_nIdxInBatch);
4705 120 : if (nPoints > 0)
4706 : {
4707 : const double *padfRawXValue =
4708 80 : fieldXDouble->raw_values() + nPointOffset;
4709 : const double *padfRawYValue =
4710 80 : fieldYDouble->raw_values() + nPointOffset;
4711 240 : for (auto l = decltype(nPoints){0}; l < nPoints;
4712 : ++l)
4713 : {
4714 160 : sEnvelope.Merge(padfRawXValue[l],
4715 160 : padfRawYValue[l]);
4716 : }
4717 80 : if (m_sFilterEnvelope.Intersects(sEnvelope))
4718 : {
4719 48 : bMatchBBOX = true;
4720 : }
4721 : }
4722 : }
4723 208 : if (bMatchBBOX &&
4724 48 : (m_asAttributeFilterConstraints.empty() ||
4725 0 : !SkipToNextFeatureDueToAttributeFilter()))
4726 : {
4727 48 : bReturnFeature = true;
4728 48 : break;
4729 : }
4730 :
4731 112 : IncrFeatureIdx();
4732 112 : m_nIdxInBatch++;
4733 112 : if (m_nIdxInBatch == m_poBatch->num_rows())
4734 : {
4735 16 : m_bEOF = !ReadNextBatch();
4736 16 : if (m_bEOF)
4737 16 : return nullptr;
4738 0 : break;
4739 : }
4740 96 : }
4741 48 : } while (!bReturnFeature);
4742 : }
4743 465 : else if (iCol >= 0 && m_aeGeomEncoding[m_iGeomFieldFilter] ==
4744 : OGRArrowGeomEncoding::GEOARROW_STRUCT_POLYGON)
4745 : {
4746 : bool bReturnFeature;
4747 0 : do
4748 : {
4749 96 : bReturnFeature = false;
4750 96 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
4751 96 : CPLAssert(array->type_id() == arrow::Type::LIST);
4752 96 : const auto listOfRingsArray =
4753 : static_cast<const arrow::ListArray *>(array);
4754 96 : CPLAssert(listOfRingsArray->values()->type_id() ==
4755 : arrow::Type::LIST);
4756 : const auto listOfRingsValues =
4757 : std::static_pointer_cast<arrow::ListArray>(
4758 96 : listOfRingsArray->values());
4759 96 : CPLAssert(listOfRingsValues->values()->type_id() ==
4760 : arrow::Type::STRUCT);
4761 : auto pointValues = std::static_pointer_cast<arrow::StructArray>(
4762 96 : listOfRingsValues->values());
4763 96 : const auto &fields = pointValues->fields();
4764 96 : const auto &fieldX = fields[0];
4765 96 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
4766 : const auto fieldXDouble =
4767 96 : static_cast<arrow::DoubleArray *>(fieldX.get());
4768 96 : const auto &fieldY = fields[1];
4769 96 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
4770 : const auto fieldYDouble =
4771 96 : static_cast<arrow::DoubleArray *>(fieldY.get());
4772 :
4773 : while (true)
4774 : {
4775 240 : bool bMatchBBOX = false;
4776 240 : if (!listOfRingsArray->IsNull(m_nIdxInBatch))
4777 : {
4778 184 : OGREnvelope sEnvelope;
4779 : const auto nRings =
4780 184 : listOfRingsArray->value_length(m_nIdxInBatch);
4781 : const auto nRingOffset =
4782 184 : listOfRingsArray->value_offset(m_nIdxInBatch);
4783 184 : if (nRings >= 1)
4784 : {
4785 : const auto nPoints =
4786 128 : listOfRingsValues->value_length(nRingOffset);
4787 : const auto nPointOffset =
4788 128 : listOfRingsValues->value_offset(nRingOffset);
4789 : const double *padfRawXValue =
4790 128 : fieldXDouble->raw_values() + nPointOffset;
4791 : const double *padfRawYValue =
4792 128 : fieldYDouble->raw_values() + nPointOffset;
4793 688 : for (auto l = decltype(nPoints){0}; l < nPoints;
4794 : ++l)
4795 : {
4796 560 : sEnvelope.Merge(padfRawXValue[l],
4797 560 : padfRawYValue[l]);
4798 : }
4799 : // for bounding box, only the first ring matters
4800 :
4801 128 : if (m_sFilterEnvelope.Intersects(sEnvelope))
4802 : {
4803 72 : bMatchBBOX = true;
4804 : }
4805 : }
4806 : }
4807 312 : if (bMatchBBOX &&
4808 72 : (m_asAttributeFilterConstraints.empty() ||
4809 0 : !SkipToNextFeatureDueToAttributeFilter()))
4810 : {
4811 72 : bReturnFeature = true;
4812 72 : break;
4813 : }
4814 :
4815 168 : IncrFeatureIdx();
4816 168 : m_nIdxInBatch++;
4817 168 : if (m_nIdxInBatch == m_poBatch->num_rows())
4818 : {
4819 24 : m_bEOF = !ReadNextBatch();
4820 24 : if (m_bEOF)
4821 24 : return nullptr;
4822 0 : break;
4823 : }
4824 144 : }
4825 72 : } while (!bReturnFeature);
4826 : }
4827 738 : else if (iCol >= 0 &&
4828 369 : m_aeGeomEncoding[m_iGeomFieldFilter] ==
4829 : OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOINT)
4830 : {
4831 : bool bReturnFeature;
4832 0 : do
4833 : {
4834 64 : bReturnFeature = false;
4835 64 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
4836 64 : CPLAssert(array->type_id() == arrow::Type::LIST);
4837 64 : const auto listArray =
4838 : static_cast<const arrow::ListArray *>(array);
4839 64 : CPLAssert(listArray->values()->type_id() ==
4840 : arrow::Type::STRUCT);
4841 : auto pointValues = std::static_pointer_cast<arrow::StructArray>(
4842 64 : listArray->values());
4843 64 : const auto &fields = pointValues->fields();
4844 64 : const auto &fieldX = fields[0];
4845 64 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
4846 : const auto fieldXDouble =
4847 64 : static_cast<arrow::DoubleArray *>(fieldX.get());
4848 64 : const auto &fieldY = fields[1];
4849 64 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
4850 : const auto fieldYDouble =
4851 64 : static_cast<arrow::DoubleArray *>(fieldY.get());
4852 :
4853 : while (true)
4854 : {
4855 160 : bool bMatchBBOX = false;
4856 160 : if (!listArray->IsNull(m_nIdxInBatch))
4857 : {
4858 : const auto nPoints =
4859 128 : listArray->value_length(m_nIdxInBatch);
4860 : const auto nPointOffset =
4861 128 : listArray->value_offset(m_nIdxInBatch);
4862 128 : if (nPoints > 0)
4863 : {
4864 : const double *padfRawXValue =
4865 96 : fieldXDouble->raw_values() + nPointOffset;
4866 : const double *padfRawYValue =
4867 96 : fieldYDouble->raw_values() + nPointOffset;
4868 176 : for (auto l = decltype(nPoints){0}; l < nPoints;
4869 : ++l)
4870 : {
4871 128 : if (padfRawXValue[l] >=
4872 128 : m_sFilterEnvelope.MinX &&
4873 108 : padfRawYValue[l] >=
4874 108 : m_sFilterEnvelope.MinY &&
4875 88 : padfRawXValue[l] <=
4876 88 : m_sFilterEnvelope.MaxX &&
4877 68 : padfRawYValue[l] <= m_sFilterEnvelope.MaxY)
4878 : {
4879 48 : bMatchBBOX = true;
4880 48 : break;
4881 : }
4882 : }
4883 : }
4884 : }
4885 208 : if (bMatchBBOX &&
4886 48 : (m_asAttributeFilterConstraints.empty() ||
4887 0 : !SkipToNextFeatureDueToAttributeFilter()))
4888 : {
4889 48 : bReturnFeature = true;
4890 48 : break;
4891 : }
4892 :
4893 112 : IncrFeatureIdx();
4894 112 : m_nIdxInBatch++;
4895 112 : if (m_nIdxInBatch == m_poBatch->num_rows())
4896 : {
4897 16 : m_bEOF = !ReadNextBatch();
4898 16 : if (m_bEOF)
4899 16 : return nullptr;
4900 0 : break;
4901 : }
4902 96 : }
4903 48 : } while (!bReturnFeature);
4904 : }
4905 610 : else if (iCol >= 0 &&
4906 305 : m_aeGeomEncoding[m_iGeomFieldFilter] ==
4907 : OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTILINESTRING)
4908 : {
4909 : bool bReturnFeature;
4910 0 : do
4911 : {
4912 88 : bReturnFeature = false;
4913 88 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
4914 88 : CPLAssert(array->type_id() == arrow::Type::LIST);
4915 88 : auto listOfPartsArray =
4916 : static_cast<const arrow::ListArray *>(array);
4917 88 : CPLAssert(listOfPartsArray->values()->type_id() ==
4918 : arrow::Type::LIST);
4919 : auto listOfPartsValues =
4920 : std::static_pointer_cast<arrow::ListArray>(
4921 88 : listOfPartsArray->values());
4922 88 : CPLAssert(listOfPartsValues->values()->type_id() ==
4923 : arrow::Type::STRUCT);
4924 : auto pointValues = std::static_pointer_cast<arrow::StructArray>(
4925 88 : listOfPartsValues->values());
4926 88 : const auto &fields = pointValues->fields();
4927 88 : const auto &fieldX = fields[0];
4928 88 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
4929 : const auto fieldXDouble =
4930 88 : static_cast<arrow::DoubleArray *>(fieldX.get());
4931 88 : const auto &fieldY = fields[1];
4932 88 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
4933 : const auto fieldYDouble =
4934 88 : static_cast<arrow::DoubleArray *>(fieldY.get());
4935 :
4936 : while (true)
4937 : {
4938 200 : bool bMatchBBOX = false;
4939 200 : if (!listOfPartsArray->IsNull(m_nIdxInBatch))
4940 : {
4941 : const auto nParts =
4942 160 : listOfPartsArray->value_length(m_nIdxInBatch);
4943 : const auto nPartOffset =
4944 160 : listOfPartsArray->value_offset(m_nIdxInBatch);
4945 312 : for (auto j = decltype(nParts){0};
4946 312 : j < nParts && !bMatchBBOX; j++)
4947 : {
4948 152 : OGREnvelope sEnvelope;
4949 : const auto nPoints =
4950 152 : listOfPartsValues->value_length(nPartOffset +
4951 : j);
4952 : const auto nPointOffset =
4953 152 : listOfPartsValues->value_offset(nPartOffset +
4954 : j);
4955 : const double *padfRawXValue =
4956 152 : fieldXDouble->raw_values() + nPointOffset;
4957 : const double *padfRawYValue =
4958 152 : fieldYDouble->raw_values() + nPointOffset;
4959 488 : for (auto l = decltype(nPoints){0}; l < nPoints;
4960 : ++l)
4961 : {
4962 336 : sEnvelope.Merge(padfRawXValue[l],
4963 336 : padfRawYValue[l]);
4964 : }
4965 :
4966 152 : if (m_sFilterEnvelope.Intersects(sEnvelope))
4967 : {
4968 72 : bMatchBBOX = true;
4969 : }
4970 : }
4971 : }
4972 272 : if (bMatchBBOX &&
4973 72 : (m_asAttributeFilterConstraints.empty() ||
4974 0 : !SkipToNextFeatureDueToAttributeFilter()))
4975 : {
4976 72 : bReturnFeature = true;
4977 72 : break;
4978 : }
4979 :
4980 128 : IncrFeatureIdx();
4981 128 : m_nIdxInBatch++;
4982 128 : if (m_nIdxInBatch == m_poBatch->num_rows())
4983 : {
4984 16 : m_bEOF = !ReadNextBatch();
4985 16 : if (m_bEOF)
4986 16 : return nullptr;
4987 0 : break;
4988 : }
4989 112 : }
4990 72 : } while (!bReturnFeature);
4991 : }
4992 434 : else if (iCol >= 0 &&
4993 217 : m_aeGeomEncoding[m_iGeomFieldFilter] ==
4994 : OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOLYGON)
4995 : {
4996 : bool bReturnFeature;
4997 0 : do
4998 : {
4999 96 : bReturnFeature = false;
5000 96 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
5001 96 : CPLAssert(array->type_id() == arrow::Type::LIST);
5002 96 : auto listOfPartsArray =
5003 : static_cast<const arrow::ListArray *>(array);
5004 96 : CPLAssert(listOfPartsArray->values()->type_id() ==
5005 : arrow::Type::LIST);
5006 : auto listOfPartsValues =
5007 : std::static_pointer_cast<arrow::ListArray>(
5008 96 : listOfPartsArray->values());
5009 96 : CPLAssert(listOfPartsValues->values()->type_id() ==
5010 : arrow::Type::LIST);
5011 : auto listOfRingsValues =
5012 : std::static_pointer_cast<arrow::ListArray>(
5013 96 : listOfPartsValues->values());
5014 96 : CPLAssert(listOfRingsValues->values()->type_id() ==
5015 : arrow::Type::STRUCT);
5016 : auto pointValues = std::static_pointer_cast<arrow::StructArray>(
5017 96 : listOfRingsValues->values());
5018 96 : const auto &fields = pointValues->fields();
5019 96 : const auto &fieldX = fields[0];
5020 96 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
5021 : const auto fieldXDouble =
5022 96 : static_cast<arrow::DoubleArray *>(fieldX.get());
5023 96 : const auto &fieldY = fields[1];
5024 96 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
5025 : const auto fieldYDouble =
5026 96 : static_cast<arrow::DoubleArray *>(fieldY.get());
5027 :
5028 : while (true)
5029 : {
5030 240 : bool bMatchBBOX = false;
5031 240 : if (!listOfPartsArray->IsNull(m_nIdxInBatch))
5032 : {
5033 : const auto nParts =
5034 188 : listOfPartsArray->value_length(m_nIdxInBatch);
5035 : const auto nPartOffset =
5036 188 : listOfPartsArray->value_offset(m_nIdxInBatch);
5037 356 : for (auto j = decltype(nParts){0};
5038 356 : j < nParts && !bMatchBBOX; j++)
5039 : {
5040 168 : OGREnvelope sEnvelope;
5041 168 : const auto nRings = listOfPartsValues->value_length(
5042 168 : nPartOffset + j);
5043 : const auto nRingOffset =
5044 168 : listOfPartsValues->value_offset(nPartOffset +
5045 : j);
5046 168 : if (nRings >= 1)
5047 : {
5048 : const auto nPoints =
5049 168 : listOfRingsValues->value_length(
5050 : nRingOffset);
5051 : const auto nPointOffset =
5052 168 : listOfRingsValues->value_offset(
5053 : nRingOffset);
5054 : const double *padfRawXValue =
5055 168 : fieldXDouble->raw_values() + nPointOffset;
5056 : const double *padfRawYValue =
5057 168 : fieldYDouble->raw_values() + nPointOffset;
5058 888 : for (auto l = decltype(nPoints){0}; l < nPoints;
5059 : ++l)
5060 : {
5061 720 : sEnvelope.Merge(padfRawXValue[l],
5062 720 : padfRawYValue[l]);
5063 : }
5064 :
5065 168 : if (m_sFilterEnvelope.Intersects(sEnvelope))
5066 : {
5067 72 : bMatchBBOX = true;
5068 : }
5069 : // for bounding box, only the first ring matters
5070 : }
5071 : }
5072 : }
5073 312 : if (bMatchBBOX &&
5074 72 : (m_asAttributeFilterConstraints.empty() ||
5075 0 : !SkipToNextFeatureDueToAttributeFilter()))
5076 : {
5077 72 : bReturnFeature = true;
5078 72 : break;
5079 : }
5080 :
5081 168 : IncrFeatureIdx();
5082 168 : m_nIdxInBatch++;
5083 168 : if (m_nIdxInBatch == m_poBatch->num_rows())
5084 : {
5085 24 : m_bEOF = !ReadNextBatch();
5086 24 : if (m_bEOF)
5087 24 : return nullptr;
5088 0 : break;
5089 : }
5090 144 : }
5091 72 : } while (!bReturnFeature);
5092 : }
5093 121 : else if (iCol >= 0)
5094 : {
5095 121 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
5096 : while (true)
5097 : {
5098 220 : bool bMatchBBOX = false;
5099 :
5100 : auto poGeometry = std::unique_ptr<OGRGeometry>(
5101 220 : ReadGeometry(m_iGeomFieldFilter, array, m_nIdxInBatch));
5102 220 : if (poGeometry && !poGeometry->IsEmpty())
5103 : {
5104 115 : OGREnvelope sEnvelope;
5105 115 : poGeometry->getEnvelope(&sEnvelope);
5106 115 : if (m_sFilterEnvelope.Intersects(sEnvelope))
5107 : {
5108 88 : bMatchBBOX = true;
5109 : }
5110 : }
5111 220 : if (bMatchBBOX && (m_asAttributeFilterConstraints.empty() ||
5112 0 : !SkipToNextFeatureDueToAttributeFilter()))
5113 : {
5114 88 : break;
5115 : }
5116 :
5117 132 : IncrFeatureIdx();
5118 132 : m_nIdxInBatch++;
5119 132 : if (m_nIdxInBatch == m_poBatch->num_rows())
5120 : {
5121 33 : m_bEOF = !ReadNextBatch();
5122 33 : if (m_bEOF)
5123 33 : return nullptr;
5124 0 : array = GetStorageArray(m_poBatchColumns[iCol].get());
5125 : }
5126 99 : }
5127 1005 : }
5128 : }
5129 :
5130 9894 : else if (!m_asAttributeFilterConstraints.empty())
5131 : {
5132 : while (true)
5133 : {
5134 445 : if (!SkipToNextFeatureDueToAttributeFilter())
5135 : {
5136 243 : break;
5137 : }
5138 :
5139 202 : IncrFeatureIdx();
5140 202 : m_nIdxInBatch++;
5141 202 : if (m_nIdxInBatch == m_poBatch->num_rows())
5142 : {
5143 90 : m_bEOF = !ReadNextBatch();
5144 90 : if (m_bEOF)
5145 52 : return nullptr;
5146 : }
5147 : }
5148 : }
5149 :
5150 10847 : auto poFeature = ReadFeature(m_nIdxInBatch, m_poBatchColumns);
5151 :
5152 10847 : if (m_iFIDArrowColumn < 0)
5153 7449 : poFeature->SetFID(m_nFeatureIdx);
5154 :
5155 10847 : IncrFeatureIdx();
5156 10847 : m_nIdxInBatch++;
5157 :
5158 10847 : return poFeature;
5159 : }
5160 :
5161 : /************************************************************************/
5162 : /* GetExtentFromMetadata() */
5163 : /************************************************************************/
5164 :
5165 : inline OGRErr
5166 956 : OGRArrowLayer::GetExtentFromMetadata(const CPLJSONObject &oJSONDef,
5167 : OGREnvelope3D *psExtent)
5168 : {
5169 2868 : const auto oBBox = oJSONDef.GetArray("bbox");
5170 956 : if (oBBox.IsValid() && oBBox.Size() == 4)
5171 : {
5172 569 : psExtent->MinX = oBBox[0].ToDouble();
5173 569 : psExtent->MinY = oBBox[1].ToDouble();
5174 569 : psExtent->MinZ = std::numeric_limits<double>::infinity();
5175 569 : psExtent->MaxX = oBBox[2].ToDouble();
5176 569 : psExtent->MaxY = oBBox[3].ToDouble();
5177 569 : psExtent->MaxZ = -std::numeric_limits<double>::infinity();
5178 569 : if (psExtent->MinX <= psExtent->MaxX)
5179 569 : return OGRERR_NONE;
5180 : }
5181 387 : else if (oBBox.IsValid() && oBBox.Size() == 6)
5182 : {
5183 318 : psExtent->MinX = oBBox[0].ToDouble();
5184 318 : psExtent->MinY = oBBox[1].ToDouble();
5185 318 : psExtent->MinZ = oBBox[2].ToDouble();
5186 318 : psExtent->MaxX = oBBox[3].ToDouble();
5187 318 : psExtent->MaxY = oBBox[4].ToDouble();
5188 318 : psExtent->MaxZ = oBBox[5].ToDouble();
5189 318 : if (psExtent->MinX <= psExtent->MaxX)
5190 318 : return OGRERR_NONE;
5191 : }
5192 69 : return OGRERR_FAILURE;
5193 : }
5194 :
5195 : /************************************************************************/
5196 : /* ISetSpatialFilter() */
5197 : /************************************************************************/
5198 :
5199 1713 : inline OGRErr OGRArrowLayer::ISetSpatialFilter(int iGeomField,
5200 : const OGRGeometry *poGeomIn)
5201 :
5202 : {
5203 : // When changing filters, we need to invalidate cached batches, as
5204 : // PostFilterArrowArray() has potentially modified array contents
5205 1713 : if (m_poFilterGeom)
5206 1044 : InvalidateCachedBatches();
5207 :
5208 1713 : m_bSpatialFilterIntersectsLayerExtent = true;
5209 1713 : if (iGeomField < GetLayerDefn()->GetGeomFieldCount())
5210 : {
5211 1697 : m_iGeomFieldFilter = iGeomField;
5212 1697 : if (InstallFilter(poGeomIn))
5213 1487 : ResetReading();
5214 1697 : if (m_poFilterGeom != nullptr)
5215 : {
5216 1420 : OGREnvelope sLayerExtent;
5217 1420 : if (FastGetExtent(iGeomField, &sLayerExtent))
5218 : {
5219 812 : m_bSpatialFilterIntersectsLayerExtent =
5220 812 : CPL_TO_BOOL(m_sFilterEnvelope.Intersects(sLayerExtent));
5221 : }
5222 : }
5223 : }
5224 :
5225 1713 : SetBatch(m_poBatch);
5226 1713 : return OGRERR_NONE;
5227 : }
5228 :
5229 : /************************************************************************/
5230 : /* FastGetExtent() */
5231 : /************************************************************************/
5232 :
5233 923 : inline bool OGRArrowLayer::FastGetExtent(int iGeomField,
5234 : OGREnvelope *psExtent) const
5235 : {
5236 : {
5237 923 : const auto oIter = m_oMapExtents.find(iGeomField);
5238 923 : if (oIter != m_oMapExtents.end())
5239 : {
5240 5 : *psExtent = oIter->second;
5241 5 : return true;
5242 : }
5243 : }
5244 :
5245 : const char *pszGeomFieldName =
5246 918 : m_poFeatureDefn->GetGeomFieldDefn(iGeomField)->GetNameRef();
5247 918 : const auto oIter = m_oMapGeometryColumns.find(pszGeomFieldName);
5248 1834 : if (oIter != m_oMapGeometryColumns.end() &&
5249 916 : CPLTestBool(CPLGetConfigOption(
5250 1834 : ("OGR_" + GetDriverUCName() + "_USE_BBOX").c_str(), "YES")))
5251 : {
5252 910 : const auto &oJSONDef = oIter->second;
5253 910 : OGREnvelope3D sEnvelope3D;
5254 910 : if (GetExtentFromMetadata(oJSONDef, &sEnvelope3D) == OGRERR_NONE)
5255 : {
5256 851 : *psExtent = sEnvelope3D;
5257 851 : return true;
5258 : }
5259 : }
5260 67 : return false;
5261 : }
5262 :
5263 : /************************************************************************/
5264 : /* IGetExtent() */
5265 : /************************************************************************/
5266 :
5267 67 : inline OGRErr OGRArrowLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
5268 : bool bForce)
5269 : {
5270 67 : if (FastGetExtent(iGeomField, psExtent))
5271 : {
5272 46 : return OGRERR_NONE;
5273 : }
5274 :
5275 21 : if (!bForce && !CanRunNonForcedGetExtent())
5276 : {
5277 0 : return OGRERR_FAILURE;
5278 : }
5279 :
5280 : int iCol;
5281 21 : if (m_bIgnoredFields)
5282 : {
5283 0 : iCol = m_anMapGeomFieldIndexToArrayIndex[iGeomField];
5284 : }
5285 : else
5286 : {
5287 21 : iCol = m_anMapGeomFieldIndexToArrowColumn[iGeomField];
5288 : }
5289 21 : if (iCol < 0)
5290 : {
5291 0 : return OGRERR_FAILURE;
5292 : }
5293 :
5294 21 : if (m_aeGeomEncoding[iGeomField] == OGRArrowGeomEncoding::WKB)
5295 : {
5296 7 : ResetReading();
5297 7 : if (m_poBatch == nullptr)
5298 : {
5299 6 : m_bEOF = !ReadNextBatch();
5300 6 : if (m_bEOF)
5301 0 : return OGRERR_FAILURE;
5302 : }
5303 7 : *psExtent = OGREnvelope();
5304 :
5305 7 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
5306 7 : const arrow::BinaryArray *smallArray = nullptr;
5307 7 : const arrow::LargeBinaryArray *largeArray = nullptr;
5308 7 : if (array->type_id() == arrow::Type::BINARY)
5309 7 : smallArray = static_cast<const arrow::BinaryArray *>(array);
5310 : else
5311 : {
5312 0 : CPLAssert(array->type_id() == arrow::Type::LARGE_BINARY);
5313 0 : largeArray = static_cast<const arrow::LargeBinaryArray *>(array);
5314 : }
5315 7 : OGREnvelope sEnvelope;
5316 : while (true)
5317 : {
5318 32 : if (!array->IsNull(m_nIdxInBatch))
5319 : {
5320 30 : if (smallArray)
5321 : {
5322 30 : int out_length = 0;
5323 : const uint8_t *data =
5324 30 : smallArray->GetValue(m_nIdxInBatch, &out_length);
5325 30 : if (OGRWKBGetBoundingBox(data, out_length, sEnvelope))
5326 : {
5327 30 : psExtent->Merge(sEnvelope);
5328 : }
5329 : }
5330 : else
5331 : {
5332 0 : assert(largeArray);
5333 0 : int64_t out_length = 0;
5334 : const uint8_t *data =
5335 0 : largeArray->GetValue(m_nIdxInBatch, &out_length);
5336 0 : if (out_length < INT_MAX &&
5337 0 : OGRWKBGetBoundingBox(data, static_cast<int>(out_length),
5338 : sEnvelope))
5339 : {
5340 0 : psExtent->Merge(sEnvelope);
5341 : }
5342 : }
5343 : }
5344 :
5345 32 : m_nIdxInBatch++;
5346 32 : if (m_nIdxInBatch == m_poBatch->num_rows())
5347 : {
5348 7 : m_bEOF = !ReadNextBatch();
5349 7 : if (m_bEOF)
5350 : {
5351 7 : ResetReading();
5352 7 : if (psExtent->IsInit())
5353 : {
5354 7 : m_oMapExtents[iGeomField] = *psExtent;
5355 7 : return OGRERR_NONE;
5356 : }
5357 0 : return OGRERR_FAILURE;
5358 : }
5359 0 : array = GetStorageArray(m_poBatchColumns[iCol].get());
5360 0 : if (array->type_id() == arrow::Type::BINARY)
5361 0 : smallArray = static_cast<const arrow::BinaryArray *>(array);
5362 : else
5363 : {
5364 0 : CPLAssert(array->type_id() == arrow::Type::LARGE_BINARY);
5365 0 : largeArray =
5366 : static_cast<const arrow::LargeBinaryArray *>(array);
5367 : }
5368 : }
5369 25 : }
5370 : }
5371 14 : else if (m_aeGeomEncoding[iGeomField] ==
5372 : OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOLYGON)
5373 : {
5374 0 : ResetReading();
5375 0 : if (m_poBatch == nullptr)
5376 : {
5377 0 : m_bEOF = !ReadNextBatch();
5378 0 : if (m_bEOF)
5379 0 : return OGRERR_FAILURE;
5380 : }
5381 0 : *psExtent = OGREnvelope();
5382 :
5383 : const auto poGeomFieldDefn =
5384 0 : m_poFeatureDefn->GetGeomFieldDefn(iGeomField);
5385 0 : const auto eGeomType = poGeomFieldDefn->GetType();
5386 0 : const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
5387 0 : const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
5388 0 : const int nDim = 2 + (bHasZ ? 1 : 0) + (bHasM ? 1 : 0);
5389 :
5390 0 : begin_multipolygon:
5391 0 : auto array = m_poBatchColumns[iCol].get();
5392 0 : CPLAssert(array->type_id() == arrow::Type::LIST);
5393 0 : auto listOfPartsArray = static_cast<const arrow::ListArray *>(array);
5394 0 : CPLAssert(listOfPartsArray->values()->type_id() == arrow::Type::LIST);
5395 : auto listOfPartsValues = std::static_pointer_cast<arrow::ListArray>(
5396 0 : listOfPartsArray->values());
5397 0 : CPLAssert(listOfPartsValues->values()->type_id() == arrow::Type::LIST);
5398 : auto listOfRingsValues = std::static_pointer_cast<arrow::ListArray>(
5399 0 : listOfPartsValues->values());
5400 0 : CPLAssert(listOfRingsValues->values()->type_id() ==
5401 : arrow::Type::FIXED_SIZE_LIST);
5402 : auto listOfPointsValues =
5403 : std::static_pointer_cast<arrow::FixedSizeListArray>(
5404 0 : listOfRingsValues->values());
5405 0 : CPLAssert(listOfPointsValues->values()->type_id() ==
5406 : arrow::Type::DOUBLE);
5407 : auto pointValues = std::static_pointer_cast<arrow::DoubleArray>(
5408 0 : listOfPointsValues->values());
5409 :
5410 : while (true)
5411 : {
5412 0 : if (!listOfPartsArray->IsNull(m_nIdxInBatch))
5413 : {
5414 : const auto nParts =
5415 0 : listOfPartsArray->value_length(m_nIdxInBatch);
5416 : const auto nPartOffset =
5417 0 : listOfPartsArray->value_offset(m_nIdxInBatch);
5418 0 : for (auto j = decltype(nParts){0}; j < nParts; j++)
5419 : {
5420 : const auto nRings =
5421 0 : listOfPartsValues->value_length(nPartOffset + j);
5422 : const auto nRingOffset =
5423 0 : listOfPartsValues->value_offset(nPartOffset + j);
5424 0 : if (nRings >= 1)
5425 : {
5426 : const auto nPoints =
5427 0 : listOfRingsValues->value_length(nRingOffset);
5428 : const auto nPointOffset =
5429 0 : listOfRingsValues->value_offset(nRingOffset) * nDim;
5430 : const double *padfRawValue =
5431 0 : pointValues->raw_values() + nPointOffset;
5432 0 : for (auto l = decltype(nPoints){0}; l < nPoints; ++l)
5433 : {
5434 0 : psExtent->Merge(padfRawValue[nDim * l],
5435 0 : padfRawValue[nDim * l + 1]);
5436 : }
5437 : // for bounding box, only the first ring matters
5438 : }
5439 : }
5440 : }
5441 :
5442 0 : m_nIdxInBatch++;
5443 0 : if (m_nIdxInBatch == m_poBatch->num_rows())
5444 : {
5445 0 : m_bEOF = !ReadNextBatch();
5446 0 : if (m_bEOF)
5447 : {
5448 0 : ResetReading();
5449 0 : if (psExtent->IsInit())
5450 : {
5451 0 : m_oMapExtents[iGeomField] = *psExtent;
5452 0 : return OGRERR_NONE;
5453 : }
5454 0 : return OGRERR_FAILURE;
5455 : }
5456 0 : goto begin_multipolygon;
5457 : }
5458 0 : }
5459 : }
5460 :
5461 14 : return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
5462 : }
5463 :
5464 : /************************************************************************/
5465 : /* FastGetExtent3D() */
5466 : /************************************************************************/
5467 :
5468 38 : inline bool OGRArrowLayer::FastGetExtent3D(int iGeomField,
5469 : OGREnvelope3D *psExtent) const
5470 : {
5471 : {
5472 38 : const auto oIter = m_oMapExtents3D.find(iGeomField);
5473 38 : if (oIter != m_oMapExtents3D.end())
5474 : {
5475 0 : *psExtent = oIter->second;
5476 0 : return true;
5477 : }
5478 : }
5479 :
5480 : const char *pszGeomFieldName =
5481 38 : m_poFeatureDefn->GetGeomFieldDefn(iGeomField)->GetNameRef();
5482 38 : const auto oIter = m_oMapGeometryColumns.find(pszGeomFieldName);
5483 76 : if (oIter != m_oMapGeometryColumns.end() &&
5484 38 : CPLTestBool(CPLGetConfigOption(
5485 76 : ("OGR_" + GetDriverUCName() + "_USE_BBOX").c_str(), "YES")))
5486 : {
5487 36 : const auto &oJSONDef = oIter->second;
5488 64 : if (GetExtentFromMetadata(oJSONDef, psExtent) == OGRERR_NONE &&
5489 28 : psExtent->Is3D())
5490 : {
5491 2 : return true;
5492 : }
5493 : }
5494 36 : return false;
5495 : }
5496 :
5497 : /************************************************************************/
5498 : /* IGetExtent3D() */
5499 : /************************************************************************/
5500 :
5501 19 : inline OGRErr OGRArrowLayer::IGetExtent3D(int iGeomField,
5502 : OGREnvelope3D *psExtent, bool bForce)
5503 : {
5504 19 : if (FastGetExtent3D(iGeomField, psExtent))
5505 : {
5506 1 : return OGRERR_NONE;
5507 : }
5508 :
5509 18 : return OGRLayer::IGetExtent3D(iGeomField, psExtent, bForce);
5510 : }
5511 :
5512 : /************************************************************************/
5513 : /* OverrideArrowSchemaRelease() */
5514 : /************************************************************************/
5515 :
5516 : template <class T>
5517 611 : static void OverrideArrowRelease(OGRArrowDataset *poDS, T *obj)
5518 : {
5519 : // We override the release callback, since it can use the memory pool,
5520 : // and we need to make sure it is still alive when the object (ArrowArray
5521 : // or ArrowSchema) is deleted
5522 611 : struct OverriddenPrivate
5523 : {
5524 : OverriddenPrivate() = default;
5525 : OverriddenPrivate(const OverriddenPrivate &) = delete;
5526 : OverriddenPrivate &operator=(const OverriddenPrivate &) = delete;
5527 :
5528 : std::shared_ptr<arrow::MemoryPool> poMemoryPool{};
5529 : void (*pfnPreviousRelease)(T *) = nullptr;
5530 : void *pPreviousPrivateData = nullptr;
5531 :
5532 611 : static void release(T *l_obj)
5533 : {
5534 611 : OverriddenPrivate *myPrivate =
5535 : static_cast<OverriddenPrivate *>(l_obj->private_data);
5536 611 : l_obj->private_data = myPrivate->pPreviousPrivateData;
5537 611 : l_obj->release = myPrivate->pfnPreviousRelease;
5538 611 : l_obj->release(l_obj);
5539 611 : delete myPrivate;
5540 611 : }
5541 : };
5542 :
5543 611 : auto overriddenPrivate = new OverriddenPrivate();
5544 611 : overriddenPrivate->poMemoryPool = poDS->GetSharedMemoryPool();
5545 611 : overriddenPrivate->pPreviousPrivateData = obj->private_data;
5546 611 : overriddenPrivate->pfnPreviousRelease = obj->release;
5547 :
5548 611 : obj->release = OverriddenPrivate::release;
5549 611 : obj->private_data = overriddenPrivate;
5550 611 : }
5551 :
5552 : /************************************************************************/
5553 : /* UseRecordBatchBaseImplementation() */
5554 : /************************************************************************/
5555 :
5556 475 : inline bool OGRArrowLayer::UseRecordBatchBaseImplementation() const
5557 : {
5558 475 : if (CPLTestBool(CPLGetConfigOption("OGR_ARROW_STREAM_BASE_IMPL", "NO")))
5559 : {
5560 4 : return true;
5561 : }
5562 :
5563 471 : if (m_aosArrowArrayStreamOptions.FetchBool(GAS_OPT_DATETIME_AS_STRING,
5564 : false))
5565 : {
5566 1 : const int nFieldCount = m_poFeatureDefn->GetFieldCount();
5567 14 : for (int i = 0; i < nFieldCount; ++i)
5568 : {
5569 14 : const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
5570 28 : if (!poFieldDefn->IsIgnored() &&
5571 14 : poFieldDefn->GetType() == OFTDateTime)
5572 : {
5573 1 : CPLDebug("ARROW",
5574 : "DATETIME_AS_STRING=YES not compatible of fast "
5575 : "Arrow implementation");
5576 1 : return true;
5577 : }
5578 : }
5579 : }
5580 :
5581 470 : if (EQUAL(m_aosArrowArrayStreamOptions.FetchNameValueDef(
5582 : "GEOMETRY_ENCODING", ""),
5583 : "WKB"))
5584 : {
5585 131 : const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
5586 181 : for (int i = 0; i < nGeomFieldCount; i++)
5587 : {
5588 146 : if (!m_poFeatureDefn->GetGeomFieldDefn(i)->IsIgnored() &&
5589 259 : m_aeGeomEncoding[i] != OGRArrowGeomEncoding::WKB &&
5590 113 : m_aeGeomEncoding[i] != OGRArrowGeomEncoding::WKT)
5591 : {
5592 96 : CPLDebug("ARROW", "Geometry encoding not compatible of fast "
5593 : "Arrow implementation");
5594 96 : return true;
5595 : }
5596 : }
5597 : }
5598 :
5599 374 : if (m_bIgnoredFields)
5600 : {
5601 : std::vector<int> ignoredState(m_anMapFieldIndexToArrowColumn.size(),
5602 107 : -1);
5603 7661 : for (size_t i = 0; i < m_anMapFieldIndexToArrowColumn.size(); i++)
5604 : {
5605 7555 : const int nArrowCol = m_anMapFieldIndexToArrowColumn[i][0];
5606 7555 : if (nArrowCol >= static_cast<int>(ignoredState.size()))
5607 2 : ignoredState.resize(nArrowCol + 1, -1);
5608 : const auto bIsIgnored =
5609 7555 : m_poFeatureDefn->GetFieldDefn(static_cast<int>(i))->IsIgnored();
5610 7555 : if (ignoredState[nArrowCol] < 0)
5611 : {
5612 7097 : ignoredState[nArrowCol] = static_cast<int>(bIsIgnored);
5613 : }
5614 : else
5615 : {
5616 : // struct fields will point to the same arrow column
5617 458 : if (ignoredState[nArrowCol] != static_cast<int>(bIsIgnored))
5618 : {
5619 1 : CPLDebug("ARROW",
5620 : "Inconsistent ignore state for Arrow Columns");
5621 1 : return true;
5622 : }
5623 : }
5624 : }
5625 : }
5626 :
5627 373 : if (m_poAttrQuery || m_poFilterGeom)
5628 : {
5629 163 : struct ArrowSchema *psSchema = &m_sCachedSchema;
5630 163 : if (psSchema->release)
5631 104 : psSchema->release(psSchema);
5632 163 : memset(psSchema, 0, sizeof(*psSchema));
5633 :
5634 326 : const bool bCanPostFilter = GetArrowSchemaInternal(psSchema) == 0 &&
5635 163 : CanPostFilterArrowArray(psSchema);
5636 163 : if (!bCanPostFilter)
5637 11 : return true;
5638 : }
5639 :
5640 362 : return false;
5641 : }
5642 :
5643 : /************************************************************************/
5644 : /* GetArrowStream() */
5645 : /************************************************************************/
5646 :
5647 294 : inline bool OGRArrowLayer::GetArrowStream(struct ArrowArrayStream *out_stream,
5648 : CSLConstList papszOptions)
5649 : {
5650 294 : if (!OGRLayer::GetArrowStream(out_stream, papszOptions))
5651 0 : return false;
5652 :
5653 294 : m_bUseRecordBatchBaseImplementation = UseRecordBatchBaseImplementation();
5654 294 : return true;
5655 : }
5656 :
5657 : /************************************************************************/
5658 : /* GetArrowSchema() */
5659 : /************************************************************************/
5660 :
5661 434 : inline int OGRArrowLayer::GetArrowSchema(struct ArrowArrayStream *stream,
5662 : struct ArrowSchema *out_schema)
5663 : {
5664 434 : if (m_bUseRecordBatchBaseImplementation)
5665 209 : return OGRLayer::GetArrowSchema(stream, out_schema);
5666 :
5667 225 : return GetArrowSchemaInternal(out_schema);
5668 : }
5669 :
5670 : /************************************************************************/
5671 : /* GetArrowSchemaInternal() */
5672 : /************************************************************************/
5673 :
5674 10802 : static bool IsSilentlyIgnoredFormatForGetArrowSchemaArray(const char *format)
5675 : {
5676 : // n: null
5677 10802 : return strcmp(format, "n") == 0;
5678 : }
5679 :
5680 : inline int
5681 388 : OGRArrowLayer::GetArrowSchemaInternal(struct ArrowSchema *out_schema) const
5682 : {
5683 776 : auto status = arrow::ExportSchema(*m_poSchema, out_schema);
5684 388 : if (!status.ok())
5685 : {
5686 0 : CPLError(CE_Failure, CPLE_AppDefined, "ExportSchema() failed with %s",
5687 0 : status.message().c_str());
5688 0 : return EIO;
5689 : }
5690 :
5691 388 : CPLAssert(out_schema->n_children == m_poSchema->num_fields());
5692 :
5693 : // Remove ignored fields from the ArrowSchema.
5694 :
5695 : struct FieldDesc
5696 : {
5697 : bool bIsRegularField =
5698 : false; // true = attribute field, false = geometry field
5699 : int nIdx = -1;
5700 : };
5701 :
5702 : // cppcheck-suppress unreadVariable
5703 : std::vector<FieldDesc> fieldDesc(
5704 776 : static_cast<size_t>(out_schema->n_children));
5705 21819 : for (size_t i = 0; i < m_anMapFieldIndexToArrowColumn.size(); i++)
5706 : {
5707 21431 : const int nArrowCol = m_anMapFieldIndexToArrowColumn[i][0];
5708 21431 : if (fieldDesc[nArrowCol].nIdx < 0)
5709 : {
5710 20150 : fieldDesc[nArrowCol].bIsRegularField = true;
5711 20150 : fieldDesc[nArrowCol].nIdx = static_cast<int>(i);
5712 : }
5713 : }
5714 790 : for (size_t i = 0; i < m_anMapGeomFieldIndexToArrowColumn.size(); i++)
5715 : {
5716 402 : const int nArrowCol = m_anMapGeomFieldIndexToArrowColumn[i];
5717 402 : CPLAssert(fieldDesc[nArrowCol].nIdx < 0);
5718 402 : fieldDesc[nArrowCol].bIsRegularField = false;
5719 402 : fieldDesc[nArrowCol].nIdx = static_cast<int>(i);
5720 : }
5721 :
5722 388 : int j = 0;
5723 : const char *pszReqGeomEncoding =
5724 388 : m_aosArrowArrayStreamOptions.FetchNameValueDef("GEOMETRY_ENCODING", "");
5725 :
5726 388 : const char *pszExtensionName = EXTENSION_NAME_OGC_WKB;
5727 388 : if (EQUAL(pszReqGeomEncoding, "WKB") || EQUAL(pszReqGeomEncoding, ""))
5728 : {
5729 : const char *const pszGeometryMetadataEncoding =
5730 388 : m_aosArrowArrayStreamOptions.FetchNameValue(
5731 : "GEOMETRY_METADATA_ENCODING");
5732 388 : if (pszGeometryMetadataEncoding)
5733 : {
5734 0 : if (EQUAL(pszGeometryMetadataEncoding, "OGC"))
5735 0 : pszExtensionName = EXTENSION_NAME_OGC_WKB;
5736 0 : else if (EQUAL(pszGeometryMetadataEncoding, "GEOARROW"))
5737 0 : pszExtensionName = EXTENSION_NAME_GEOARROW_WKB;
5738 : else
5739 0 : CPLError(CE_Warning, CPLE_NotSupported,
5740 : "Unsupported GEOMETRY_METADATA_ENCODING value: %s",
5741 : pszGeometryMetadataEncoding);
5742 : }
5743 : }
5744 :
5745 21298 : for (int i = 0; i < out_schema->n_children; ++i)
5746 : {
5747 20910 : if (fieldDesc[i].nIdx < 0)
5748 : {
5749 358 : if (m_iFIDArrowColumn == i)
5750 : {
5751 21 : out_schema->children[j] = out_schema->children[i];
5752 21 : ++j;
5753 : }
5754 337 : else if (cpl::contains(m_oSetBBoxArrowColumns, i))
5755 : {
5756 : // Remove bounding box columns from exported schema
5757 82 : out_schema->children[i]->release(out_schema->children[i]);
5758 82 : out_schema->children[i] = nullptr;
5759 : }
5760 255 : else if (IsSilentlyIgnoredFormatForGetArrowSchemaArray(
5761 255 : out_schema->children[i]->format))
5762 : {
5763 : // Silently ignore columns with null data type...
5764 255 : out_schema->children[i]->release(out_schema->children[i]);
5765 : }
5766 : else
5767 : {
5768 : // can happen with data types we don't support
5769 0 : if (m_aosArrowArrayStreamOptions.FetchBool(
5770 : "SILENCE_GET_SCHEMA_ERROR", false))
5771 : {
5772 0 : CPLDebug(GetDriverUCName().c_str(),
5773 : "GetArrowSchema() error: fieldDesc[%d].nIdx < 0 "
5774 : "not expected: name=%s, format=%s",
5775 0 : i, out_schema->children[i]->name,
5776 0 : out_schema->children[i]->format);
5777 : }
5778 : else
5779 : {
5780 0 : CPLError(CE_Failure, CPLE_NotSupported,
5781 : "GetArrowSchema() error: fieldDesc[%d].nIdx < 0 "
5782 : "not expected: name=%s, format=%s",
5783 0 : i, out_schema->children[i]->name,
5784 0 : out_schema->children[i]->format);
5785 : }
5786 0 : for (; i < out_schema->n_children; ++i, ++j)
5787 0 : out_schema->children[j] = out_schema->children[i];
5788 0 : out_schema->n_children = j;
5789 :
5790 0 : out_schema->release(out_schema);
5791 :
5792 0 : return EIO;
5793 : }
5794 358 : continue;
5795 : }
5796 :
5797 : const auto bIsIgnored =
5798 20552 : fieldDesc[i].bIsRegularField
5799 20552 : ? m_poFeatureDefn->GetFieldDefn(fieldDesc[i].nIdx)->IsIgnored()
5800 402 : : m_poFeatureDefn->GetGeomFieldDefn(fieldDesc[i].nIdx)
5801 402 : ->IsIgnored();
5802 20552 : if (bIsIgnored)
5803 : {
5804 2912 : out_schema->children[i]->release(out_schema->children[i]);
5805 : }
5806 : else
5807 : {
5808 18023 : if (!fieldDesc[i].bIsRegularField &&
5809 383 : EQUAL(pszReqGeomEncoding, "WKB"))
5810 : {
5811 64 : const int iGeomField = fieldDesc[i].nIdx;
5812 64 : if (m_aeGeomEncoding[iGeomField] == OGRArrowGeomEncoding::WKT)
5813 : {
5814 : const auto poGeomFieldDefn =
5815 17 : m_poFeatureDefn->GetGeomFieldDefn(iGeomField);
5816 17 : CPLAssert(strcmp(out_schema->children[i]->name,
5817 : poGeomFieldDefn->GetNameRef()) == 0);
5818 17 : auto poSchema = CreateSchemaForWKBGeometryColumn(
5819 : poGeomFieldDefn, "z", pszExtensionName);
5820 17 : out_schema->children[i]->release(out_schema->children[i]);
5821 17 : *(out_schema->children[j]) = *poSchema;
5822 17 : CPLFree(poSchema);
5823 : }
5824 47 : else if (m_aeGeomEncoding[iGeomField] !=
5825 : OGRArrowGeomEncoding::WKB)
5826 : {
5827 : // Shouldn't happen if UseRecordBatchBaseImplementation()
5828 : // is up to date
5829 0 : CPLAssert(false);
5830 : }
5831 : else
5832 : {
5833 47 : out_schema->children[j] = out_schema->children[i];
5834 : }
5835 : }
5836 : else
5837 : {
5838 17576 : out_schema->children[j] = out_schema->children[i];
5839 : }
5840 :
5841 18023 : if (!fieldDesc[i].bIsRegularField &&
5842 383 : (EQUAL(pszReqGeomEncoding, "WKB") ||
5843 319 : EQUAL(pszReqGeomEncoding, "")))
5844 : {
5845 383 : const int iGeomField = fieldDesc[i].nIdx;
5846 383 : const char *pszFormat = out_schema->children[j]->format;
5847 383 : if (m_aeGeomEncoding[iGeomField] == OGRArrowGeomEncoding::WKB &&
5848 695 : !out_schema->children[j]->metadata &&
5849 312 : (strcmp(pszFormat, "z") == 0 ||
5850 1 : strcmp(pszFormat, "Z") == 0))
5851 : {
5852 : const auto poGeomFieldDefn =
5853 312 : m_poFeatureDefn->GetGeomFieldDefn(iGeomField);
5854 : // Set ARROW:extension:name = ogc:wkb
5855 312 : auto poSchema = CreateSchemaForWKBGeometryColumn(
5856 : poGeomFieldDefn, pszFormat, pszExtensionName);
5857 312 : out_schema->children[i]->release(out_schema->children[i]);
5858 312 : *(out_schema->children[j]) = *poSchema;
5859 312 : CPLFree(poSchema);
5860 : }
5861 : }
5862 :
5863 17640 : ++j;
5864 : }
5865 : }
5866 :
5867 388 : out_schema->n_children = j;
5868 :
5869 388 : OverrideArrowRelease(m_poArrowDS, out_schema);
5870 :
5871 388 : return 0;
5872 : }
5873 :
5874 : /************************************************************************/
5875 : /* GetNextArrowArray() */
5876 : /************************************************************************/
5877 :
5878 606 : inline int OGRArrowLayer::GetNextArrowArray(struct ArrowArrayStream *stream,
5879 : struct ArrowArray *out_array)
5880 : {
5881 606 : if (m_bUseRecordBatchBaseImplementation)
5882 224 : return OGRLayer::GetNextArrowArray(stream, out_array);
5883 :
5884 : while (true)
5885 : {
5886 400 : if (m_bEOF)
5887 : {
5888 10 : memset(out_array, 0, sizeof(*out_array));
5889 177 : return 0;
5890 : }
5891 :
5892 390 : if (m_poBatch == nullptr || m_nIdxInBatch == m_poBatch->num_rows())
5893 : {
5894 365 : if (!ReadNextBatch())
5895 : {
5896 167 : if (m_poAttrQuery || m_poFilterGeom)
5897 : {
5898 97 : InvalidateCachedBatches();
5899 : }
5900 167 : m_bEOF = true;
5901 167 : memset(out_array, 0, sizeof(*out_array));
5902 167 : return 0;
5903 : }
5904 : }
5905 :
5906 223 : const bool bNeedsPostFilter =
5907 335 : (m_poAttrQuery && !m_bBaseArrowIgnoreAttributeFilter) ||
5908 112 : (m_poFilterGeom && !m_bBaseArrowIgnoreSpatialFilter);
5909 :
5910 : struct ArrowSchema schema;
5911 223 : memset(&schema, 0, sizeof(schema));
5912 223 : auto status = arrow::ExportRecordBatch(*m_poBatch, out_array, &schema);
5913 223 : m_nIdxInBatch = m_poBatch->num_rows();
5914 223 : if (!status.ok())
5915 : {
5916 0 : CPLError(CE_Failure, CPLE_AppDefined,
5917 : "ExportRecordBatch() failed with %s",
5918 0 : status.message().c_str());
5919 0 : return EIO;
5920 : }
5921 :
5922 : // Remove bounding box columns from exported array, or columns
5923 : // of unsupported data types that we voluntarily strip off.
5924 : const auto RemoveBBoxOrUnsupportedColumns =
5925 32662 : [out_array, &schema](const std::set<int> &oSetBBoxArrayIndex)
5926 : {
5927 223 : int j = 0;
5928 10820 : for (int i = 0; i < static_cast<int>(schema.n_children); ++i)
5929 : {
5930 21144 : if (cpl::contains(oSetBBoxArrayIndex, i) ||
5931 10547 : IsSilentlyIgnoredFormatForGetArrowSchemaArray(
5932 10547 : schema.children[i]->format))
5933 : {
5934 126 : out_array->children[i]->release(out_array->children[i]);
5935 126 : out_array->children[i] = nullptr;
5936 :
5937 126 : schema.children[i]->release(schema.children[i]);
5938 126 : schema.children[i] = nullptr;
5939 : }
5940 : else
5941 : {
5942 10471 : out_array->children[j] = out_array->children[i];
5943 10471 : schema.children[j] = schema.children[i];
5944 10471 : ++j;
5945 : }
5946 : }
5947 223 : out_array->n_children = j;
5948 223 : schema.n_children = j;
5949 223 : };
5950 :
5951 223 : if (m_bIgnoredFields)
5952 : {
5953 180 : std::set<int> oSetBBoxArrayIndex;
5954 127 : for (const auto &iter : m_oMapGeomFieldIndexToGeomColBBOX)
5955 : {
5956 37 : if (iter.second.iArrayIdx >= 0)
5957 20 : oSetBBoxArrayIndex.insert(iter.second.iArrayIdx);
5958 : }
5959 90 : RemoveBBoxOrUnsupportedColumns(oSetBBoxArrayIndex);
5960 : }
5961 : else
5962 : {
5963 133 : RemoveBBoxOrUnsupportedColumns(m_oSetBBoxArrowColumns);
5964 : }
5965 :
5966 223 : if (EQUAL(m_aosArrowArrayStreamOptions.FetchNameValueDef(
5967 : "GEOMETRY_ENCODING", ""),
5968 : "WKB"))
5969 : {
5970 40 : const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
5971 95 : for (int i = 0; i < nGeomFieldCount; i++)
5972 : {
5973 : const auto poGeomFieldDefn =
5974 55 : m_poFeatureDefn->GetGeomFieldDefn(i);
5975 55 : if (!poGeomFieldDefn->IsIgnored())
5976 : {
5977 47 : if (m_aeGeomEncoding[i] == OGRArrowGeomEncoding::WKT)
5978 : {
5979 : const int nArrayIdx =
5980 18 : m_bIgnoredFields
5981 26 : ? m_anMapGeomFieldIndexToArrayIndex[i]
5982 8 : : m_anMapGeomFieldIndexToArrowColumn[i];
5983 18 : auto sourceArray = out_array->children[nArrayIdx];
5984 : auto targetArray =
5985 18 : strcmp(schema.children[nArrayIdx]->format, "u") == 0
5986 18 : ? CreateWKBArrayFromWKTArray<uint32_t>(
5987 : sourceArray)
5988 0 : : CreateWKBArrayFromWKTArray<uint64_t>(
5989 18 : sourceArray);
5990 18 : if (targetArray)
5991 : {
5992 18 : sourceArray->release(sourceArray);
5993 18 : *(out_array->children[nArrayIdx]) = *targetArray;
5994 18 : CPLFree(targetArray);
5995 : }
5996 : else
5997 : {
5998 0 : out_array->release(out_array);
5999 0 : memset(out_array, 0, sizeof(*out_array));
6000 0 : if (schema.release)
6001 0 : schema.release(&schema);
6002 0 : return ENOMEM;
6003 : }
6004 : }
6005 29 : else if (m_aeGeomEncoding[i] != OGRArrowGeomEncoding::WKB)
6006 : {
6007 : // Shouldn't happen if UseRecordBatchBaseImplementation()
6008 : // is up to date
6009 0 : CPLAssert(false);
6010 : }
6011 : }
6012 : }
6013 : }
6014 :
6015 223 : if (schema.release)
6016 223 : schema.release(&schema);
6017 :
6018 223 : OverrideArrowRelease(m_poArrowDS, out_array);
6019 :
6020 223 : const auto nFeatureIdxCur = m_nFeatureIdx;
6021 : // TODO: We likely have an issue regarding FIDs based on m_nFeatureIdx
6022 : // when m_iFIDArrowColumn < 0, only a subset of row groups is
6023 : // selected, and this batch goes across non consecutive row groups.
6024 1123 : for (int64_t i = 0; i < m_nIdxInBatch; ++i)
6025 900 : IncrFeatureIdx();
6026 :
6027 223 : if (bNeedsPostFilter)
6028 : {
6029 130 : CPLStringList aosOptions;
6030 130 : if (m_iFIDArrowColumn < 0)
6031 : aosOptions.SetNameValue(
6032 : "BASE_SEQUENTIAL_FID",
6033 : CPLSPrintf(CPL_FRMT_GIB,
6034 122 : static_cast<GIntBig>(nFeatureIdxCur)));
6035 :
6036 : // If there might be more than one record batch, it is more
6037 : // prudent to clone the array before modifying it.
6038 130 : if (nFeatureIdxCur > 0 || !TestCapability(OLCFastFeatureCount) ||
6039 0 : out_array->length < GetFeatureCount(false))
6040 : {
6041 : struct ArrowArray new_array;
6042 130 : if (!OGRCloneArrowArray(&m_sCachedSchema, out_array,
6043 : &new_array))
6044 : {
6045 0 : if (out_array->release)
6046 0 : out_array->release(out_array);
6047 0 : memset(out_array, 0, sizeof(*out_array));
6048 0 : return ENOMEM;
6049 : }
6050 130 : if (out_array->release)
6051 130 : out_array->release(out_array);
6052 130 : memcpy(out_array, &new_array, sizeof(new_array));
6053 : }
6054 :
6055 130 : PostFilterArrowArray(&m_sCachedSchema, out_array,
6056 130 : aosOptions.List());
6057 130 : if (out_array->length == 0)
6058 : {
6059 18 : if (out_array->release)
6060 18 : out_array->release(out_array);
6061 18 : memset(out_array, 0, sizeof(*out_array));
6062 : // If there are no records after filtering, start again
6063 : // with a new batch
6064 18 : continue;
6065 : }
6066 : }
6067 :
6068 205 : break;
6069 18 : }
6070 :
6071 205 : return 0;
6072 : }
6073 :
6074 : /************************************************************************/
6075 : /* OGRArrowLayerAppendBuffer */
6076 : /************************************************************************/
6077 :
6078 : class OGRArrowLayerAppendBuffer : public OGRAppendBuffer
6079 : {
6080 : public:
6081 18 : OGRArrowLayerAppendBuffer(struct ArrowArray *targetArrayIn,
6082 : size_t nInitialCapacityIn)
6083 18 : : m_psTargetArray(targetArrayIn)
6084 : {
6085 18 : m_nCapacity = nInitialCapacityIn;
6086 18 : m_pRawBuffer = const_cast<void *>(m_psTargetArray->buffers[2]);
6087 18 : }
6088 :
6089 : protected:
6090 4 : bool Grow(size_t nItemSize) override
6091 : {
6092 4 : constexpr uint32_t MAX_SIZE_SINT32 =
6093 : static_cast<uint32_t>(std::numeric_limits<int32_t>::max());
6094 4 : if (nItemSize > MAX_SIZE_SINT32 - m_nSize)
6095 : {
6096 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too large WKT content");
6097 0 : return false;
6098 : }
6099 4 : size_t nNewCapacity = m_nSize + nItemSize;
6100 4 : CPLAssert(m_nCapacity <= MAX_SIZE_SINT32);
6101 : const size_t nDoubleCapacity =
6102 4 : std::min<size_t>(MAX_SIZE_SINT32, 2 * m_nCapacity);
6103 4 : if (nNewCapacity < nDoubleCapacity)
6104 4 : nNewCapacity = nDoubleCapacity;
6105 4 : CPLAssert(nNewCapacity <= MAX_SIZE_SINT32);
6106 4 : void *newBuffer = VSI_MALLOC_ALIGNED_AUTO_VERBOSE(nNewCapacity);
6107 4 : if (newBuffer == nullptr)
6108 : {
6109 0 : return false;
6110 : }
6111 4 : m_nCapacity = nNewCapacity;
6112 4 : memcpy(newBuffer, m_pRawBuffer, m_nSize);
6113 4 : VSIFreeAligned(m_pRawBuffer);
6114 4 : m_pRawBuffer = newBuffer;
6115 4 : m_psTargetArray->buffers[2] = m_pRawBuffer;
6116 4 : return true;
6117 : }
6118 :
6119 : private:
6120 : struct ArrowArray *m_psTargetArray;
6121 :
6122 : OGRArrowLayerAppendBuffer(const OGRArrowLayerAppendBuffer &) = delete;
6123 : OGRArrowLayerAppendBuffer &
6124 : operator=(const OGRArrowLayerAppendBuffer &) = delete;
6125 : };
6126 :
6127 : /************************************************************************/
6128 : /* CreateWKBArrayFromWKTArray() */
6129 : /************************************************************************/
6130 :
6131 : template <typename SourceOffset>
6132 : inline struct ArrowArray *
6133 18 : OGRArrowLayer::CreateWKBArrayFromWKTArray(const struct ArrowArray *sourceArray)
6134 : {
6135 18 : CPLAssert(sourceArray->n_buffers == 3);
6136 18 : CPLAssert(sourceArray->buffers[1] != nullptr);
6137 18 : CPLAssert(sourceArray->buffers[2] != nullptr);
6138 :
6139 18 : const size_t nLength = static_cast<size_t>(sourceArray->length);
6140 : auto targetArray = static_cast<struct ArrowArray *>(
6141 18 : CPLCalloc(1, sizeof(struct ArrowArray)));
6142 18 : targetArray->release = OGRLayer::ReleaseArray;
6143 18 : targetArray->length = nLength;
6144 :
6145 18 : targetArray->n_buffers = 3;
6146 18 : targetArray->buffers =
6147 18 : static_cast<const void **>(CPLCalloc(3, sizeof(void *)));
6148 :
6149 : // Allocate validity map buffer if needed
6150 18 : const auto sourceNull =
6151 18 : static_cast<const uint8_t *>(sourceArray->buffers[0]);
6152 18 : const size_t nOffset = static_cast<size_t>(sourceArray->offset);
6153 18 : uint8_t *targetNull = nullptr;
6154 18 : if (sourceArray->null_count && sourceNull)
6155 : {
6156 6 : targetArray->buffers[0] =
6157 3 : VSI_MALLOC_ALIGNED_AUTO_VERBOSE((nLength + 7) / 8);
6158 3 : if (targetArray->buffers[0])
6159 : {
6160 3 : targetArray->null_count = sourceArray->null_count;
6161 3 : targetNull = static_cast<uint8_t *>(
6162 3 : const_cast<void *>(targetArray->buffers[0]));
6163 3 : if (nOffset == 0)
6164 : {
6165 3 : memcpy(targetNull, sourceNull, (nLength + 7) / 8);
6166 : }
6167 : else
6168 : {
6169 0 : memset(targetNull, 0, (nLength + 7) / 8);
6170 0 : for (size_t i = 0; i < nLength; ++i)
6171 : {
6172 0 : if ((sourceNull[(i + nOffset) / 8] >> ((i + nOffset) % 8)) &
6173 : 1)
6174 0 : targetNull[i / 8] |= (1 << (i % 8));
6175 : }
6176 : }
6177 : }
6178 : }
6179 :
6180 : // Allocate offset buffer
6181 36 : targetArray->buffers[1] =
6182 18 : VSI_MALLOC_ALIGNED_AUTO_VERBOSE(sizeof(uint32_t) * (1 + nLength));
6183 :
6184 : // Allocate data (WKB) buffer
6185 18 : constexpr size_t DEFAULT_WKB_SIZE = 100;
6186 18 : uint32_t nInitialCapacity = static_cast<uint32_t>(std::min<size_t>(
6187 18 : std::numeric_limits<int32_t>::max(), DEFAULT_WKB_SIZE * nLength));
6188 18 : targetArray->buffers[2] = VSI_MALLOC_ALIGNED_AUTO_VERBOSE(nInitialCapacity);
6189 :
6190 : // Check buffers have been allocated
6191 18 : if ((sourceArray->null_count && sourceNull && !targetNull) ||
6192 18 : targetArray->buffers[1] == nullptr ||
6193 18 : targetArray->buffers[2] == nullptr)
6194 : {
6195 0 : targetArray->release(targetArray);
6196 0 : return nullptr;
6197 : }
6198 :
6199 36 : OGRArrowLayerAppendBuffer oOGRAppendBuffer(targetArray, nInitialCapacity);
6200 18 : OGRWKTToWKBTranslator oTranslator(oOGRAppendBuffer);
6201 :
6202 18 : const auto sourceOffsets =
6203 18 : static_cast<const SourceOffset *>(sourceArray->buffers[1]) + nOffset;
6204 18 : auto sourceBytes =
6205 18 : static_cast<char *>(const_cast<void *>(sourceArray->buffers[2]));
6206 18 : auto targetOffsets =
6207 18 : static_cast<uint32_t *>(const_cast<void *>(targetArray->buffers[1]));
6208 159 : for (size_t i = 0; i < nLength; ++i)
6209 : {
6210 141 : targetOffsets[i] = static_cast<uint32_t>(oOGRAppendBuffer.GetSize());
6211 :
6212 141 : if (targetNull && ((targetNull[i / 8] >> (i % 8)) & 1) == 0)
6213 : {
6214 3 : continue;
6215 : }
6216 :
6217 276 : const size_t nWKBSize = oTranslator.TranslateWKT(
6218 138 : sourceBytes + sourceOffsets[i],
6219 138 : static_cast<size_t>(sourceOffsets[i + 1] - sourceOffsets[i]),
6220 138 : sourceOffsets[i + 1] < sourceOffsets[nLength]);
6221 138 : if (nWKBSize == static_cast<size_t>(-1))
6222 : {
6223 0 : targetArray->release(targetArray);
6224 0 : return nullptr;
6225 : }
6226 : }
6227 18 : targetOffsets[nLength] = static_cast<uint32_t>(oOGRAppendBuffer.GetSize());
6228 :
6229 18 : return targetArray;
6230 : }
6231 :
6232 : /************************************************************************/
6233 : /* TestCapability() */
6234 : /************************************************************************/
6235 :
6236 913 : inline int OGRArrowLayer::TestCapability(const char *pszCap) const
6237 : {
6238 :
6239 913 : if (EQUAL(pszCap, OLCStringsAsUTF8))
6240 505 : return true;
6241 :
6242 589 : else if (EQUAL(pszCap, OLCFastGetArrowStream) &&
6243 181 : !UseRecordBatchBaseImplementation())
6244 : {
6245 181 : return true;
6246 : }
6247 :
6248 227 : if (EQUAL(pszCap, OLCFastGetExtent))
6249 : {
6250 31 : OGREnvelope sEnvelope;
6251 48 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
6252 : {
6253 31 : if (!FastGetExtent(i, &sEnvelope))
6254 14 : return false;
6255 : }
6256 17 : return true;
6257 : }
6258 :
6259 196 : if (EQUAL(pszCap, OLCFastGetExtent3D))
6260 : {
6261 19 : OGREnvelope3D sEnvelope;
6262 20 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
6263 : {
6264 19 : if (!FastGetExtent3D(i, &sEnvelope))
6265 18 : return false;
6266 : }
6267 1 : return true;
6268 : }
6269 :
6270 177 : return false;
6271 : }
6272 :
6273 : #if defined(__clang__)
6274 : #pragma clang diagnostic pop
6275 : #endif
6276 :
6277 : #endif /* OGARROWLAYER_HPP_INCLUDED */
|