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 1967 : inline OGRArrowLayer::OGRArrowLayer(OGRArrowDataset *poDS,
46 : const char *pszLayerName,
47 1967 : bool bListsAsStringJson)
48 : : m_poArrowDS(poDS), m_bListsAsStringJson(bListsAsStringJson),
49 1967 : m_poMemoryPool(poDS->GetMemoryPool())
50 : {
51 1967 : m_poFeatureDefn = new OGRFeatureDefn(pszLayerName);
52 1967 : m_poFeatureDefn->SetGeomType(wkbNone);
53 1967 : m_poFeatureDefn->Reference();
54 1967 : SetDescription(pszLayerName);
55 1967 : }
56 :
57 : /************************************************************************/
58 : /* ~OGRFeatherLayer() */
59 : /************************************************************************/
60 :
61 1964 : inline OGRArrowLayer::~OGRArrowLayer()
62 : {
63 1964 : if (m_sCachedSchema.release)
64 59 : m_sCachedSchema.release(&m_sCachedSchema);
65 :
66 1964 : CPLDebug("ARROW", "Memory pool: bytes_allocated = %" PRId64,
67 1964 : m_poMemoryPool->bytes_allocated());
68 1964 : CPLDebug("ARROW", "Memory pool: max_memory = %" PRId64,
69 1964 : m_poMemoryPool->max_memory());
70 1964 : m_poFeatureDefn->Release();
71 1964 : }
72 :
73 : /************************************************************************/
74 : /* LoadGDALSchema() */
75 : /************************************************************************/
76 :
77 : inline std::map<std::string, std::unique_ptr<OGRFieldDefn>>
78 1967 : OGRArrowLayer::LoadGDALSchema(const arrow::KeyValueMetadata *kv_metadata)
79 : {
80 : std::map<std::string, std::unique_ptr<OGRFieldDefn>>
81 1967 : oMapFieldNameToGDALSchemaFieldDefn;
82 2010 : if (kv_metadata && kv_metadata->Contains("gdal:schema") &&
83 43 : CPLTestBool(CPLGetConfigOption(
84 2010 : ("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 1967 : return oMapFieldNameToGDALSchemaFieldDefn;
159 : }
160 :
161 : /************************************************************************/
162 : /* LoadGDALMetadata() */
163 : /************************************************************************/
164 :
165 : inline void
166 1419 : OGRArrowLayer::LoadGDALMetadata(const arrow::KeyValueMetadata *kv_metadata)
167 : {
168 1419 : 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 1419 : }
216 :
217 : /************************************************************************/
218 : /* IsIntegerArrowType() */
219 : /************************************************************************/
220 :
221 9048 : inline bool OGRArrowLayer::IsIntegerArrowType(arrow::Type::type typeId)
222 : {
223 8645 : return typeId == arrow::Type::INT8 || typeId == arrow::Type::UINT8 ||
224 7839 : typeId == arrow::Type::INT16 || typeId == arrow::Type::UINT16 ||
225 6621 : typeId == arrow::Type::INT32 || typeId == arrow::Type::UINT32 ||
226 17693 : typeId == arrow::Type::INT64 || typeId == arrow::Type::UINT64;
227 : }
228 :
229 : /************************************************************************/
230 : /* IsHandledListOrMapType() */
231 : /************************************************************************/
232 :
233 9039 : inline bool OGRArrowLayer::IsHandledListOrMapType(
234 : const std::shared_ptr<arrow::DataType> &valueType)
235 : {
236 9039 : const auto itemTypeId = valueType->id();
237 8636 : return itemTypeId == arrow::Type::BOOL || IsIntegerArrowType(itemTypeId) ||
238 5364 : itemTypeId == arrow::Type::HALF_FLOAT ||
239 4961 : itemTypeId == arrow::Type::FLOAT ||
240 4558 : itemTypeId == arrow::Type::DOUBLE ||
241 : #if ARROW_VERSION_MAJOR >= 18
242 4558 : itemTypeId == arrow::Type::DECIMAL32 ||
243 4558 : itemTypeId == arrow::Type::DECIMAL64 ||
244 : #endif
245 3781 : itemTypeId == arrow::Type::DECIMAL128 ||
246 3756 : itemTypeId == arrow::Type::DECIMAL256 ||
247 1772 : itemTypeId == arrow::Type::STRING ||
248 1747 : itemTypeId == arrow::Type::LARGE_STRING ||
249 : #if ARROW_VERSION_MAJOR >= 15
250 1743 : itemTypeId == arrow::Type::STRING_VIEW ||
251 : #endif
252 1742 : itemTypeId == arrow::Type::BINARY ||
253 : #if ARROW_VERSION_MAJOR >= 15
254 1742 : itemTypeId == arrow::Type::BINARY_VIEW ||
255 : #endif
256 1268 : itemTypeId == arrow::Type::STRUCT ||
257 26 : (itemTypeId == arrow::Type::MAP &&
258 13 : IsHandledMapType(
259 27982 : 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 19333 : std::static_pointer_cast<arrow::BaseListType>(valueType)));
265 : }
266 :
267 : /************************************************************************/
268 : /* IsHandledListType() */
269 : /************************************************************************/
270 :
271 1774 : inline bool OGRArrowLayer::IsHandledListType(
272 : const std::shared_ptr<arrow::BaseListType> &listType)
273 : {
274 1774 : return IsHandledListOrMapType(listType->value_type());
275 : }
276 :
277 : /************************************************************************/
278 : /* IsHandledMapType() */
279 : /************************************************************************/
280 :
281 : inline bool
282 7265 : OGRArrowLayer::IsHandledMapType(const std::shared_ptr<arrow::MapType> &mapType)
283 : {
284 7265 : 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 14530 : ) &&
290 14530 : IsHandledListOrMapType(mapType->item_type());
291 : }
292 :
293 : /************************************************************************/
294 : /* GetFieldExtensionName() */
295 : /************************************************************************/
296 :
297 : inline static std::string
298 37545 : GetFieldExtensionName(const std::shared_ptr<arrow::Field> &field,
299 : std::shared_ptr<arrow::DataType> &type,
300 : const char *pszDebugKey)
301 : {
302 37545 : std::string osExtensionName;
303 37545 : 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 37381 : 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 37731 : if (!osExtensionName.empty() &&
319 37731 : 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 37545 : return osExtensionName;
328 : }
329 :
330 : /************************************************************************/
331 : /* MapArrowTypeToOGR() */
332 : /************************************************************************/
333 :
334 36661 : 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 36661 : bool bTypeOK = false;
343 :
344 73322 : std::shared_ptr<arrow::DataType> type(typeIn);
345 : const std::string osExtensionName =
346 36661 : GetFieldExtensionName(field, type, GetDriverUCName().c_str());
347 36661 : 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 1431 : case arrow::Type::INT32:
373 1431 : bTypeOK = true;
374 1431 : eType = OFTInteger;
375 1431 : 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 2855 : 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 2855 : bTypeOK = true;
400 2855 : eType = OFTString;
401 2855 : if (osExtensionName == EXTENSION_NAME_ARROW_JSON)
402 176 : eSubType = OFSTJSON;
403 2855 : 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 12069 : case arrow::Type::LIST:
474 : case arrow::Type::LARGE_LIST:
475 : case arrow::Type::FIXED_SIZE_LIST:
476 : {
477 12069 : bTypeOK = true;
478 24138 : auto listType = std::static_pointer_cast<arrow::BaseListType>(type);
479 12069 : 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 519 : default:
525 : {
526 519 : if (IsHandledListType(listType))
527 : {
528 519 : eType = OFTString;
529 519 : 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 519 : break;
540 : }
541 : }
542 :
543 12069 : if (bTypeOK && m_bListsAsStringJson)
544 : {
545 61 : eType = OFTString;
546 61 : eSubType = OFSTJSON;
547 : }
548 :
549 12069 : break;
550 : }
551 :
552 7252 : case arrow::Type::MAP:
553 : {
554 7252 : bTypeOK = true;
555 14504 : auto mapType = std::static_pointer_cast<arrow::MapType>(type);
556 7252 : if (IsHandledMapType(mapType))
557 : {
558 7252 : eType = OFTString;
559 7252 : 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 7252 : 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 36661 : if (bTypeOK)
603 : {
604 : const auto oIter =
605 36277 : oMapFieldNameToGDALSchemaFieldDefn.find(field->name());
606 36277 : oField.SetType(eType);
607 36277 : 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 36277 : oField.SetSubType(eSubType);
648 36277 : oField.SetNullable(field->nullable());
649 36277 : m_poFeatureDefn->AddFieldDefn(&oField);
650 36277 : m_anMapFieldIndexToArrowColumn.push_back(path);
651 : }
652 :
653 73322 : 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 191240 : static const arrow::Array *GetStorageArray(const arrow::Array *array)
785 : {
786 191240 : 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 191240 : 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 1898 : 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 1898 : const auto &fieldName = field->name();
1043 3796 : std::shared_ptr<arrow::DataType> fieldType = field->type();
1044 1898 : auto fieldTypeId = fieldType->id();
1045 :
1046 1898 : 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 1898 : eGeomTypeOut = wkbUnknown;
1055 :
1056 3741 : if (osEncoding == "WKT" || // As used in Parquet geo metadata
1057 1843 : osEncoding ==
1058 3741 : "ogc.wkt" || // As used in ARROW:extension:name field metadata
1059 1843 : 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 2901 : if (osEncoding == "WKB" || // As used in Parquet geo metadata
1077 1058 : osEncoding ==
1078 2901 : "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 786 : 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 786 : eOGRArrowGeomEncodingOut = OGRArrowGeomEncoding::WKB;
1093 786 : 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 290 : OGRArrowLayer::GetGeometryTypeFromString(const std::string &osType)
1279 : {
1280 290 : OGRwkbGeometryType eGeomType = wkbUnknown;
1281 290 : OGRReadWKTGeometryType(osType.c_str(), &eGeomType);
1282 290 : if (eGeomType == wkbUnknown && !osType.empty())
1283 : {
1284 0 : CPLDebug("ARROW", "Unknown geometry type: %s", osType.c_str());
1285 : }
1286 290 : 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 27816 : void AddToContainer(Container &oContainer, const arrow::Array *array,
1298 : const size_t nIdx)
1299 : {
1300 27816 : 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 4341 : case arrow::Type::DOUBLE:
1373 : {
1374 4341 : oContainer.Add(
1375 4313 : static_cast<const arrow::DoubleArray *>(array)->Value(nIdx));
1376 4341 : 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 3702 : case arrow::Type::STRING:
1414 : {
1415 3702 : oContainer.Add(
1416 : static_cast<const arrow::StringArray *>(array)->GetString(
1417 : nIdx));
1418 3702 : 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 6578 : 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 6578 : oContainer.Add(GetObjectAsJSON(array, nIdx));
1443 6578 : 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 27816 : }
1511 :
1512 : /************************************************************************/
1513 : /* AddToArray() */
1514 : /************************************************************************/
1515 :
1516 6744 : static void AddToArray(CPLJSONArray &oArray, const arrow::Array *array,
1517 : const size_t nIdx)
1518 : {
1519 6744 : AddToContainer(oArray, array, nIdx);
1520 6744 : }
1521 :
1522 : /************************************************************************/
1523 : /* GetListAsJSON() */
1524 : /************************************************************************/
1525 :
1526 : template <class ArrowType>
1527 5197 : static CPLJSONArray GetListAsJSON(const ArrowType *array,
1528 : const size_t nIdxInArray)
1529 : {
1530 10394 : const auto values = array->values();
1531 5197 : const auto nIdxStart = array->value_offset(nIdxInArray);
1532 5197 : const auto nCount = array->value_length(nIdxInArray);
1533 5197 : CPLJSONArray oArray;
1534 14365 : for (auto k = decltype(nCount){0}; k < nCount; k++)
1535 : {
1536 9168 : if (values->IsNull(nIdxStart + k))
1537 2424 : oArray.AddNull();
1538 : else
1539 6744 : AddToArray(oArray, values.get(),
1540 6744 : static_cast<size_t>(nIdxStart + k));
1541 : }
1542 10394 : 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 21072 : ContainerAdapter(CPLJSONObject &oDictIn, const std::string &osKeyIn)
1557 21072 : : m_oDict(oDictIn), m_osKey(osKeyIn)
1558 : {
1559 21072 : }
1560 :
1561 21072 : template <class T> void Add(const T &v)
1562 : {
1563 21072 : m_oDict.Add(m_osKey, v);
1564 21072 : }
1565 : };
1566 : } // namespace
1567 :
1568 21072 : static void AddToDict(CPLJSONObject &oDict, const std::string &osKey,
1569 : const arrow::Array *array, const size_t nIdx)
1570 : {
1571 42144 : ContainerAdapter dictAdapter(oDict, osKey);
1572 21072 : AddToContainer(dictAdapter, array, nIdx);
1573 21072 : }
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 3615 : static CPLJSONObject GetStructureAsJSON(const arrow::Array *array,
1626 : const size_t nIdxInArray)
1627 : {
1628 3615 : CPLJSONObject oRoot;
1629 3615 : const auto structArray = static_cast<const arrow::StructArray *>(array);
1630 7230 : const auto structArrayType = structArray->type();
1631 14543 : for (int i = 0; i < structArrayType->num_fields(); ++i)
1632 : {
1633 21856 : const auto field = structArray->field(i);
1634 10928 : if (!field->IsNull(nIdxInArray))
1635 : {
1636 7265 : AddToDict(oRoot, structArrayType->field(i)->name(), field.get(),
1637 : nIdxInArray);
1638 : }
1639 : else
1640 3663 : oRoot.AddNull(structArrayType->field(i)->name());
1641 : }
1642 :
1643 7230 : return oRoot;
1644 : }
1645 :
1646 : /************************************************************************/
1647 : /* GetObjectAsJSON() */
1648 : /************************************************************************/
1649 :
1650 6578 : static CPLJSONObject GetObjectAsJSON(const arrow::Array *array,
1651 : const size_t nIdxInArray)
1652 : {
1653 6578 : 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 3615 : case arrow::Type::STRUCT:
1668 3615 : 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 50307 : static void ReadList(OGRFeature *poFeature, int i, int64_t nIdxInArray,
1734 : const ArrayType *array, arrow::Type::type valueTypeId)
1735 : {
1736 50307 : 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 2092 : 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 2092 : poFeature->SetField(
2017 : i, GetListAsJSON(array, static_cast<size_t>(nIdxInArray))
2018 : .Format(CPLJSONObject::PrettyFormat::Plain)
2019 : .c_str());
2020 2092 : 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 50307 : }
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 10900 : inline OGRFeature *OGRArrowLayer::ReadFeature(
2222 : int64_t nIdxInBatch,
2223 : const std::vector<std::shared_ptr<arrow::Array>> &poColumnArrays) const
2224 : {
2225 10900 : OGRFeature *poFeature = new OGRFeature(m_poFeatureDefn);
2226 :
2227 10900 : 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 151425 : [this, poFeature, nIdxInBatch](const auto *array, int iField)
2252 : {
2253 : const auto listType =
2254 50475 : static_cast<const arrow::ListType *>(array->data()->type.get());
2255 :
2256 50475 : 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 50307 : ::ReadList(poFeature, iField, nIdxInBatch, array,
2266 50307 : listType->value_field()->type()->id());
2267 : }
2268 61375 : };
2269 :
2270 10900 : const int nFieldCount = m_poFeatureDefn->GetFieldCount();
2271 179902 : for (int i = 0; i < nFieldCount; ++i)
2272 : {
2273 : int iCol;
2274 169002 : if (m_bIgnoredFields)
2275 : {
2276 17055 : iCol = m_anMapFieldIndexToArrayIndex[i];
2277 17055 : if (iCol < 0)
2278 5434 : continue;
2279 : }
2280 : else
2281 : {
2282 151947 : iCol = m_anMapFieldIndexToArrowColumn[i][0];
2283 : }
2284 :
2285 163568 : const arrow::Array *array = GetStorageArray(poColumnArrays[iCol].get());
2286 163568 : if (array->IsNull(nIdxInBatch))
2287 : {
2288 17452 : poFeature->SetFieldNull(i);
2289 17452 : continue;
2290 : }
2291 :
2292 146116 : int j = 1;
2293 146116 : bool bSkipToNextField = false;
2294 156912 : if (array->type_id() == arrow::Type::STRUCT &&
2295 156912 : 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 160504 : while (array->type_id() == arrow::Type::STRUCT)
2328 : {
2329 14419 : const auto castArray =
2330 : static_cast<const arrow::StructArray *>(array);
2331 14419 : const auto &subArrays = castArray->fields();
2332 14419 : CPLAssert(j < static_cast<int>(
2333 : m_anMapFieldIndexToArrowColumn[i].size()));
2334 14419 : const int iArrowSubcol = m_anMapFieldIndexToArrowColumn[i][j];
2335 14419 : j++;
2336 14419 : CPLAssert(iArrowSubcol < static_cast<int>(subArrays.size()));
2337 14419 : array = GetStorageArray(subArrays[iArrowSubcol].get());
2338 14419 : if (array->IsNull(nIdxInBatch))
2339 : {
2340 12 : poFeature->SetFieldNull(i);
2341 12 : bSkipToNextField = true;
2342 12 : break;
2343 : }
2344 : }
2345 : }
2346 146097 : if (bSkipToNextField)
2347 12 : continue;
2348 :
2349 146085 : 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 146085 : 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 8566 : case arrow::Type::INT32:
2417 : {
2418 8566 : const auto castArray =
2419 : static_cast<const arrow::Int32Array *>(array);
2420 8566 : poFeature->SetFieldSameTypeUnsafe(
2421 : i, castArray->Value(nIdxInBatch));
2422 8566 : 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 7644 : case arrow::Type::STRING:
2468 : {
2469 7644 : const auto castArray =
2470 : static_cast<const arrow::StringArray *>(array);
2471 7644 : int out_length = 0;
2472 : const uint8_t *data =
2473 7644 : castArray->GetValue(nIdxInBatch, &out_length);
2474 : char *pszString =
2475 7644 : static_cast<char *>(CPLMalloc(out_length + 1));
2476 7644 : memcpy(pszString, data, out_length);
2477 7644 : pszString[out_length] = 0;
2478 7644 : poFeature->SetFieldSameTypeUnsafe(i, pszString);
2479 7644 : 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 28996 : case arrow::Type::LIST:
2637 : {
2638 28996 : ReadList(static_cast<const arrow::ListArray *>(array), i);
2639 28996 : 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 10900 : const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
2727 21448 : for (int i = 0; i < nGeomFieldCount; ++i)
2728 : {
2729 : int iCol;
2730 10548 : if (m_bIgnoredFields)
2731 : {
2732 1514 : iCol = m_anMapGeomFieldIndexToArrayIndex[i];
2733 1514 : if (iCol < 0)
2734 55 : 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 8810 : else if (wkbFlatten(poGeometry->getGeometryType()) == wkbPolygon &&
2753 1022 : 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 10900 : 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 8892 : inline void OGRArrowLayer::ResetReading()
3412 : {
3413 8892 : m_bEOF = false;
3414 8892 : m_nFeatureIdx = 0;
3415 8892 : m_nIdxInBatch = 0;
3416 8892 : m_poReadFeatureTmpArray.reset();
3417 8892 : if (m_iRecordBatch != 0)
3418 : {
3419 7923 : m_iRecordBatch = -1;
3420 7923 : m_poBatch.reset();
3421 7923 : m_poBatchColumns.clear();
3422 : }
3423 8892 : }
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 577 : inline void OGRArrowLayer::ComputeConstraintsArrayIdx()
3563 : {
3564 784 : 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 4345 : OGRArrowLayer::SetBatch(const std::shared_ptr<arrow::RecordBatch> &poBatch)
4174 : {
4175 4345 : m_poBatch = poBatch;
4176 4345 : m_poBatchColumns.clear();
4177 4345 : m_poArrayWKB = nullptr;
4178 4345 : m_poArrayWKBLarge = nullptr;
4179 4345 : m_poArrayBBOX = nullptr;
4180 4345 : m_poArrayXMinDouble = nullptr;
4181 4345 : m_poArrayYMinDouble = nullptr;
4182 4345 : m_poArrayXMaxDouble = nullptr;
4183 4345 : m_poArrayYMaxDouble = nullptr;
4184 4345 : m_poArrayXMinFloat = nullptr;
4185 4345 : m_poArrayYMinFloat = nullptr;
4186 4345 : m_poArrayXMaxFloat = nullptr;
4187 4345 : m_poArrayYMaxFloat = nullptr;
4188 :
4189 4345 : if (m_poBatch)
4190 : {
4191 2997 : m_poBatchColumns = m_poBatch->columns();
4192 2997 : if (!SanityCheckOfSetBatch())
4193 : {
4194 0 : m_poBatch.reset();
4195 0 : m_poBatchColumns.clear();
4196 : }
4197 : }
4198 :
4199 4345 : if (m_poBatch && m_poFilterGeom && !m_bBaseArrowIgnoreSpatialFilterRect)
4200 : {
4201 : int iCol;
4202 672 : if (m_bIgnoredFields)
4203 : {
4204 204 : iCol = m_anMapGeomFieldIndexToArrayIndex[m_iGeomFieldFilter];
4205 : }
4206 : else
4207 : {
4208 468 : iCol = m_anMapGeomFieldIndexToArrowColumn[m_iGeomFieldFilter];
4209 : }
4210 1344 : if (iCol >= 0 &&
4211 672 : m_aeGeomEncoding[m_iGeomFieldFilter] == OGRArrowGeomEncoding::WKB)
4212 : {
4213 : const arrow::Array *poArrayWKB =
4214 117 : GetStorageArray(m_poBatchColumns[iCol].get());
4215 117 : if (poArrayWKB->type_id() == arrow::Type::BINARY)
4216 117 : m_poArrayWKB =
4217 : static_cast<const arrow::BinaryArray *>(poArrayWKB);
4218 : else
4219 : {
4220 0 : CPLAssert(poArrayWKB->type_id() == arrow::Type::LARGE_BINARY);
4221 0 : m_poArrayWKBLarge =
4222 : static_cast<const arrow::LargeBinaryArray *>(poArrayWKB);
4223 : }
4224 : }
4225 :
4226 1344 : if (iCol >= 0 &&
4227 672 : CPLTestBool(CPLGetConfigOption(
4228 1344 : ("OGR_" + GetDriverUCName() + "_USE_BBOX").c_str(), "YES")))
4229 : {
4230 : const auto oIter =
4231 672 : m_oMapGeomFieldIndexToGeomColBBOX.find(m_iGeomFieldFilter);
4232 672 : if (oIter != m_oMapGeomFieldIndexToGeomColBBOX.end())
4233 : {
4234 422 : const int idx = m_bIgnoredFields ? oIter->second.iArrayIdx
4235 155 : : oIter->second.iArrowCol;
4236 267 : if (idx >= 0)
4237 : {
4238 211 : CPLAssert(static_cast<size_t>(idx) <
4239 : m_poBatchColumns.size());
4240 211 : m_poArrayBBOX = m_poBatchColumns[idx].get();
4241 211 : CPLAssert(m_poArrayBBOX->type_id() == arrow::Type::STRUCT);
4242 211 : const auto castArray =
4243 : static_cast<const arrow::StructArray *>(m_poArrayBBOX);
4244 211 : const auto &subArrays = castArray->fields();
4245 211 : CPLAssert(
4246 : static_cast<size_t>(oIter->second.iArrowSubfieldXMin) <
4247 : subArrays.size());
4248 : const auto xminArray =
4249 211 : subArrays[oIter->second.iArrowSubfieldXMin].get();
4250 211 : CPLAssert(
4251 : static_cast<size_t>(oIter->second.iArrowSubfieldYMin) <
4252 : subArrays.size());
4253 : const auto yminArray =
4254 211 : subArrays[oIter->second.iArrowSubfieldYMin].get();
4255 211 : CPLAssert(
4256 : static_cast<size_t>(oIter->second.iArrowSubfieldXMax) <
4257 : subArrays.size());
4258 : const auto xmaxArray =
4259 211 : subArrays[oIter->second.iArrowSubfieldXMax].get();
4260 211 : CPLAssert(
4261 : static_cast<size_t>(oIter->second.iArrowSubfieldYMax) <
4262 : subArrays.size());
4263 : const auto ymaxArray =
4264 211 : subArrays[oIter->second.iArrowSubfieldYMax].get();
4265 211 : if (oIter->second.bIsFloat)
4266 : {
4267 210 : CPLAssert(xminArray->type_id() == arrow::Type::FLOAT);
4268 210 : m_poArrayXMinFloat =
4269 : static_cast<const arrow::FloatArray *>(xminArray);
4270 210 : CPLAssert(yminArray->type_id() == arrow::Type::FLOAT);
4271 210 : m_poArrayYMinFloat =
4272 : static_cast<const arrow::FloatArray *>(yminArray);
4273 210 : CPLAssert(xmaxArray->type_id() == arrow::Type::FLOAT);
4274 210 : m_poArrayXMaxFloat =
4275 : static_cast<const arrow::FloatArray *>(xmaxArray);
4276 210 : CPLAssert(ymaxArray->type_id() == arrow::Type::FLOAT);
4277 210 : m_poArrayYMaxFloat =
4278 : static_cast<const arrow::FloatArray *>(ymaxArray);
4279 : }
4280 : else
4281 : {
4282 1 : CPLAssert(xminArray->type_id() == arrow::Type::DOUBLE);
4283 1 : m_poArrayXMinDouble =
4284 : static_cast<const arrow::DoubleArray *>(xminArray);
4285 1 : CPLAssert(yminArray->type_id() == arrow::Type::DOUBLE);
4286 1 : m_poArrayYMinDouble =
4287 : static_cast<const arrow::DoubleArray *>(yminArray);
4288 1 : CPLAssert(xmaxArray->type_id() == arrow::Type::DOUBLE);
4289 1 : m_poArrayXMaxDouble =
4290 : static_cast<const arrow::DoubleArray *>(xmaxArray);
4291 1 : CPLAssert(ymaxArray->type_id() == arrow::Type::DOUBLE);
4292 1 : m_poArrayYMaxDouble =
4293 : static_cast<const arrow::DoubleArray *>(ymaxArray);
4294 : }
4295 : }
4296 : }
4297 : }
4298 : }
4299 4345 : }
4300 :
4301 : /************************************************************************/
4302 : /* SanityCheckOfSetBatch() */
4303 : /************************************************************************/
4304 :
4305 2997 : inline bool OGRArrowLayer::SanityCheckOfSetBatch() const
4306 : {
4307 2997 : CPLAssert(m_poBatch);
4308 :
4309 : // Sanity checks
4310 : const auto nExpectedBatchColumns =
4311 2997 : (m_bIgnoredFields ? m_nExpectedBatchColumns : m_poSchema->num_fields());
4312 2997 : if (m_poBatch->num_columns() != nExpectedBatchColumns)
4313 : {
4314 0 : CPLError(
4315 : CE_Failure, CPLE_AppDefined,
4316 : "BUG! m_poBatch->num_columns() (=%d) != nExpectedBatchColumns(=%d)",
4317 0 : static_cast<int>(m_poBatch->num_columns()),
4318 : static_cast<int>(nExpectedBatchColumns));
4319 0 : return false;
4320 : }
4321 :
4322 : #ifdef DEBUG
4323 :
4324 2997 : const auto &poColumns = m_poBatch->columns();
4325 :
4326 2997 : const auto &fields = m_poSchema->fields();
4327 :
4328 96107 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i)
4329 : {
4330 : int iCol;
4331 93110 : if (m_bIgnoredFields)
4332 : {
4333 15276 : iCol = m_anMapFieldIndexToArrayIndex[i];
4334 15276 : if (iCol < 0)
4335 3894 : continue;
4336 : }
4337 : else
4338 : {
4339 77834 : iCol = m_anMapFieldIndexToArrowColumn[i][0];
4340 : }
4341 89216 : CPL_IGNORE_RET_VAL(iCol); // to make cppcheck happy
4342 :
4343 89216 : CPLAssert(iCol < static_cast<int>(poColumns.size()));
4344 89216 : CPLAssert(fields[m_anMapFieldIndexToArrowColumn[i][0]]->type()->id() ==
4345 : poColumns[iCol]->type_id());
4346 : }
4347 :
4348 5882 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
4349 : {
4350 : int iCol;
4351 2885 : if (m_bIgnoredFields)
4352 : {
4353 654 : iCol = m_anMapGeomFieldIndexToArrayIndex[i];
4354 654 : if (iCol < 0)
4355 31 : continue;
4356 : }
4357 : else
4358 : {
4359 2231 : iCol = m_anMapGeomFieldIndexToArrowColumn[i];
4360 : }
4361 2854 : CPL_IGNORE_RET_VAL(iCol); // to make cppcheck happy
4362 :
4363 2854 : CPLAssert(iCol < static_cast<int>(poColumns.size()));
4364 2854 : CPLAssert(fields[m_anMapGeomFieldIndexToArrowColumn[i]]->type()->id() ==
4365 : poColumns[iCol]->type_id());
4366 : }
4367 : #endif
4368 :
4369 2997 : return true;
4370 : }
4371 :
4372 : /************************************************************************/
4373 : /* GetNextRawFeature() */
4374 : /************************************************************************/
4375 :
4376 13281 : inline OGRFeature *OGRArrowLayer::GetNextRawFeature()
4377 : {
4378 13281 : if (m_bEOF || !m_bSpatialFilterIntersectsLayerExtent)
4379 533 : return nullptr;
4380 :
4381 12748 : if (m_poBatch == nullptr || m_nIdxInBatch == m_poBatch->num_rows())
4382 : {
4383 4029 : m_bEOF = !ReadNextBatch();
4384 4029 : if (m_bEOF)
4385 1686 : return nullptr;
4386 : }
4387 :
4388 : // Evaluate spatial filter by computing the bounding box of each geometry
4389 : // but without creating a OGRGeometry
4390 11062 : if (m_poFilterGeom && !m_bBaseArrowIgnoreSpatialFilterRect)
4391 : {
4392 : int iCol;
4393 1167 : if (m_bIgnoredFields)
4394 : {
4395 276 : iCol = m_anMapGeomFieldIndexToArrayIndex[m_iGeomFieldFilter];
4396 : }
4397 : else
4398 : {
4399 891 : iCol = m_anMapGeomFieldIndexToArrowColumn[m_iGeomFieldFilter];
4400 : }
4401 :
4402 1167 : if (iCol >= 0 && (m_poArrayXMinFloat || m_poArrayXMinDouble))
4403 : {
4404 463 : OGREnvelope sEnvelopeSkipToNextFeatureDueToBBOX;
4405 : const auto IntersectsBBOX =
4406 3459 : [this, &sEnvelopeSkipToNextFeatureDueToBBOX]()
4407 : {
4408 987 : if (m_poArrayXMinFloat &&
4409 493 : !m_poArrayXMinFloat->IsNull(m_nIdxInBatch))
4410 : {
4411 493 : sEnvelopeSkipToNextFeatureDueToBBOX.MinX =
4412 493 : m_poArrayXMinFloat->Value(m_nIdxInBatch);
4413 493 : sEnvelopeSkipToNextFeatureDueToBBOX.MinY =
4414 493 : m_poArrayYMinFloat->Value(m_nIdxInBatch);
4415 493 : sEnvelopeSkipToNextFeatureDueToBBOX.MaxX =
4416 493 : m_poArrayXMaxFloat->Value(m_nIdxInBatch);
4417 493 : sEnvelopeSkipToNextFeatureDueToBBOX.MaxY =
4418 493 : m_poArrayYMaxFloat->Value(m_nIdxInBatch);
4419 493 : if (m_sFilterEnvelope.Intersects(
4420 493 : sEnvelopeSkipToNextFeatureDueToBBOX))
4421 : {
4422 450 : return true;
4423 : }
4424 : }
4425 2 : else if (m_poArrayXMinDouble &&
4426 1 : !m_poArrayXMinDouble->IsNull(m_nIdxInBatch))
4427 : {
4428 1 : sEnvelopeSkipToNextFeatureDueToBBOX.MinX =
4429 1 : m_poArrayXMinDouble->Value(m_nIdxInBatch);
4430 1 : sEnvelopeSkipToNextFeatureDueToBBOX.MinY =
4431 1 : m_poArrayYMinDouble->Value(m_nIdxInBatch);
4432 1 : sEnvelopeSkipToNextFeatureDueToBBOX.MaxX =
4433 1 : m_poArrayXMaxDouble->Value(m_nIdxInBatch);
4434 1 : sEnvelopeSkipToNextFeatureDueToBBOX.MaxY =
4435 1 : m_poArrayYMaxDouble->Value(m_nIdxInBatch);
4436 1 : if (m_sFilterEnvelope.Intersects(
4437 1 : sEnvelopeSkipToNextFeatureDueToBBOX))
4438 : {
4439 1 : return true;
4440 : }
4441 : }
4442 43 : return false;
4443 463 : };
4444 :
4445 : while (true)
4446 : {
4447 1321 : if (!m_poArrayBBOX->IsNull(m_nIdxInBatch) && IntersectsBBOX() &&
4448 451 : (m_asAttributeFilterConstraints.empty() ||
4449 2 : !SkipToNextFeatureDueToAttributeFilter()))
4450 : {
4451 450 : break;
4452 : }
4453 :
4454 418 : IncrFeatureIdx();
4455 418 : m_nIdxInBatch++;
4456 418 : if (m_nIdxInBatch == m_poBatch->num_rows())
4457 : {
4458 22 : m_bEOF = !ReadNextBatch();
4459 22 : if (m_bEOF)
4460 13 : return nullptr;
4461 : }
4462 450 : }
4463 : }
4464 704 : else if (iCol >= 0 && m_aeGeomEncoding[m_iGeomFieldFilter] ==
4465 : OGRArrowGeomEncoding::WKB)
4466 : {
4467 131 : CPLAssert(m_poArrayWKB || m_poArrayWKBLarge);
4468 131 : OGREnvelope sEnvelope;
4469 :
4470 : while (true)
4471 : {
4472 220 : bool bMatchBBOX = false;
4473 417 : if ((m_poArrayWKB && m_poArrayWKB->IsNull(m_nIdxInBatch)) ||
4474 197 : (m_poArrayWKBLarge &&
4475 0 : m_poArrayWKBLarge->IsNull(m_nIdxInBatch)))
4476 : {
4477 : // nothing to do
4478 : }
4479 : else
4480 : {
4481 197 : if (m_poArrayWKB)
4482 : {
4483 197 : int out_length = 0;
4484 : const uint8_t *data =
4485 197 : m_poArrayWKB->GetValue(m_nIdxInBatch, &out_length);
4486 394 : if (OGRWKBGetBoundingBox(data, out_length, sEnvelope) &&
4487 197 : m_sFilterEnvelope.Intersects(sEnvelope))
4488 : {
4489 115 : bMatchBBOX = true;
4490 : }
4491 : }
4492 : else
4493 : {
4494 0 : CPLAssert(m_poArrayWKBLarge);
4495 0 : int64_t out_length64 = 0;
4496 0 : const uint8_t *data = m_poArrayWKBLarge->GetValue(
4497 : m_nIdxInBatch, &out_length64);
4498 0 : if (out_length64 < INT_MAX &&
4499 0 : OGRWKBGetBoundingBox(data,
4500 : static_cast<int>(out_length64),
4501 0 : sEnvelope) &&
4502 0 : m_sFilterEnvelope.Intersects(sEnvelope))
4503 : {
4504 0 : bMatchBBOX = true;
4505 : }
4506 : }
4507 : }
4508 221 : if (bMatchBBOX && (m_asAttributeFilterConstraints.empty() ||
4509 1 : !SkipToNextFeatureDueToAttributeFilter()))
4510 : {
4511 115 : break;
4512 : }
4513 :
4514 105 : IncrFeatureIdx();
4515 105 : m_nIdxInBatch++;
4516 105 : if (m_nIdxInBatch == m_poBatch->num_rows())
4517 : {
4518 25 : m_bEOF = !ReadNextBatch();
4519 25 : if (m_bEOF)
4520 16 : return nullptr;
4521 : }
4522 89 : }
4523 : }
4524 1146 : else if (iCol >= 0 &&
4525 573 : m_aeGeomEncoding[m_iGeomFieldFilter] ==
4526 : OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOLYGON)
4527 : {
4528 : const auto poGeomFieldDefn =
4529 10 : m_poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
4530 10 : const auto eGeomType = poGeomFieldDefn->GetType();
4531 10 : const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
4532 10 : const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
4533 10 : const int nDim = 2 + (bHasZ ? 1 : 0) + (bHasM ? 1 : 0);
4534 :
4535 : bool bReturnFeature;
4536 0 : do
4537 : {
4538 10 : bReturnFeature = false;
4539 10 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
4540 10 : CPLAssert(array->type_id() == arrow::Type::LIST);
4541 10 : auto listOfPartsArray =
4542 : static_cast<const arrow::ListArray *>(array);
4543 10 : CPLAssert(listOfPartsArray->values()->type_id() ==
4544 : arrow::Type::LIST);
4545 : auto listOfPartsValues =
4546 : std::static_pointer_cast<arrow::ListArray>(
4547 10 : listOfPartsArray->values());
4548 10 : CPLAssert(listOfPartsValues->values()->type_id() ==
4549 : arrow::Type::LIST);
4550 : auto listOfRingsValues =
4551 : std::static_pointer_cast<arrow::ListArray>(
4552 10 : listOfPartsValues->values());
4553 10 : CPLAssert(listOfRingsValues->values()->type_id() ==
4554 : arrow::Type::FIXED_SIZE_LIST);
4555 : auto listOfPointsValues =
4556 : std::static_pointer_cast<arrow::FixedSizeListArray>(
4557 10 : listOfRingsValues->values());
4558 10 : CPLAssert(listOfPointsValues->values()->type_id() ==
4559 : arrow::Type::DOUBLE);
4560 : auto pointValues = std::static_pointer_cast<arrow::DoubleArray>(
4561 10 : listOfPointsValues->values());
4562 :
4563 : while (true)
4564 : {
4565 18 : bool bMatchBBOX = false;
4566 18 : if (!listOfPartsArray->IsNull(m_nIdxInBatch))
4567 : {
4568 14 : OGREnvelope sEnvelope;
4569 : const auto nParts =
4570 14 : listOfPartsArray->value_length(m_nIdxInBatch);
4571 : const auto nPartOffset =
4572 14 : listOfPartsArray->value_offset(m_nIdxInBatch);
4573 28 : for (auto j = decltype(nParts){0}; j < nParts; j++)
4574 : {
4575 14 : const auto nRings = listOfPartsValues->value_length(
4576 14 : nPartOffset + j);
4577 : const auto nRingOffset =
4578 14 : listOfPartsValues->value_offset(nPartOffset +
4579 : j);
4580 14 : if (nRings >= 1)
4581 : {
4582 : const auto nPoints =
4583 14 : listOfRingsValues->value_length(
4584 : nRingOffset);
4585 : const auto nPointOffset =
4586 14 : listOfRingsValues->value_offset(
4587 : nRingOffset) *
4588 14 : nDim;
4589 : const double *padfRawValue =
4590 14 : pointValues->raw_values() + nPointOffset;
4591 73 : for (auto l = decltype(nPoints){0}; l < nPoints;
4592 : ++l)
4593 : {
4594 59 : sEnvelope.Merge(padfRawValue[nDim * l],
4595 59 : padfRawValue[nDim * l + 1]);
4596 : }
4597 : // for bounding box, only the first ring matters
4598 : }
4599 : }
4600 :
4601 24 : if (nParts != 0 &&
4602 10 : m_sFilterEnvelope.Intersects(sEnvelope))
4603 : {
4604 10 : bMatchBBOX = true;
4605 : }
4606 : }
4607 28 : if (bMatchBBOX &&
4608 10 : (m_asAttributeFilterConstraints.empty() ||
4609 0 : !SkipToNextFeatureDueToAttributeFilter()))
4610 : {
4611 10 : bReturnFeature = true;
4612 10 : break;
4613 : }
4614 :
4615 8 : IncrFeatureIdx();
4616 8 : m_nIdxInBatch++;
4617 8 : if (m_nIdxInBatch == m_poBatch->num_rows())
4618 : {
4619 0 : m_bEOF = !ReadNextBatch();
4620 0 : if (m_bEOF)
4621 0 : return nullptr;
4622 0 : break;
4623 : }
4624 8 : }
4625 10 : } while (!bReturnFeature);
4626 : }
4627 563 : else if (iCol >= 0 && m_aeGeomEncoding[m_iGeomFieldFilter] ==
4628 : OGRArrowGeomEncoding::GEOARROW_STRUCT_POINT)
4629 : {
4630 : bool bReturnFeature;
4631 10 : do
4632 : {
4633 44 : bReturnFeature = false;
4634 44 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
4635 44 : CPLAssert(array->type_id() == arrow::Type::STRUCT);
4636 44 : auto pointValues =
4637 : static_cast<const arrow::StructArray *>(array);
4638 44 : const auto &fields = pointValues->fields();
4639 44 : const auto &fieldX = fields[0];
4640 44 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
4641 : const auto fieldXDouble =
4642 44 : static_cast<arrow::DoubleArray *>(fieldX.get());
4643 44 : const auto &fieldY = fields[1];
4644 44 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
4645 : const auto fieldYDouble =
4646 44 : static_cast<arrow::DoubleArray *>(fieldY.get());
4647 :
4648 : while (true)
4649 : {
4650 98 : bool bMatchBBOX = false;
4651 98 : if (!array->IsNull(m_nIdxInBatch))
4652 : {
4653 76 : const double dfX = fieldXDouble->Value(m_nIdxInBatch);
4654 76 : const double dfY = fieldYDouble->Value(m_nIdxInBatch);
4655 76 : if (dfX >= m_sFilterEnvelope.MinX &&
4656 46 : dfY >= m_sFilterEnvelope.MinY &&
4657 46 : dfX <= m_sFilterEnvelope.MaxX &&
4658 30 : dfY <= m_sFilterEnvelope.MaxY)
4659 : {
4660 30 : bMatchBBOX = true;
4661 : }
4662 : }
4663 128 : if (bMatchBBOX &&
4664 30 : (m_asAttributeFilterConstraints.empty() ||
4665 0 : !SkipToNextFeatureDueToAttributeFilter()))
4666 : {
4667 30 : bReturnFeature = true;
4668 30 : break;
4669 : }
4670 :
4671 68 : IncrFeatureIdx();
4672 68 : m_nIdxInBatch++;
4673 68 : if (m_nIdxInBatch == m_poBatch->num_rows())
4674 : {
4675 14 : m_bEOF = !ReadNextBatch();
4676 14 : if (m_bEOF)
4677 4 : return nullptr;
4678 10 : break;
4679 : }
4680 54 : }
4681 40 : } while (!bReturnFeature);
4682 : }
4683 1058 : else if (iCol >= 0 &&
4684 529 : m_aeGeomEncoding[m_iGeomFieldFilter] ==
4685 : OGRArrowGeomEncoding::GEOARROW_STRUCT_LINESTRING)
4686 : {
4687 : bool bReturnFeature;
4688 0 : do
4689 : {
4690 64 : bReturnFeature = false;
4691 64 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
4692 64 : CPLAssert(array->type_id() == arrow::Type::LIST);
4693 64 : const auto listArray =
4694 : static_cast<const arrow::ListArray *>(array);
4695 64 : CPLAssert(listArray->values()->type_id() ==
4696 : arrow::Type::STRUCT);
4697 : auto pointValues = std::static_pointer_cast<arrow::StructArray>(
4698 64 : listArray->values());
4699 64 : const auto &fields = pointValues->fields();
4700 64 : const auto &fieldX = fields[0];
4701 64 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
4702 : const auto fieldXDouble =
4703 64 : static_cast<arrow::DoubleArray *>(fieldX.get());
4704 64 : const auto &fieldY = fields[1];
4705 64 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
4706 : const auto fieldYDouble =
4707 64 : static_cast<arrow::DoubleArray *>(fieldY.get());
4708 :
4709 : while (true)
4710 : {
4711 160 : bool bMatchBBOX = false;
4712 160 : if (!listArray->IsNull(m_nIdxInBatch))
4713 : {
4714 120 : OGREnvelope sEnvelope;
4715 : const auto nPoints =
4716 120 : listArray->value_length(m_nIdxInBatch);
4717 : const auto nPointOffset =
4718 120 : listArray->value_offset(m_nIdxInBatch);
4719 120 : if (nPoints > 0)
4720 : {
4721 : const double *padfRawXValue =
4722 80 : fieldXDouble->raw_values() + nPointOffset;
4723 : const double *padfRawYValue =
4724 80 : fieldYDouble->raw_values() + nPointOffset;
4725 240 : for (auto l = decltype(nPoints){0}; l < nPoints;
4726 : ++l)
4727 : {
4728 160 : sEnvelope.Merge(padfRawXValue[l],
4729 160 : padfRawYValue[l]);
4730 : }
4731 80 : if (m_sFilterEnvelope.Intersects(sEnvelope))
4732 : {
4733 48 : bMatchBBOX = true;
4734 : }
4735 : }
4736 : }
4737 208 : if (bMatchBBOX &&
4738 48 : (m_asAttributeFilterConstraints.empty() ||
4739 0 : !SkipToNextFeatureDueToAttributeFilter()))
4740 : {
4741 48 : bReturnFeature = true;
4742 48 : break;
4743 : }
4744 :
4745 112 : IncrFeatureIdx();
4746 112 : m_nIdxInBatch++;
4747 112 : if (m_nIdxInBatch == m_poBatch->num_rows())
4748 : {
4749 16 : m_bEOF = !ReadNextBatch();
4750 16 : if (m_bEOF)
4751 16 : return nullptr;
4752 0 : break;
4753 : }
4754 96 : }
4755 48 : } while (!bReturnFeature);
4756 : }
4757 465 : else if (iCol >= 0 && m_aeGeomEncoding[m_iGeomFieldFilter] ==
4758 : OGRArrowGeomEncoding::GEOARROW_STRUCT_POLYGON)
4759 : {
4760 : bool bReturnFeature;
4761 0 : do
4762 : {
4763 96 : bReturnFeature = false;
4764 96 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
4765 96 : CPLAssert(array->type_id() == arrow::Type::LIST);
4766 96 : const auto listOfRingsArray =
4767 : static_cast<const arrow::ListArray *>(array);
4768 96 : CPLAssert(listOfRingsArray->values()->type_id() ==
4769 : arrow::Type::LIST);
4770 : const auto listOfRingsValues =
4771 : std::static_pointer_cast<arrow::ListArray>(
4772 96 : listOfRingsArray->values());
4773 96 : CPLAssert(listOfRingsValues->values()->type_id() ==
4774 : arrow::Type::STRUCT);
4775 : auto pointValues = std::static_pointer_cast<arrow::StructArray>(
4776 96 : listOfRingsValues->values());
4777 96 : const auto &fields = pointValues->fields();
4778 96 : const auto &fieldX = fields[0];
4779 96 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
4780 : const auto fieldXDouble =
4781 96 : static_cast<arrow::DoubleArray *>(fieldX.get());
4782 96 : const auto &fieldY = fields[1];
4783 96 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
4784 : const auto fieldYDouble =
4785 96 : static_cast<arrow::DoubleArray *>(fieldY.get());
4786 :
4787 : while (true)
4788 : {
4789 240 : bool bMatchBBOX = false;
4790 240 : if (!listOfRingsArray->IsNull(m_nIdxInBatch))
4791 : {
4792 184 : OGREnvelope sEnvelope;
4793 : const auto nRings =
4794 184 : listOfRingsArray->value_length(m_nIdxInBatch);
4795 : const auto nRingOffset =
4796 184 : listOfRingsArray->value_offset(m_nIdxInBatch);
4797 184 : if (nRings >= 1)
4798 : {
4799 : const auto nPoints =
4800 128 : listOfRingsValues->value_length(nRingOffset);
4801 : const auto nPointOffset =
4802 128 : listOfRingsValues->value_offset(nRingOffset);
4803 : const double *padfRawXValue =
4804 128 : fieldXDouble->raw_values() + nPointOffset;
4805 : const double *padfRawYValue =
4806 128 : fieldYDouble->raw_values() + nPointOffset;
4807 688 : for (auto l = decltype(nPoints){0}; l < nPoints;
4808 : ++l)
4809 : {
4810 560 : sEnvelope.Merge(padfRawXValue[l],
4811 560 : padfRawYValue[l]);
4812 : }
4813 : // for bounding box, only the first ring matters
4814 :
4815 128 : if (m_sFilterEnvelope.Intersects(sEnvelope))
4816 : {
4817 72 : bMatchBBOX = true;
4818 : }
4819 : }
4820 : }
4821 312 : if (bMatchBBOX &&
4822 72 : (m_asAttributeFilterConstraints.empty() ||
4823 0 : !SkipToNextFeatureDueToAttributeFilter()))
4824 : {
4825 72 : bReturnFeature = true;
4826 72 : break;
4827 : }
4828 :
4829 168 : IncrFeatureIdx();
4830 168 : m_nIdxInBatch++;
4831 168 : if (m_nIdxInBatch == m_poBatch->num_rows())
4832 : {
4833 24 : m_bEOF = !ReadNextBatch();
4834 24 : if (m_bEOF)
4835 24 : return nullptr;
4836 0 : break;
4837 : }
4838 144 : }
4839 72 : } while (!bReturnFeature);
4840 : }
4841 738 : else if (iCol >= 0 &&
4842 369 : m_aeGeomEncoding[m_iGeomFieldFilter] ==
4843 : OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOINT)
4844 : {
4845 : bool bReturnFeature;
4846 0 : do
4847 : {
4848 64 : bReturnFeature = false;
4849 64 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
4850 64 : CPLAssert(array->type_id() == arrow::Type::LIST);
4851 64 : const auto listArray =
4852 : static_cast<const arrow::ListArray *>(array);
4853 64 : CPLAssert(listArray->values()->type_id() ==
4854 : arrow::Type::STRUCT);
4855 : auto pointValues = std::static_pointer_cast<arrow::StructArray>(
4856 64 : listArray->values());
4857 64 : const auto &fields = pointValues->fields();
4858 64 : const auto &fieldX = fields[0];
4859 64 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
4860 : const auto fieldXDouble =
4861 64 : static_cast<arrow::DoubleArray *>(fieldX.get());
4862 64 : const auto &fieldY = fields[1];
4863 64 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
4864 : const auto fieldYDouble =
4865 64 : static_cast<arrow::DoubleArray *>(fieldY.get());
4866 :
4867 : while (true)
4868 : {
4869 160 : bool bMatchBBOX = false;
4870 160 : if (!listArray->IsNull(m_nIdxInBatch))
4871 : {
4872 : const auto nPoints =
4873 128 : listArray->value_length(m_nIdxInBatch);
4874 : const auto nPointOffset =
4875 128 : listArray->value_offset(m_nIdxInBatch);
4876 128 : if (nPoints > 0)
4877 : {
4878 : const double *padfRawXValue =
4879 96 : fieldXDouble->raw_values() + nPointOffset;
4880 : const double *padfRawYValue =
4881 96 : fieldYDouble->raw_values() + nPointOffset;
4882 176 : for (auto l = decltype(nPoints){0}; l < nPoints;
4883 : ++l)
4884 : {
4885 128 : if (padfRawXValue[l] >=
4886 128 : m_sFilterEnvelope.MinX &&
4887 108 : padfRawYValue[l] >=
4888 108 : m_sFilterEnvelope.MinY &&
4889 88 : padfRawXValue[l] <=
4890 88 : m_sFilterEnvelope.MaxX &&
4891 68 : padfRawYValue[l] <= m_sFilterEnvelope.MaxY)
4892 : {
4893 48 : bMatchBBOX = true;
4894 48 : break;
4895 : }
4896 : }
4897 : }
4898 : }
4899 208 : if (bMatchBBOX &&
4900 48 : (m_asAttributeFilterConstraints.empty() ||
4901 0 : !SkipToNextFeatureDueToAttributeFilter()))
4902 : {
4903 48 : bReturnFeature = true;
4904 48 : break;
4905 : }
4906 :
4907 112 : IncrFeatureIdx();
4908 112 : m_nIdxInBatch++;
4909 112 : if (m_nIdxInBatch == m_poBatch->num_rows())
4910 : {
4911 16 : m_bEOF = !ReadNextBatch();
4912 16 : if (m_bEOF)
4913 16 : return nullptr;
4914 0 : break;
4915 : }
4916 96 : }
4917 48 : } while (!bReturnFeature);
4918 : }
4919 610 : else if (iCol >= 0 &&
4920 305 : m_aeGeomEncoding[m_iGeomFieldFilter] ==
4921 : OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTILINESTRING)
4922 : {
4923 : bool bReturnFeature;
4924 0 : do
4925 : {
4926 88 : bReturnFeature = false;
4927 88 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
4928 88 : CPLAssert(array->type_id() == arrow::Type::LIST);
4929 88 : auto listOfPartsArray =
4930 : static_cast<const arrow::ListArray *>(array);
4931 88 : CPLAssert(listOfPartsArray->values()->type_id() ==
4932 : arrow::Type::LIST);
4933 : auto listOfPartsValues =
4934 : std::static_pointer_cast<arrow::ListArray>(
4935 88 : listOfPartsArray->values());
4936 88 : CPLAssert(listOfPartsValues->values()->type_id() ==
4937 : arrow::Type::STRUCT);
4938 : auto pointValues = std::static_pointer_cast<arrow::StructArray>(
4939 88 : listOfPartsValues->values());
4940 88 : const auto &fields = pointValues->fields();
4941 88 : const auto &fieldX = fields[0];
4942 88 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
4943 : const auto fieldXDouble =
4944 88 : static_cast<arrow::DoubleArray *>(fieldX.get());
4945 88 : const auto &fieldY = fields[1];
4946 88 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
4947 : const auto fieldYDouble =
4948 88 : static_cast<arrow::DoubleArray *>(fieldY.get());
4949 :
4950 : while (true)
4951 : {
4952 200 : bool bMatchBBOX = false;
4953 200 : if (!listOfPartsArray->IsNull(m_nIdxInBatch))
4954 : {
4955 : const auto nParts =
4956 160 : listOfPartsArray->value_length(m_nIdxInBatch);
4957 : const auto nPartOffset =
4958 160 : listOfPartsArray->value_offset(m_nIdxInBatch);
4959 312 : for (auto j = decltype(nParts){0};
4960 312 : j < nParts && !bMatchBBOX; j++)
4961 : {
4962 152 : OGREnvelope sEnvelope;
4963 : const auto nPoints =
4964 152 : listOfPartsValues->value_length(nPartOffset +
4965 : j);
4966 : const auto nPointOffset =
4967 152 : listOfPartsValues->value_offset(nPartOffset +
4968 : j);
4969 : const double *padfRawXValue =
4970 152 : fieldXDouble->raw_values() + nPointOffset;
4971 : const double *padfRawYValue =
4972 152 : fieldYDouble->raw_values() + nPointOffset;
4973 488 : for (auto l = decltype(nPoints){0}; l < nPoints;
4974 : ++l)
4975 : {
4976 336 : sEnvelope.Merge(padfRawXValue[l],
4977 336 : padfRawYValue[l]);
4978 : }
4979 :
4980 152 : if (m_sFilterEnvelope.Intersects(sEnvelope))
4981 : {
4982 72 : bMatchBBOX = true;
4983 : }
4984 : }
4985 : }
4986 272 : if (bMatchBBOX &&
4987 72 : (m_asAttributeFilterConstraints.empty() ||
4988 0 : !SkipToNextFeatureDueToAttributeFilter()))
4989 : {
4990 72 : bReturnFeature = true;
4991 72 : break;
4992 : }
4993 :
4994 128 : IncrFeatureIdx();
4995 128 : m_nIdxInBatch++;
4996 128 : if (m_nIdxInBatch == m_poBatch->num_rows())
4997 : {
4998 16 : m_bEOF = !ReadNextBatch();
4999 16 : if (m_bEOF)
5000 16 : return nullptr;
5001 0 : break;
5002 : }
5003 112 : }
5004 72 : } while (!bReturnFeature);
5005 : }
5006 434 : else if (iCol >= 0 &&
5007 217 : m_aeGeomEncoding[m_iGeomFieldFilter] ==
5008 : OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOLYGON)
5009 : {
5010 : bool bReturnFeature;
5011 0 : do
5012 : {
5013 96 : bReturnFeature = false;
5014 96 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
5015 96 : CPLAssert(array->type_id() == arrow::Type::LIST);
5016 96 : auto listOfPartsArray =
5017 : static_cast<const arrow::ListArray *>(array);
5018 96 : CPLAssert(listOfPartsArray->values()->type_id() ==
5019 : arrow::Type::LIST);
5020 : auto listOfPartsValues =
5021 : std::static_pointer_cast<arrow::ListArray>(
5022 96 : listOfPartsArray->values());
5023 96 : CPLAssert(listOfPartsValues->values()->type_id() ==
5024 : arrow::Type::LIST);
5025 : auto listOfRingsValues =
5026 : std::static_pointer_cast<arrow::ListArray>(
5027 96 : listOfPartsValues->values());
5028 96 : CPLAssert(listOfRingsValues->values()->type_id() ==
5029 : arrow::Type::STRUCT);
5030 : auto pointValues = std::static_pointer_cast<arrow::StructArray>(
5031 96 : listOfRingsValues->values());
5032 96 : const auto &fields = pointValues->fields();
5033 96 : const auto &fieldX = fields[0];
5034 96 : CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
5035 : const auto fieldXDouble =
5036 96 : static_cast<arrow::DoubleArray *>(fieldX.get());
5037 96 : const auto &fieldY = fields[1];
5038 96 : CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
5039 : const auto fieldYDouble =
5040 96 : static_cast<arrow::DoubleArray *>(fieldY.get());
5041 :
5042 : while (true)
5043 : {
5044 240 : bool bMatchBBOX = false;
5045 240 : if (!listOfPartsArray->IsNull(m_nIdxInBatch))
5046 : {
5047 : const auto nParts =
5048 188 : listOfPartsArray->value_length(m_nIdxInBatch);
5049 : const auto nPartOffset =
5050 188 : listOfPartsArray->value_offset(m_nIdxInBatch);
5051 356 : for (auto j = decltype(nParts){0};
5052 356 : j < nParts && !bMatchBBOX; j++)
5053 : {
5054 168 : OGREnvelope sEnvelope;
5055 168 : const auto nRings = listOfPartsValues->value_length(
5056 168 : nPartOffset + j);
5057 : const auto nRingOffset =
5058 168 : listOfPartsValues->value_offset(nPartOffset +
5059 : j);
5060 168 : if (nRings >= 1)
5061 : {
5062 : const auto nPoints =
5063 168 : listOfRingsValues->value_length(
5064 : nRingOffset);
5065 : const auto nPointOffset =
5066 168 : listOfRingsValues->value_offset(
5067 : nRingOffset);
5068 : const double *padfRawXValue =
5069 168 : fieldXDouble->raw_values() + nPointOffset;
5070 : const double *padfRawYValue =
5071 168 : fieldYDouble->raw_values() + nPointOffset;
5072 888 : for (auto l = decltype(nPoints){0}; l < nPoints;
5073 : ++l)
5074 : {
5075 720 : sEnvelope.Merge(padfRawXValue[l],
5076 720 : padfRawYValue[l]);
5077 : }
5078 :
5079 168 : if (m_sFilterEnvelope.Intersects(sEnvelope))
5080 : {
5081 72 : bMatchBBOX = true;
5082 : }
5083 : // for bounding box, only the first ring matters
5084 : }
5085 : }
5086 : }
5087 312 : if (bMatchBBOX &&
5088 72 : (m_asAttributeFilterConstraints.empty() ||
5089 0 : !SkipToNextFeatureDueToAttributeFilter()))
5090 : {
5091 72 : bReturnFeature = true;
5092 72 : break;
5093 : }
5094 :
5095 168 : IncrFeatureIdx();
5096 168 : m_nIdxInBatch++;
5097 168 : if (m_nIdxInBatch == m_poBatch->num_rows())
5098 : {
5099 24 : m_bEOF = !ReadNextBatch();
5100 24 : if (m_bEOF)
5101 24 : return nullptr;
5102 0 : break;
5103 : }
5104 144 : }
5105 72 : } while (!bReturnFeature);
5106 : }
5107 121 : else if (iCol >= 0)
5108 : {
5109 121 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
5110 : while (true)
5111 : {
5112 220 : bool bMatchBBOX = false;
5113 :
5114 : auto poGeometry = std::unique_ptr<OGRGeometry>(
5115 220 : ReadGeometry(m_iGeomFieldFilter, array, m_nIdxInBatch));
5116 220 : if (poGeometry && !poGeometry->IsEmpty())
5117 : {
5118 115 : OGREnvelope sEnvelope;
5119 115 : poGeometry->getEnvelope(&sEnvelope);
5120 115 : if (m_sFilterEnvelope.Intersects(sEnvelope))
5121 : {
5122 88 : bMatchBBOX = true;
5123 : }
5124 : }
5125 220 : if (bMatchBBOX && (m_asAttributeFilterConstraints.empty() ||
5126 0 : !SkipToNextFeatureDueToAttributeFilter()))
5127 : {
5128 88 : break;
5129 : }
5130 :
5131 132 : IncrFeatureIdx();
5132 132 : m_nIdxInBatch++;
5133 132 : if (m_nIdxInBatch == m_poBatch->num_rows())
5134 : {
5135 33 : m_bEOF = !ReadNextBatch();
5136 33 : if (m_bEOF)
5137 33 : return nullptr;
5138 0 : array = GetStorageArray(m_poBatchColumns[iCol].get());
5139 : }
5140 99 : }
5141 1005 : }
5142 : }
5143 :
5144 9895 : else if (!m_asAttributeFilterConstraints.empty())
5145 : {
5146 : while (true)
5147 : {
5148 445 : if (!SkipToNextFeatureDueToAttributeFilter())
5149 : {
5150 243 : break;
5151 : }
5152 :
5153 202 : IncrFeatureIdx();
5154 202 : m_nIdxInBatch++;
5155 202 : if (m_nIdxInBatch == m_poBatch->num_rows())
5156 : {
5157 90 : m_bEOF = !ReadNextBatch();
5158 90 : if (m_bEOF)
5159 52 : return nullptr;
5160 : }
5161 : }
5162 : }
5163 :
5164 10848 : auto poFeature = ReadFeature(m_nIdxInBatch, m_poBatchColumns);
5165 :
5166 10848 : if (m_iFIDArrowColumn < 0)
5167 7450 : poFeature->SetFID(m_nFeatureIdx);
5168 :
5169 10848 : IncrFeatureIdx();
5170 10848 : m_nIdxInBatch++;
5171 :
5172 10848 : return poFeature;
5173 : }
5174 :
5175 : /************************************************************************/
5176 : /* GetExtentFromMetadata() */
5177 : /************************************************************************/
5178 :
5179 : inline OGRErr
5180 956 : OGRArrowLayer::GetExtentFromMetadata(const CPLJSONObject &oJSONDef,
5181 : OGREnvelope3D *psExtent)
5182 : {
5183 2868 : const auto oBBox = oJSONDef.GetArray("bbox");
5184 956 : if (oBBox.IsValid() && oBBox.Size() == 4)
5185 : {
5186 569 : psExtent->MinX = oBBox[0].ToDouble();
5187 569 : psExtent->MinY = oBBox[1].ToDouble();
5188 569 : psExtent->MinZ = std::numeric_limits<double>::infinity();
5189 569 : psExtent->MaxX = oBBox[2].ToDouble();
5190 569 : psExtent->MaxY = oBBox[3].ToDouble();
5191 569 : psExtent->MaxZ = -std::numeric_limits<double>::infinity();
5192 569 : if (psExtent->MinX <= psExtent->MaxX)
5193 569 : return OGRERR_NONE;
5194 : }
5195 387 : else if (oBBox.IsValid() && oBBox.Size() == 6)
5196 : {
5197 318 : psExtent->MinX = oBBox[0].ToDouble();
5198 318 : psExtent->MinY = oBBox[1].ToDouble();
5199 318 : psExtent->MinZ = oBBox[2].ToDouble();
5200 318 : psExtent->MaxX = oBBox[3].ToDouble();
5201 318 : psExtent->MaxY = oBBox[4].ToDouble();
5202 318 : psExtent->MaxZ = oBBox[5].ToDouble();
5203 318 : if (psExtent->MinX <= psExtent->MaxX)
5204 318 : return OGRERR_NONE;
5205 : }
5206 69 : return OGRERR_FAILURE;
5207 : }
5208 :
5209 : /************************************************************************/
5210 : /* ISetSpatialFilter() */
5211 : /************************************************************************/
5212 :
5213 1713 : inline OGRErr OGRArrowLayer::ISetSpatialFilter(int iGeomField,
5214 : const OGRGeometry *poGeomIn)
5215 :
5216 : {
5217 : // When changing filters, we need to invalidate cached batches, as
5218 : // PostFilterArrowArray() has potentially modified array contents
5219 1713 : if (m_poFilterGeom)
5220 1044 : InvalidateCachedBatches();
5221 :
5222 1713 : m_bSpatialFilterIntersectsLayerExtent = true;
5223 1713 : if (iGeomField < GetLayerDefn()->GetGeomFieldCount())
5224 : {
5225 1697 : m_iGeomFieldFilter = iGeomField;
5226 1697 : if (InstallFilter(poGeomIn))
5227 1487 : ResetReading();
5228 1697 : if (m_poFilterGeom != nullptr)
5229 : {
5230 1420 : OGREnvelope sLayerExtent;
5231 1420 : if (FastGetExtent(iGeomField, &sLayerExtent))
5232 : {
5233 812 : m_bSpatialFilterIntersectsLayerExtent =
5234 812 : CPL_TO_BOOL(m_sFilterEnvelope.Intersects(sLayerExtent));
5235 : }
5236 : }
5237 : }
5238 :
5239 1713 : SetBatch(m_poBatch);
5240 1713 : return OGRERR_NONE;
5241 : }
5242 :
5243 : /************************************************************************/
5244 : /* FastGetExtent() */
5245 : /************************************************************************/
5246 :
5247 923 : inline bool OGRArrowLayer::FastGetExtent(int iGeomField,
5248 : OGREnvelope *psExtent) const
5249 : {
5250 : {
5251 923 : const auto oIter = m_oMapExtents.find(iGeomField);
5252 923 : if (oIter != m_oMapExtents.end())
5253 : {
5254 5 : *psExtent = oIter->second;
5255 5 : return true;
5256 : }
5257 : }
5258 :
5259 : const char *pszGeomFieldName =
5260 918 : m_poFeatureDefn->GetGeomFieldDefn(iGeomField)->GetNameRef();
5261 918 : const auto oIter = m_oMapGeometryColumns.find(pszGeomFieldName);
5262 1834 : if (oIter != m_oMapGeometryColumns.end() &&
5263 916 : CPLTestBool(CPLGetConfigOption(
5264 1834 : ("OGR_" + GetDriverUCName() + "_USE_BBOX").c_str(), "YES")))
5265 : {
5266 910 : const auto &oJSONDef = oIter->second;
5267 910 : OGREnvelope3D sEnvelope3D;
5268 910 : if (GetExtentFromMetadata(oJSONDef, &sEnvelope3D) == OGRERR_NONE)
5269 : {
5270 851 : *psExtent = sEnvelope3D;
5271 851 : return true;
5272 : }
5273 : }
5274 67 : return false;
5275 : }
5276 :
5277 : /************************************************************************/
5278 : /* IGetExtent() */
5279 : /************************************************************************/
5280 :
5281 67 : inline OGRErr OGRArrowLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
5282 : bool bForce)
5283 : {
5284 67 : if (FastGetExtent(iGeomField, psExtent))
5285 : {
5286 46 : return OGRERR_NONE;
5287 : }
5288 :
5289 21 : if (!bForce && !CanRunNonForcedGetExtent())
5290 : {
5291 0 : return OGRERR_FAILURE;
5292 : }
5293 :
5294 : int iCol;
5295 21 : if (m_bIgnoredFields)
5296 : {
5297 0 : iCol = m_anMapGeomFieldIndexToArrayIndex[iGeomField];
5298 : }
5299 : else
5300 : {
5301 21 : iCol = m_anMapGeomFieldIndexToArrowColumn[iGeomField];
5302 : }
5303 21 : if (iCol < 0)
5304 : {
5305 0 : return OGRERR_FAILURE;
5306 : }
5307 :
5308 21 : if (m_aeGeomEncoding[iGeomField] == OGRArrowGeomEncoding::WKB)
5309 : {
5310 7 : ResetReading();
5311 7 : if (m_poBatch == nullptr)
5312 : {
5313 6 : m_bEOF = !ReadNextBatch();
5314 6 : if (m_bEOF)
5315 0 : return OGRERR_FAILURE;
5316 : }
5317 7 : *psExtent = OGREnvelope();
5318 :
5319 7 : auto array = GetStorageArray(m_poBatchColumns[iCol].get());
5320 7 : const arrow::BinaryArray *smallArray = nullptr;
5321 7 : const arrow::LargeBinaryArray *largeArray = nullptr;
5322 7 : if (array->type_id() == arrow::Type::BINARY)
5323 7 : smallArray = static_cast<const arrow::BinaryArray *>(array);
5324 : else
5325 : {
5326 0 : CPLAssert(array->type_id() == arrow::Type::LARGE_BINARY);
5327 0 : largeArray = static_cast<const arrow::LargeBinaryArray *>(array);
5328 : }
5329 7 : OGREnvelope sEnvelope;
5330 : while (true)
5331 : {
5332 32 : if (!array->IsNull(m_nIdxInBatch))
5333 : {
5334 30 : if (smallArray)
5335 : {
5336 30 : int out_length = 0;
5337 : const uint8_t *data =
5338 30 : smallArray->GetValue(m_nIdxInBatch, &out_length);
5339 30 : if (OGRWKBGetBoundingBox(data, out_length, sEnvelope))
5340 : {
5341 30 : psExtent->Merge(sEnvelope);
5342 : }
5343 : }
5344 : else
5345 : {
5346 0 : assert(largeArray);
5347 0 : int64_t out_length = 0;
5348 : const uint8_t *data =
5349 0 : largeArray->GetValue(m_nIdxInBatch, &out_length);
5350 0 : if (out_length < INT_MAX &&
5351 0 : OGRWKBGetBoundingBox(data, static_cast<int>(out_length),
5352 : sEnvelope))
5353 : {
5354 0 : psExtent->Merge(sEnvelope);
5355 : }
5356 : }
5357 : }
5358 :
5359 32 : m_nIdxInBatch++;
5360 32 : if (m_nIdxInBatch == m_poBatch->num_rows())
5361 : {
5362 7 : m_bEOF = !ReadNextBatch();
5363 7 : if (m_bEOF)
5364 : {
5365 7 : ResetReading();
5366 7 : if (psExtent->IsInit())
5367 : {
5368 7 : m_oMapExtents[iGeomField] = *psExtent;
5369 7 : return OGRERR_NONE;
5370 : }
5371 0 : return OGRERR_FAILURE;
5372 : }
5373 0 : array = GetStorageArray(m_poBatchColumns[iCol].get());
5374 0 : if (array->type_id() == arrow::Type::BINARY)
5375 0 : smallArray = static_cast<const arrow::BinaryArray *>(array);
5376 : else
5377 : {
5378 0 : CPLAssert(array->type_id() == arrow::Type::LARGE_BINARY);
5379 0 : largeArray =
5380 : static_cast<const arrow::LargeBinaryArray *>(array);
5381 : }
5382 : }
5383 25 : }
5384 : }
5385 14 : else if (m_aeGeomEncoding[iGeomField] ==
5386 : OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOLYGON)
5387 : {
5388 0 : ResetReading();
5389 0 : if (m_poBatch == nullptr)
5390 : {
5391 0 : m_bEOF = !ReadNextBatch();
5392 0 : if (m_bEOF)
5393 0 : return OGRERR_FAILURE;
5394 : }
5395 0 : *psExtent = OGREnvelope();
5396 :
5397 : const auto poGeomFieldDefn =
5398 0 : m_poFeatureDefn->GetGeomFieldDefn(iGeomField);
5399 0 : const auto eGeomType = poGeomFieldDefn->GetType();
5400 0 : const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
5401 0 : const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
5402 0 : const int nDim = 2 + (bHasZ ? 1 : 0) + (bHasM ? 1 : 0);
5403 :
5404 0 : begin_multipolygon:
5405 0 : auto array = m_poBatchColumns[iCol].get();
5406 0 : CPLAssert(array->type_id() == arrow::Type::LIST);
5407 0 : auto listOfPartsArray = static_cast<const arrow::ListArray *>(array);
5408 0 : CPLAssert(listOfPartsArray->values()->type_id() == arrow::Type::LIST);
5409 : auto listOfPartsValues = std::static_pointer_cast<arrow::ListArray>(
5410 0 : listOfPartsArray->values());
5411 0 : CPLAssert(listOfPartsValues->values()->type_id() == arrow::Type::LIST);
5412 : auto listOfRingsValues = std::static_pointer_cast<arrow::ListArray>(
5413 0 : listOfPartsValues->values());
5414 0 : CPLAssert(listOfRingsValues->values()->type_id() ==
5415 : arrow::Type::FIXED_SIZE_LIST);
5416 : auto listOfPointsValues =
5417 : std::static_pointer_cast<arrow::FixedSizeListArray>(
5418 0 : listOfRingsValues->values());
5419 0 : CPLAssert(listOfPointsValues->values()->type_id() ==
5420 : arrow::Type::DOUBLE);
5421 : auto pointValues = std::static_pointer_cast<arrow::DoubleArray>(
5422 0 : listOfPointsValues->values());
5423 :
5424 : while (true)
5425 : {
5426 0 : if (!listOfPartsArray->IsNull(m_nIdxInBatch))
5427 : {
5428 : const auto nParts =
5429 0 : listOfPartsArray->value_length(m_nIdxInBatch);
5430 : const auto nPartOffset =
5431 0 : listOfPartsArray->value_offset(m_nIdxInBatch);
5432 0 : for (auto j = decltype(nParts){0}; j < nParts; j++)
5433 : {
5434 : const auto nRings =
5435 0 : listOfPartsValues->value_length(nPartOffset + j);
5436 : const auto nRingOffset =
5437 0 : listOfPartsValues->value_offset(nPartOffset + j);
5438 0 : if (nRings >= 1)
5439 : {
5440 : const auto nPoints =
5441 0 : listOfRingsValues->value_length(nRingOffset);
5442 : const auto nPointOffset =
5443 0 : listOfRingsValues->value_offset(nRingOffset) * nDim;
5444 : const double *padfRawValue =
5445 0 : pointValues->raw_values() + nPointOffset;
5446 0 : for (auto l = decltype(nPoints){0}; l < nPoints; ++l)
5447 : {
5448 0 : psExtent->Merge(padfRawValue[nDim * l],
5449 0 : padfRawValue[nDim * l + 1]);
5450 : }
5451 : // for bounding box, only the first ring matters
5452 : }
5453 : }
5454 : }
5455 :
5456 0 : m_nIdxInBatch++;
5457 0 : if (m_nIdxInBatch == m_poBatch->num_rows())
5458 : {
5459 0 : m_bEOF = !ReadNextBatch();
5460 0 : if (m_bEOF)
5461 : {
5462 0 : ResetReading();
5463 0 : if (psExtent->IsInit())
5464 : {
5465 0 : m_oMapExtents[iGeomField] = *psExtent;
5466 0 : return OGRERR_NONE;
5467 : }
5468 0 : return OGRERR_FAILURE;
5469 : }
5470 0 : goto begin_multipolygon;
5471 : }
5472 0 : }
5473 : }
5474 :
5475 14 : return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
5476 : }
5477 :
5478 : /************************************************************************/
5479 : /* FastGetExtent3D() */
5480 : /************************************************************************/
5481 :
5482 38 : inline bool OGRArrowLayer::FastGetExtent3D(int iGeomField,
5483 : OGREnvelope3D *psExtent) const
5484 : {
5485 : {
5486 38 : const auto oIter = m_oMapExtents3D.find(iGeomField);
5487 38 : if (oIter != m_oMapExtents3D.end())
5488 : {
5489 0 : *psExtent = oIter->second;
5490 0 : return true;
5491 : }
5492 : }
5493 :
5494 : const char *pszGeomFieldName =
5495 38 : m_poFeatureDefn->GetGeomFieldDefn(iGeomField)->GetNameRef();
5496 38 : const auto oIter = m_oMapGeometryColumns.find(pszGeomFieldName);
5497 76 : if (oIter != m_oMapGeometryColumns.end() &&
5498 38 : CPLTestBool(CPLGetConfigOption(
5499 76 : ("OGR_" + GetDriverUCName() + "_USE_BBOX").c_str(), "YES")))
5500 : {
5501 36 : const auto &oJSONDef = oIter->second;
5502 64 : if (GetExtentFromMetadata(oJSONDef, psExtent) == OGRERR_NONE &&
5503 28 : psExtent->Is3D())
5504 : {
5505 2 : return true;
5506 : }
5507 : }
5508 36 : return false;
5509 : }
5510 :
5511 : /************************************************************************/
5512 : /* IGetExtent3D() */
5513 : /************************************************************************/
5514 :
5515 19 : inline OGRErr OGRArrowLayer::IGetExtent3D(int iGeomField,
5516 : OGREnvelope3D *psExtent, bool bForce)
5517 : {
5518 19 : if (FastGetExtent3D(iGeomField, psExtent))
5519 : {
5520 1 : return OGRERR_NONE;
5521 : }
5522 :
5523 18 : return OGRLayer::IGetExtent3D(iGeomField, psExtent, bForce);
5524 : }
5525 :
5526 : /************************************************************************/
5527 : /* OverrideArrowSchemaRelease() */
5528 : /************************************************************************/
5529 :
5530 : template <class T>
5531 611 : static void OverrideArrowRelease(OGRArrowDataset *poDS, T *obj)
5532 : {
5533 : // We override the release callback, since it can use the memory pool,
5534 : // and we need to make sure it is still alive when the object (ArrowArray
5535 : // or ArrowSchema) is deleted
5536 611 : struct OverriddenPrivate
5537 : {
5538 : OverriddenPrivate() = default;
5539 : OverriddenPrivate(const OverriddenPrivate &) = delete;
5540 : OverriddenPrivate &operator=(const OverriddenPrivate &) = delete;
5541 :
5542 : std::shared_ptr<arrow::MemoryPool> poMemoryPool{};
5543 : void (*pfnPreviousRelease)(T *) = nullptr;
5544 : void *pPreviousPrivateData = nullptr;
5545 :
5546 611 : static void release(T *l_obj)
5547 : {
5548 611 : OverriddenPrivate *myPrivate =
5549 : static_cast<OverriddenPrivate *>(l_obj->private_data);
5550 611 : l_obj->private_data = myPrivate->pPreviousPrivateData;
5551 611 : l_obj->release = myPrivate->pfnPreviousRelease;
5552 611 : l_obj->release(l_obj);
5553 611 : delete myPrivate;
5554 611 : }
5555 : };
5556 :
5557 611 : auto overriddenPrivate = new OverriddenPrivate();
5558 611 : overriddenPrivate->poMemoryPool = poDS->GetSharedMemoryPool();
5559 611 : overriddenPrivate->pPreviousPrivateData = obj->private_data;
5560 611 : overriddenPrivate->pfnPreviousRelease = obj->release;
5561 :
5562 611 : obj->release = OverriddenPrivate::release;
5563 611 : obj->private_data = overriddenPrivate;
5564 611 : }
5565 :
5566 : /************************************************************************/
5567 : /* UseRecordBatchBaseImplementation() */
5568 : /************************************************************************/
5569 :
5570 475 : inline bool OGRArrowLayer::UseRecordBatchBaseImplementation() const
5571 : {
5572 475 : if (CPLTestBool(CPLGetConfigOption("OGR_ARROW_STREAM_BASE_IMPL", "NO")))
5573 : {
5574 4 : return true;
5575 : }
5576 :
5577 471 : if (m_aosArrowArrayStreamOptions.FetchBool(GAS_OPT_DATETIME_AS_STRING,
5578 : false))
5579 : {
5580 1 : const int nFieldCount = m_poFeatureDefn->GetFieldCount();
5581 14 : for (int i = 0; i < nFieldCount; ++i)
5582 : {
5583 14 : const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
5584 28 : if (!poFieldDefn->IsIgnored() &&
5585 14 : poFieldDefn->GetType() == OFTDateTime)
5586 : {
5587 1 : CPLDebug("ARROW",
5588 : "DATETIME_AS_STRING=YES not compatible with fast "
5589 : "Arrow implementation");
5590 1 : return true;
5591 : }
5592 : }
5593 : }
5594 :
5595 470 : if (EQUAL(m_aosArrowArrayStreamOptions.FetchNameValueDef(
5596 : "GEOMETRY_ENCODING", ""),
5597 : "WKB"))
5598 : {
5599 131 : const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
5600 181 : for (int i = 0; i < nGeomFieldCount; i++)
5601 : {
5602 146 : if (!m_poFeatureDefn->GetGeomFieldDefn(i)->IsIgnored() &&
5603 259 : m_aeGeomEncoding[i] != OGRArrowGeomEncoding::WKB &&
5604 113 : m_aeGeomEncoding[i] != OGRArrowGeomEncoding::WKT)
5605 : {
5606 96 : CPLDebug("ARROW", "Geometry encoding not compatible with fast "
5607 : "Arrow implementation");
5608 96 : return true;
5609 : }
5610 : }
5611 : }
5612 :
5613 374 : if (m_bIgnoredFields)
5614 : {
5615 : std::vector<int> ignoredState(m_anMapFieldIndexToArrowColumn.size(),
5616 107 : -1);
5617 7661 : for (size_t i = 0; i < m_anMapFieldIndexToArrowColumn.size(); i++)
5618 : {
5619 7555 : const int nArrowCol = m_anMapFieldIndexToArrowColumn[i][0];
5620 7555 : if (nArrowCol >= static_cast<int>(ignoredState.size()))
5621 2 : ignoredState.resize(nArrowCol + 1, -1);
5622 : const auto bIsIgnored =
5623 7555 : m_poFeatureDefn->GetFieldDefn(static_cast<int>(i))->IsIgnored();
5624 7555 : if (ignoredState[nArrowCol] < 0)
5625 : {
5626 7097 : ignoredState[nArrowCol] = static_cast<int>(bIsIgnored);
5627 : }
5628 : else
5629 : {
5630 : // struct fields will point to the same arrow column
5631 458 : if (ignoredState[nArrowCol] != static_cast<int>(bIsIgnored))
5632 : {
5633 1 : CPLDebug("ARROW",
5634 : "Inconsistent ignore state for Arrow Columns");
5635 1 : return true;
5636 : }
5637 : }
5638 : }
5639 : }
5640 :
5641 373 : if (m_poAttrQuery || m_poFilterGeom)
5642 : {
5643 163 : struct ArrowSchema *psSchema = &m_sCachedSchema;
5644 163 : if (psSchema->release)
5645 104 : psSchema->release(psSchema);
5646 163 : memset(psSchema, 0, sizeof(*psSchema));
5647 :
5648 326 : const bool bCanPostFilter = GetArrowSchemaInternal(psSchema) == 0 &&
5649 163 : CanPostFilterArrowArray(psSchema);
5650 163 : if (!bCanPostFilter)
5651 11 : return true;
5652 : }
5653 :
5654 362 : return false;
5655 : }
5656 :
5657 : /************************************************************************/
5658 : /* GetArrowStream() */
5659 : /************************************************************************/
5660 :
5661 294 : inline bool OGRArrowLayer::GetArrowStream(struct ArrowArrayStream *out_stream,
5662 : CSLConstList papszOptions)
5663 : {
5664 294 : if (!OGRLayer::GetArrowStream(out_stream, papszOptions))
5665 0 : return false;
5666 :
5667 294 : m_bUseRecordBatchBaseImplementation = UseRecordBatchBaseImplementation();
5668 294 : return true;
5669 : }
5670 :
5671 : /************************************************************************/
5672 : /* GetArrowSchema() */
5673 : /************************************************************************/
5674 :
5675 434 : inline int OGRArrowLayer::GetArrowSchema(struct ArrowArrayStream *stream,
5676 : struct ArrowSchema *out_schema)
5677 : {
5678 434 : if (m_bUseRecordBatchBaseImplementation)
5679 209 : return OGRLayer::GetArrowSchema(stream, out_schema);
5680 :
5681 225 : return GetArrowSchemaInternal(out_schema);
5682 : }
5683 :
5684 : /************************************************************************/
5685 : /* GetArrowSchemaInternal() */
5686 : /************************************************************************/
5687 :
5688 10802 : static bool IsSilentlyIgnoredFormatForGetArrowSchemaArray(const char *format)
5689 : {
5690 : // n: null
5691 10802 : return strcmp(format, "n") == 0;
5692 : }
5693 :
5694 : inline int
5695 388 : OGRArrowLayer::GetArrowSchemaInternal(struct ArrowSchema *out_schema) const
5696 : {
5697 776 : auto status = arrow::ExportSchema(*m_poSchema, out_schema);
5698 388 : if (!status.ok())
5699 : {
5700 0 : CPLError(CE_Failure, CPLE_AppDefined, "ExportSchema() failed with %s",
5701 0 : status.message().c_str());
5702 0 : return EIO;
5703 : }
5704 :
5705 388 : CPLAssert(out_schema->n_children == m_poSchema->num_fields());
5706 :
5707 : // Remove ignored fields from the ArrowSchema.
5708 :
5709 : struct FieldDesc
5710 : {
5711 : bool bIsRegularField =
5712 : false; // true = attribute field, false = geometry field
5713 : int nIdx = -1;
5714 : };
5715 :
5716 : // cppcheck-suppress unreadVariable
5717 : std::vector<FieldDesc> fieldDesc(
5718 776 : static_cast<size_t>(out_schema->n_children));
5719 21819 : for (size_t i = 0; i < m_anMapFieldIndexToArrowColumn.size(); i++)
5720 : {
5721 21431 : const int nArrowCol = m_anMapFieldIndexToArrowColumn[i][0];
5722 21431 : if (fieldDesc[nArrowCol].nIdx < 0)
5723 : {
5724 20150 : fieldDesc[nArrowCol].bIsRegularField = true;
5725 20150 : fieldDesc[nArrowCol].nIdx = static_cast<int>(i);
5726 : }
5727 : }
5728 790 : for (size_t i = 0; i < m_anMapGeomFieldIndexToArrowColumn.size(); i++)
5729 : {
5730 402 : const int nArrowCol = m_anMapGeomFieldIndexToArrowColumn[i];
5731 402 : CPLAssert(fieldDesc[nArrowCol].nIdx < 0);
5732 402 : fieldDesc[nArrowCol].bIsRegularField = false;
5733 402 : fieldDesc[nArrowCol].nIdx = static_cast<int>(i);
5734 : }
5735 :
5736 388 : int j = 0;
5737 : const char *pszReqGeomEncoding =
5738 388 : m_aosArrowArrayStreamOptions.FetchNameValueDef("GEOMETRY_ENCODING", "");
5739 :
5740 388 : const char *pszExtensionName = EXTENSION_NAME_OGC_WKB;
5741 388 : if (EQUAL(pszReqGeomEncoding, "WKB") || EQUAL(pszReqGeomEncoding, ""))
5742 : {
5743 : const char *const pszGeometryMetadataEncoding =
5744 388 : m_aosArrowArrayStreamOptions.FetchNameValue(
5745 : "GEOMETRY_METADATA_ENCODING");
5746 388 : if (pszGeometryMetadataEncoding)
5747 : {
5748 0 : if (EQUAL(pszGeometryMetadataEncoding, "OGC"))
5749 0 : pszExtensionName = EXTENSION_NAME_OGC_WKB;
5750 0 : else if (EQUAL(pszGeometryMetadataEncoding, "GEOARROW"))
5751 0 : pszExtensionName = EXTENSION_NAME_GEOARROW_WKB;
5752 : else
5753 0 : CPLError(CE_Warning, CPLE_NotSupported,
5754 : "Unsupported GEOMETRY_METADATA_ENCODING value: %s",
5755 : pszGeometryMetadataEncoding);
5756 : }
5757 : }
5758 :
5759 21298 : for (int i = 0; i < out_schema->n_children; ++i)
5760 : {
5761 20910 : if (fieldDesc[i].nIdx < 0)
5762 : {
5763 358 : if (m_iFIDArrowColumn == i)
5764 : {
5765 21 : out_schema->children[j] = out_schema->children[i];
5766 21 : ++j;
5767 : }
5768 337 : else if (cpl::contains(m_oSetBBoxArrowColumns, i))
5769 : {
5770 : // Remove bounding box columns from exported schema
5771 82 : out_schema->children[i]->release(out_schema->children[i]);
5772 82 : out_schema->children[i] = nullptr;
5773 : }
5774 255 : else if (IsSilentlyIgnoredFormatForGetArrowSchemaArray(
5775 255 : out_schema->children[i]->format))
5776 : {
5777 : // Silently ignore columns with null data type...
5778 255 : out_schema->children[i]->release(out_schema->children[i]);
5779 : }
5780 : else
5781 : {
5782 : // can happen with data types we don't support
5783 0 : if (m_aosArrowArrayStreamOptions.FetchBool(
5784 : "SILENCE_GET_SCHEMA_ERROR", false))
5785 : {
5786 0 : CPLDebug(GetDriverUCName().c_str(),
5787 : "GetArrowSchema() error: fieldDesc[%d].nIdx < 0 "
5788 : "not expected: name=%s, format=%s",
5789 0 : i, out_schema->children[i]->name,
5790 0 : out_schema->children[i]->format);
5791 : }
5792 : else
5793 : {
5794 0 : CPLError(CE_Failure, CPLE_NotSupported,
5795 : "GetArrowSchema() error: fieldDesc[%d].nIdx < 0 "
5796 : "not expected: name=%s, format=%s",
5797 0 : i, out_schema->children[i]->name,
5798 0 : out_schema->children[i]->format);
5799 : }
5800 0 : for (; i < out_schema->n_children; ++i, ++j)
5801 0 : out_schema->children[j] = out_schema->children[i];
5802 0 : out_schema->n_children = j;
5803 :
5804 0 : out_schema->release(out_schema);
5805 :
5806 0 : return EIO;
5807 : }
5808 358 : continue;
5809 : }
5810 :
5811 : const auto bIsIgnored =
5812 20552 : fieldDesc[i].bIsRegularField
5813 20552 : ? m_poFeatureDefn->GetFieldDefn(fieldDesc[i].nIdx)->IsIgnored()
5814 402 : : m_poFeatureDefn->GetGeomFieldDefn(fieldDesc[i].nIdx)
5815 402 : ->IsIgnored();
5816 20552 : if (bIsIgnored)
5817 : {
5818 2912 : out_schema->children[i]->release(out_schema->children[i]);
5819 : }
5820 : else
5821 : {
5822 18023 : if (!fieldDesc[i].bIsRegularField &&
5823 383 : EQUAL(pszReqGeomEncoding, "WKB"))
5824 : {
5825 64 : const int iGeomField = fieldDesc[i].nIdx;
5826 64 : if (m_aeGeomEncoding[iGeomField] == OGRArrowGeomEncoding::WKT)
5827 : {
5828 : const auto poGeomFieldDefn =
5829 17 : m_poFeatureDefn->GetGeomFieldDefn(iGeomField);
5830 17 : CPLAssert(strcmp(out_schema->children[i]->name,
5831 : poGeomFieldDefn->GetNameRef()) == 0);
5832 17 : auto poSchema = CreateSchemaForWKBGeometryColumn(
5833 : poGeomFieldDefn, "z", pszExtensionName);
5834 17 : out_schema->children[i]->release(out_schema->children[i]);
5835 17 : *(out_schema->children[j]) = *poSchema;
5836 17 : CPLFree(poSchema);
5837 : }
5838 47 : else if (m_aeGeomEncoding[iGeomField] !=
5839 : OGRArrowGeomEncoding::WKB)
5840 : {
5841 : // Shouldn't happen if UseRecordBatchBaseImplementation()
5842 : // is up to date
5843 0 : CPLAssert(false);
5844 : }
5845 : else
5846 : {
5847 47 : out_schema->children[j] = out_schema->children[i];
5848 : }
5849 : }
5850 : else
5851 : {
5852 17576 : out_schema->children[j] = out_schema->children[i];
5853 : }
5854 :
5855 18023 : if (!fieldDesc[i].bIsRegularField &&
5856 383 : (EQUAL(pszReqGeomEncoding, "WKB") ||
5857 319 : EQUAL(pszReqGeomEncoding, "")))
5858 : {
5859 383 : const int iGeomField = fieldDesc[i].nIdx;
5860 383 : const char *pszFormat = out_schema->children[j]->format;
5861 383 : if (m_aeGeomEncoding[iGeomField] == OGRArrowGeomEncoding::WKB &&
5862 695 : !out_schema->children[j]->metadata &&
5863 312 : (strcmp(pszFormat, "z") == 0 ||
5864 1 : strcmp(pszFormat, "Z") == 0))
5865 : {
5866 : const auto poGeomFieldDefn =
5867 312 : m_poFeatureDefn->GetGeomFieldDefn(iGeomField);
5868 : // Set ARROW:extension:name = ogc:wkb
5869 312 : auto poSchema = CreateSchemaForWKBGeometryColumn(
5870 : poGeomFieldDefn, pszFormat, pszExtensionName);
5871 312 : out_schema->children[i]->release(out_schema->children[i]);
5872 312 : *(out_schema->children[j]) = *poSchema;
5873 312 : CPLFree(poSchema);
5874 : }
5875 : }
5876 :
5877 17640 : ++j;
5878 : }
5879 : }
5880 :
5881 388 : out_schema->n_children = j;
5882 :
5883 388 : OverrideArrowRelease(m_poArrowDS, out_schema);
5884 :
5885 388 : return 0;
5886 : }
5887 :
5888 : /************************************************************************/
5889 : /* GetNextArrowArray() */
5890 : /************************************************************************/
5891 :
5892 606 : inline int OGRArrowLayer::GetNextArrowArray(struct ArrowArrayStream *stream,
5893 : struct ArrowArray *out_array)
5894 : {
5895 606 : if (m_bUseRecordBatchBaseImplementation)
5896 224 : return OGRLayer::GetNextArrowArray(stream, out_array);
5897 :
5898 : while (true)
5899 : {
5900 400 : if (m_bEOF)
5901 : {
5902 10 : memset(out_array, 0, sizeof(*out_array));
5903 177 : return 0;
5904 : }
5905 :
5906 390 : if (m_poBatch == nullptr || m_nIdxInBatch == m_poBatch->num_rows())
5907 : {
5908 365 : if (!ReadNextBatch())
5909 : {
5910 167 : if (m_poAttrQuery || m_poFilterGeom)
5911 : {
5912 97 : InvalidateCachedBatches();
5913 : }
5914 167 : m_bEOF = true;
5915 167 : memset(out_array, 0, sizeof(*out_array));
5916 167 : return 0;
5917 : }
5918 : }
5919 :
5920 223 : const bool bNeedsPostFilter =
5921 335 : (m_poAttrQuery && !m_bBaseArrowIgnoreAttributeFilter) ||
5922 112 : (m_poFilterGeom && !m_bBaseArrowIgnoreSpatialFilter);
5923 :
5924 : struct ArrowSchema schema;
5925 223 : memset(&schema, 0, sizeof(schema));
5926 223 : auto status = arrow::ExportRecordBatch(*m_poBatch, out_array, &schema);
5927 223 : m_nIdxInBatch = m_poBatch->num_rows();
5928 223 : if (!status.ok())
5929 : {
5930 0 : CPLError(CE_Failure, CPLE_AppDefined,
5931 : "ExportRecordBatch() failed with %s",
5932 0 : status.message().c_str());
5933 0 : return EIO;
5934 : }
5935 :
5936 : // Remove bounding box columns from exported array, or columns
5937 : // of unsupported data types that we voluntarily strip off.
5938 : const auto RemoveBBoxOrUnsupportedColumns =
5939 32662 : [out_array, &schema](const std::set<int> &oSetBBoxArrayIndex)
5940 : {
5941 223 : int j = 0;
5942 10820 : for (int i = 0; i < static_cast<int>(schema.n_children); ++i)
5943 : {
5944 21144 : if (cpl::contains(oSetBBoxArrayIndex, i) ||
5945 10547 : IsSilentlyIgnoredFormatForGetArrowSchemaArray(
5946 10547 : schema.children[i]->format))
5947 : {
5948 126 : out_array->children[i]->release(out_array->children[i]);
5949 126 : out_array->children[i] = nullptr;
5950 :
5951 126 : schema.children[i]->release(schema.children[i]);
5952 126 : schema.children[i] = nullptr;
5953 : }
5954 : else
5955 : {
5956 10471 : out_array->children[j] = out_array->children[i];
5957 10471 : schema.children[j] = schema.children[i];
5958 10471 : ++j;
5959 : }
5960 : }
5961 223 : out_array->n_children = j;
5962 223 : schema.n_children = j;
5963 223 : };
5964 :
5965 223 : if (m_bIgnoredFields)
5966 : {
5967 180 : std::set<int> oSetBBoxArrayIndex;
5968 127 : for (const auto &iter : m_oMapGeomFieldIndexToGeomColBBOX)
5969 : {
5970 37 : if (iter.second.iArrayIdx >= 0)
5971 20 : oSetBBoxArrayIndex.insert(iter.second.iArrayIdx);
5972 : }
5973 90 : RemoveBBoxOrUnsupportedColumns(oSetBBoxArrayIndex);
5974 : }
5975 : else
5976 : {
5977 133 : RemoveBBoxOrUnsupportedColumns(m_oSetBBoxArrowColumns);
5978 : }
5979 :
5980 223 : if (EQUAL(m_aosArrowArrayStreamOptions.FetchNameValueDef(
5981 : "GEOMETRY_ENCODING", ""),
5982 : "WKB"))
5983 : {
5984 40 : const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
5985 95 : for (int i = 0; i < nGeomFieldCount; i++)
5986 : {
5987 : const auto poGeomFieldDefn =
5988 55 : m_poFeatureDefn->GetGeomFieldDefn(i);
5989 55 : if (!poGeomFieldDefn->IsIgnored())
5990 : {
5991 47 : if (m_aeGeomEncoding[i] == OGRArrowGeomEncoding::WKT)
5992 : {
5993 : const int nArrayIdx =
5994 18 : m_bIgnoredFields
5995 26 : ? m_anMapGeomFieldIndexToArrayIndex[i]
5996 8 : : m_anMapGeomFieldIndexToArrowColumn[i];
5997 18 : auto sourceArray = out_array->children[nArrayIdx];
5998 : auto targetArray =
5999 18 : strcmp(schema.children[nArrayIdx]->format, "u") == 0
6000 18 : ? CreateWKBArrayFromWKTArray<uint32_t>(
6001 : sourceArray)
6002 0 : : CreateWKBArrayFromWKTArray<uint64_t>(
6003 18 : sourceArray);
6004 18 : if (targetArray)
6005 : {
6006 18 : sourceArray->release(sourceArray);
6007 18 : *(out_array->children[nArrayIdx]) = *targetArray;
6008 18 : CPLFree(targetArray);
6009 : }
6010 : else
6011 : {
6012 0 : out_array->release(out_array);
6013 0 : memset(out_array, 0, sizeof(*out_array));
6014 0 : if (schema.release)
6015 0 : schema.release(&schema);
6016 0 : return ENOMEM;
6017 : }
6018 : }
6019 29 : else if (m_aeGeomEncoding[i] != OGRArrowGeomEncoding::WKB)
6020 : {
6021 : // Shouldn't happen if UseRecordBatchBaseImplementation()
6022 : // is up to date
6023 0 : CPLAssert(false);
6024 : }
6025 : }
6026 : }
6027 : }
6028 :
6029 223 : if (schema.release)
6030 223 : schema.release(&schema);
6031 :
6032 223 : OverrideArrowRelease(m_poArrowDS, out_array);
6033 :
6034 223 : const auto nFeatureIdxCur = m_nFeatureIdx;
6035 : // TODO: We likely have an issue regarding FIDs based on m_nFeatureIdx
6036 : // when m_iFIDArrowColumn < 0, only a subset of row groups is
6037 : // selected, and this batch goes across non consecutive row groups.
6038 1123 : for (int64_t i = 0; i < m_nIdxInBatch; ++i)
6039 900 : IncrFeatureIdx();
6040 :
6041 223 : if (bNeedsPostFilter)
6042 : {
6043 130 : CPLStringList aosOptions;
6044 130 : if (m_iFIDArrowColumn < 0)
6045 : aosOptions.SetNameValue(
6046 : "BASE_SEQUENTIAL_FID",
6047 : CPLSPrintf(CPL_FRMT_GIB,
6048 122 : static_cast<GIntBig>(nFeatureIdxCur)));
6049 :
6050 : // If there might be more than one record batch, it is more
6051 : // prudent to clone the array before modifying it.
6052 130 : if (nFeatureIdxCur > 0 || !TestCapability(OLCFastFeatureCount) ||
6053 0 : out_array->length < GetFeatureCount(false))
6054 : {
6055 : struct ArrowArray new_array;
6056 130 : if (!OGRCloneArrowArray(&m_sCachedSchema, out_array,
6057 : &new_array))
6058 : {
6059 0 : if (out_array->release)
6060 0 : out_array->release(out_array);
6061 0 : memset(out_array, 0, sizeof(*out_array));
6062 0 : return ENOMEM;
6063 : }
6064 130 : if (out_array->release)
6065 130 : out_array->release(out_array);
6066 130 : memcpy(out_array, &new_array, sizeof(new_array));
6067 : }
6068 :
6069 130 : PostFilterArrowArray(&m_sCachedSchema, out_array,
6070 130 : aosOptions.List());
6071 130 : if (out_array->length == 0)
6072 : {
6073 18 : if (out_array->release)
6074 18 : out_array->release(out_array);
6075 18 : memset(out_array, 0, sizeof(*out_array));
6076 : // If there are no records after filtering, start again
6077 : // with a new batch
6078 18 : continue;
6079 : }
6080 : }
6081 :
6082 205 : break;
6083 18 : }
6084 :
6085 205 : return 0;
6086 : }
6087 :
6088 : /************************************************************************/
6089 : /* OGRArrowLayerAppendBuffer */
6090 : /************************************************************************/
6091 :
6092 : class OGRArrowLayerAppendBuffer : public OGRAppendBuffer
6093 : {
6094 : public:
6095 18 : OGRArrowLayerAppendBuffer(struct ArrowArray *targetArrayIn,
6096 : size_t nInitialCapacityIn)
6097 18 : : m_psTargetArray(targetArrayIn)
6098 : {
6099 18 : m_nCapacity = nInitialCapacityIn;
6100 18 : m_pRawBuffer = const_cast<void *>(m_psTargetArray->buffers[2]);
6101 18 : }
6102 :
6103 : protected:
6104 4 : bool Grow(size_t nItemSize) override
6105 : {
6106 4 : constexpr uint32_t MAX_SIZE_SINT32 =
6107 : static_cast<uint32_t>(std::numeric_limits<int32_t>::max());
6108 4 : if (nItemSize > MAX_SIZE_SINT32 - m_nSize)
6109 : {
6110 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too large WKT content");
6111 0 : return false;
6112 : }
6113 4 : size_t nNewCapacity = m_nSize + nItemSize;
6114 4 : CPLAssert(m_nCapacity <= MAX_SIZE_SINT32);
6115 : const size_t nDoubleCapacity =
6116 4 : std::min<size_t>(MAX_SIZE_SINT32, 2 * m_nCapacity);
6117 4 : if (nNewCapacity < nDoubleCapacity)
6118 4 : nNewCapacity = nDoubleCapacity;
6119 4 : CPLAssert(nNewCapacity <= MAX_SIZE_SINT32);
6120 4 : void *newBuffer = VSI_MALLOC_ALIGNED_AUTO_VERBOSE(nNewCapacity);
6121 4 : if (newBuffer == nullptr)
6122 : {
6123 0 : return false;
6124 : }
6125 4 : m_nCapacity = nNewCapacity;
6126 4 : memcpy(newBuffer, m_pRawBuffer, m_nSize);
6127 4 : VSIFreeAligned(m_pRawBuffer);
6128 4 : m_pRawBuffer = newBuffer;
6129 4 : m_psTargetArray->buffers[2] = m_pRawBuffer;
6130 4 : return true;
6131 : }
6132 :
6133 : private:
6134 : struct ArrowArray *m_psTargetArray;
6135 :
6136 : OGRArrowLayerAppendBuffer(const OGRArrowLayerAppendBuffer &) = delete;
6137 : OGRArrowLayerAppendBuffer &
6138 : operator=(const OGRArrowLayerAppendBuffer &) = delete;
6139 : };
6140 :
6141 : /************************************************************************/
6142 : /* CreateWKBArrayFromWKTArray() */
6143 : /************************************************************************/
6144 :
6145 : template <typename SourceOffset>
6146 : inline struct ArrowArray *
6147 18 : OGRArrowLayer::CreateWKBArrayFromWKTArray(const struct ArrowArray *sourceArray)
6148 : {
6149 18 : CPLAssert(sourceArray->n_buffers == 3);
6150 18 : CPLAssert(sourceArray->buffers[1] != nullptr);
6151 18 : CPLAssert(sourceArray->buffers[2] != nullptr);
6152 :
6153 18 : const size_t nLength = static_cast<size_t>(sourceArray->length);
6154 : auto targetArray = static_cast<struct ArrowArray *>(
6155 18 : CPLCalloc(1, sizeof(struct ArrowArray)));
6156 18 : targetArray->release = OGRLayer::ReleaseArray;
6157 18 : targetArray->length = nLength;
6158 :
6159 18 : targetArray->n_buffers = 3;
6160 18 : targetArray->buffers =
6161 18 : static_cast<const void **>(CPLCalloc(3, sizeof(void *)));
6162 :
6163 : // Allocate validity map buffer if needed
6164 18 : const auto sourceNull =
6165 18 : static_cast<const uint8_t *>(sourceArray->buffers[0]);
6166 18 : const size_t nOffset = static_cast<size_t>(sourceArray->offset);
6167 18 : uint8_t *targetNull = nullptr;
6168 18 : if (sourceArray->null_count && sourceNull)
6169 : {
6170 6 : targetArray->buffers[0] =
6171 3 : VSI_MALLOC_ALIGNED_AUTO_VERBOSE((nLength + 7) / 8);
6172 3 : if (targetArray->buffers[0])
6173 : {
6174 3 : targetArray->null_count = sourceArray->null_count;
6175 3 : targetNull = static_cast<uint8_t *>(
6176 3 : const_cast<void *>(targetArray->buffers[0]));
6177 3 : if (nOffset == 0)
6178 : {
6179 3 : memcpy(targetNull, sourceNull, (nLength + 7) / 8);
6180 : }
6181 : else
6182 : {
6183 0 : memset(targetNull, 0, (nLength + 7) / 8);
6184 0 : for (size_t i = 0; i < nLength; ++i)
6185 : {
6186 0 : if ((sourceNull[(i + nOffset) / 8] >> ((i + nOffset) % 8)) &
6187 : 1)
6188 0 : targetNull[i / 8] |= (1 << (i % 8));
6189 : }
6190 : }
6191 : }
6192 : }
6193 :
6194 : // Allocate offset buffer
6195 36 : targetArray->buffers[1] =
6196 18 : VSI_MALLOC_ALIGNED_AUTO_VERBOSE(sizeof(uint32_t) * (1 + nLength));
6197 :
6198 : // Allocate data (WKB) buffer
6199 18 : constexpr size_t DEFAULT_WKB_SIZE = 100;
6200 18 : uint32_t nInitialCapacity = static_cast<uint32_t>(std::min<size_t>(
6201 18 : std::numeric_limits<int32_t>::max(), DEFAULT_WKB_SIZE * nLength));
6202 18 : targetArray->buffers[2] = VSI_MALLOC_ALIGNED_AUTO_VERBOSE(nInitialCapacity);
6203 :
6204 : // Check buffers have been allocated
6205 18 : if ((sourceArray->null_count && sourceNull && !targetNull) ||
6206 18 : targetArray->buffers[1] == nullptr ||
6207 18 : targetArray->buffers[2] == nullptr)
6208 : {
6209 0 : targetArray->release(targetArray);
6210 0 : return nullptr;
6211 : }
6212 :
6213 36 : OGRArrowLayerAppendBuffer oOGRAppendBuffer(targetArray, nInitialCapacity);
6214 18 : OGRWKTToWKBTranslator oTranslator(oOGRAppendBuffer);
6215 :
6216 18 : const auto sourceOffsets =
6217 18 : static_cast<const SourceOffset *>(sourceArray->buffers[1]) + nOffset;
6218 18 : auto sourceBytes =
6219 18 : static_cast<char *>(const_cast<void *>(sourceArray->buffers[2]));
6220 18 : auto targetOffsets =
6221 18 : static_cast<uint32_t *>(const_cast<void *>(targetArray->buffers[1]));
6222 159 : for (size_t i = 0; i < nLength; ++i)
6223 : {
6224 141 : targetOffsets[i] = static_cast<uint32_t>(oOGRAppendBuffer.GetSize());
6225 :
6226 141 : if (targetNull && ((targetNull[i / 8] >> (i % 8)) & 1) == 0)
6227 : {
6228 3 : continue;
6229 : }
6230 :
6231 276 : const size_t nWKBSize = oTranslator.TranslateWKT(
6232 138 : sourceBytes + sourceOffsets[i],
6233 138 : static_cast<size_t>(sourceOffsets[i + 1] - sourceOffsets[i]),
6234 138 : sourceOffsets[i + 1] < sourceOffsets[nLength]);
6235 138 : if (nWKBSize == static_cast<size_t>(-1))
6236 : {
6237 0 : targetArray->release(targetArray);
6238 0 : return nullptr;
6239 : }
6240 : }
6241 18 : targetOffsets[nLength] = static_cast<uint32_t>(oOGRAppendBuffer.GetSize());
6242 :
6243 18 : return targetArray;
6244 : }
6245 :
6246 : /************************************************************************/
6247 : /* TestCapability() */
6248 : /************************************************************************/
6249 :
6250 913 : inline int OGRArrowLayer::TestCapability(const char *pszCap) const
6251 : {
6252 :
6253 913 : if (EQUAL(pszCap, OLCStringsAsUTF8))
6254 505 : return true;
6255 :
6256 589 : else if (EQUAL(pszCap, OLCFastGetArrowStream) &&
6257 181 : !UseRecordBatchBaseImplementation())
6258 : {
6259 181 : return true;
6260 : }
6261 :
6262 227 : if (EQUAL(pszCap, OLCFastGetExtent))
6263 : {
6264 31 : OGREnvelope sEnvelope;
6265 48 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
6266 : {
6267 31 : if (!FastGetExtent(i, &sEnvelope))
6268 14 : return false;
6269 : }
6270 17 : return true;
6271 : }
6272 :
6273 196 : if (EQUAL(pszCap, OLCFastGetExtent3D))
6274 : {
6275 19 : OGREnvelope3D sEnvelope;
6276 20 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
6277 : {
6278 19 : if (!FastGetExtent3D(i, &sEnvelope))
6279 18 : return false;
6280 : }
6281 1 : return true;
6282 : }
6283 :
6284 177 : return false;
6285 : }
6286 :
6287 : #if defined(__clang__)
6288 : #pragma clang diagnostic pop
6289 : #endif
6290 :
6291 : #endif /* OGARROWLAYER_HPP_INCLUDED */
|