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