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