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