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