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