QGIS API Documentation 3.99.0-Master (21b3aa880ba)
Loading...
Searching...
No Matches
qgsarrowiterator.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsarrowiterator.cpp
3 ---------------------
4 begin : November 2025
5 copyright : (C) 2025 by Dewey Dunnington
6 email : dewey at dunnington dot ca
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsarrowiterator.h"
17
18#include <nlohmann/json.hpp>
19
20#include "nanoarrow/nanoarrow.hpp"
21#include "qgsfeatureiterator.h"
22#include "qgsvectorlayer.h"
23
24#define QGIS_NANOARROW_THROW_NOT_OK_ERR( expr, err ) \
25 do \
26 { \
27 const int ec = ( expr ); \
28 if ( ec != NANOARROW_OK ) \
29 { \
30 throw QgsException( QStringLiteral( "nanoarrow error (%1): %2" ).arg( ec ).arg( QString::fromUtf8( ( err )->message ) ) ); \
31 } \
32 } while ( 0 )
33
34#define QGIS_NANOARROW_THROW_NOT_OK( expr ) \
35 do \
36 { \
37 const int ec = ( expr ); \
38 if ( ec != NANOARROW_OK ) \
39 { \
40 throw QgsException( QStringLiteral( "nanoarrow error (%1)" ).arg( ec ) ); \
41 } \
42 } while ( 0 )
43
44
47
49{
50 mGeometryColumnName = geometryColumnName;
51}
52
54{
55 return mGeometryColumnName;
56}
57
60
62{
63 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaDeepCopy( &other.mSchema, &mSchema ) );
64 mGeometryColumnIndex = other.mGeometryColumnIndex;
65}
66
68{
69 if ( mSchema.release )
70 {
71 ArrowSchemaRelease( &mSchema );
72 }
73 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaDeepCopy( &other.mSchema, &mSchema ) );
74 mGeometryColumnIndex = other.mGeometryColumnIndex;
75 return *this;
76}
77
79{
80 if ( mSchema.release )
81 {
82 ArrowSchemaRelease( &mSchema );
83 }
84}
85
86struct ArrowSchema *QgsArrowSchema::schema()
87{
88 return &mSchema;
89}
90
91const struct ArrowSchema *QgsArrowSchema::schema() const
92{
93 return &mSchema;
94}
95
96unsigned long long QgsArrowSchema::cSchemaAddress() const
97{
98 return reinterpret_cast<unsigned long long>( &mSchema );
99}
100
101void QgsArrowSchema::exportToAddress( unsigned long long otherAddress )
102{
103 struct ArrowSchema *otherArrowSchema = reinterpret_cast<struct ArrowSchema *>( otherAddress );
104 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaDeepCopy( &mSchema, otherArrowSchema ) );
105}
106
108{
109 return mSchema.release;
110}
111
112int QgsArrowSchema::geometryColumnIndex() const { return mGeometryColumnIndex; }
113
115
117{
118 if ( mArray.release )
119 {
120 ArrowArrayRelease( &mArray );
121 }
122
123 ArrowArrayMove( other.array(), &mArray );
124}
125
127{
128 if ( this != &other )
129 {
130 ArrowArrayMove( other.array(), &mArray );
131 }
132
133 return *this;
134}
135
137{
138 if ( mArray.release )
139 {
140 ArrowArrayRelease( &mArray );
141 }
142}
143
144struct ArrowArray *QgsArrowArray::array()
145{
146 return &mArray;
147}
148
149const struct ArrowArray *QgsArrowArray::array() const
150{
151 return &mArray;
152}
153
154unsigned long long QgsArrowArray::cArrayAddress() const
155{
156 return reinterpret_cast<unsigned long long>( &mArray );
157}
158
159void QgsArrowArray::exportToAddress( unsigned long long otherAddress )
160{
161 struct ArrowArray *otherArrowArray = reinterpret_cast<struct ArrowArray *>( otherAddress );
162 ArrowArrayMove( &mArray, otherArrowArray );
163}
164
166{
167 return mArray.release;
168}
169
170namespace
171{
172
173
174 void inferGeometry( struct ArrowSchema *col, const QString &name, const QgsCoordinateReferenceSystem &crs )
175 {
176 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetName( col, name.toUtf8().constData() ) );
177 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetType( col, NANOARROW_TYPE_BINARY ) );
178
179 std::string crsString = crs.toJsonString();
180 std::string geoArrowMetadata;
181 if ( crsString.empty() )
182 {
183 geoArrowMetadata = "{}";
184 }
185 else
186 {
187 geoArrowMetadata = R"({"crs":)" + crsString + R"(})";
188 }
189
190 nanoarrow::UniqueBuffer metadataKv;
191 QGIS_NANOARROW_THROW_NOT_OK( ArrowMetadataBuilderInit( metadataKv.get(), nullptr ) );
192 QGIS_NANOARROW_THROW_NOT_OK( ArrowMetadataBuilderAppend( metadataKv.get(), ArrowCharView( "ARROW:extension:name" ), ArrowCharView( "geoarrow.wkb" ) ) );
193 QGIS_NANOARROW_THROW_NOT_OK( ArrowMetadataBuilderAppend( metadataKv.get(), ArrowCharView( "ARROW:extension:metadata" ), ArrowCharView( geoArrowMetadata.c_str() ) ) );
194 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetMetadata( col, reinterpret_cast<char *>( metadataKv->data ) ) );
195 }
196
197 void appendGeometry( const QgsFeature &feature, struct ArrowArray *col )
198 {
199 if ( !feature.hasGeometry() )
200 {
201 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayAppendNull( col, 1 ) );
202 return;
203 }
204
205 const QByteArray wkb = feature.geometry().asWkb( QgsAbstractGeometry::FlagExportTrianglesAsPolygons );
206 struct ArrowBufferView v;
207 v.data.data = wkb.data();
208 v.size_bytes = static_cast<int64_t>( wkb.size() );
209 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayAppendBytes( col, v ) );
210 }
211
212 void inferMetaType( const QMetaType::Type metaType, struct ArrowSchema *col, const QString &fieldName )
213 {
214 switch ( metaType )
215 {
216 case QMetaType::Bool:
217 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetType( col, NANOARROW_TYPE_BOOL ) );
218 return;
219 case QMetaType::QChar:
220 case QMetaType::SChar:
221 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetType( col, NANOARROW_TYPE_INT8 ) );
222 return;
223 case QMetaType::UChar:
224 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetType( col, NANOARROW_TYPE_UINT8 ) );
225 return;
226 case QMetaType::Short:
227 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetType( col, NANOARROW_TYPE_INT16 ) );
228 return;
229 case QMetaType::UShort:
230 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetType( col, NANOARROW_TYPE_UINT16 ) );
231 return;
232 case QMetaType::Int:
233 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetType( col, NANOARROW_TYPE_INT32 ) );
234 return;
235 case QMetaType::UInt:
236 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetType( col, NANOARROW_TYPE_UINT32 ) );
237 return;
238 case QMetaType::Long:
239 case QMetaType::LongLong:
240 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetType( col, NANOARROW_TYPE_INT64 ) );
241 return;
242 case QMetaType::ULong:
243 case QMetaType::ULongLong:
244 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetType( col, NANOARROW_TYPE_UINT64 ) );
245 return;
246 case QMetaType::Float:
247 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetType( col, NANOARROW_TYPE_FLOAT ) );
248 return;
249 case QMetaType::Double:
250 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetType( col, NANOARROW_TYPE_DOUBLE ) );
251 return;
252 case QMetaType::QString:
253 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetType( col, NANOARROW_TYPE_STRING ) );
254 return;
255 case QMetaType::QByteArray:
256 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetType( col, NANOARROW_TYPE_BINARY ) );
257 return;
258 case QMetaType::QDate:
259 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetType( col, NANOARROW_TYPE_DATE32 ) );
260 return;
261 case QMetaType::QTime:
262 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetTypeDateTime( col, NANOARROW_TYPE_TIME32, NANOARROW_TIME_UNIT_MILLI, nullptr ) );
263 return;
264 case QMetaType::QDateTime:
265 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetTypeDateTime( col, NANOARROW_TYPE_TIMESTAMP, NANOARROW_TIME_UNIT_MILLI, "UTC" ) );
266 return;
267 case QMetaType::QStringList:
268 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetType( col, NANOARROW_TYPE_LIST ) );
269 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetType( col->children[0], NANOARROW_TYPE_STRING ) );
270 return;
271 default:
272 throw QgsException( QStringLiteral( "QgsArrowIterator can't infer field type '%1' for field '%2'" ).arg( QMetaType::typeName( metaType ) ).arg( fieldName ) );
273 }
274 }
275
276 void inferField( const QgsField &field, struct ArrowSchema *col )
277 {
278 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetName( col, field.name().toUtf8().constData() ) );
279 switch ( field.type() )
280 {
281 case QMetaType::QVariantList:
282 QGIS_NANOARROW_THROW_NOT_OK( ArrowSchemaSetType( col, NANOARROW_TYPE_LIST ) );
283 inferMetaType( field.subType(), col->children[0], field.name() );
284 break;
285 default:
286 inferMetaType( field.type(), col, field.name() );
287 break;
288 }
289 }
290
291 void appendVariant( const QVariant &v, struct ArrowArray *col, struct ArrowSchemaView &columnTypeView, struct ArrowSchemaView &columnListTypeView )
292 {
293 if ( QgsVariantUtils::isNull( v ) )
294 {
295 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayAppendNull( col, 1 ) );
296 return;
297 }
298
299 switch ( columnTypeView.type )
300 {
301 case NANOARROW_TYPE_BOOL:
302 if ( v.canConvert( QMetaType::Bool ) )
303 {
304 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayAppendInt( col, v.toBool() ) );
305 return;
306 }
307 break;
308 case NANOARROW_TYPE_UINT8:
309 case NANOARROW_TYPE_UINT16:
310 case NANOARROW_TYPE_UINT32:
311 case NANOARROW_TYPE_UINT64:
312 if ( v.canConvert( QMetaType::ULongLong ) )
313 {
314 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayAppendUInt( col, v.toULongLong() ) );
315 return;
316 }
317 break;
318 case NANOARROW_TYPE_INT8:
319 case NANOARROW_TYPE_INT16:
320 case NANOARROW_TYPE_INT32:
321 case NANOARROW_TYPE_INT64:
322 if ( v.canConvert( QMetaType::LongLong ) )
323 {
324 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayAppendInt( col, v.toLongLong() ) );
325 return;
326 }
327 break;
328 case NANOARROW_TYPE_HALF_FLOAT:
329 case NANOARROW_TYPE_FLOAT:
330 case NANOARROW_TYPE_DOUBLE:
331 if ( v.canConvert( QMetaType::Double ) )
332 {
333 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayAppendDouble( col, v.toDouble() ) );
334 return;
335 }
336 break;
337 case NANOARROW_TYPE_STRING:
338 case NANOARROW_TYPE_LARGE_STRING:
339 case NANOARROW_TYPE_STRING_VIEW:
340 {
341 if ( v.canConvert( QMetaType::QString ) )
342 {
343 const QByteArray string = v.toString().toUtf8();
344 struct ArrowBufferView bytesView;
345 bytesView.data.data = string.constData();
346 bytesView.size_bytes = static_cast<int64_t>( string.size() );
347 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayAppendBytes( col, bytesView ) );
348 return;
349 }
350 break;
351 }
352
353 case NANOARROW_TYPE_BINARY:
354 case NANOARROW_TYPE_LARGE_BINARY:
355 case NANOARROW_TYPE_BINARY_VIEW:
356 case NANOARROW_TYPE_FIXED_SIZE_BINARY:
357 {
358 if ( v.canConvert( QMetaType::QByteArray ) )
359 {
360 const QByteArray bytes = v.toByteArray();
361 struct ArrowBufferView bytesView;
362 bytesView.data.data = bytes.data();
363 bytesView.size_bytes = static_cast<int64_t>( bytes.size() );
364 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayAppendBytes( col, bytesView ) );
365 return;
366 }
367 break;
368 }
369
370 case NANOARROW_TYPE_DATE32:
371 {
372 if ( v.canConvert( QMetaType::QDate ) )
373 {
374 static QDate epoch = QDate( 1970, 1, 1 );
375 int64_t daysSinceEpoch = epoch.daysTo( v.toDate() );
376 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayAppendInt( col, daysSinceEpoch ) );
377 return;
378 }
379 break;
380 }
381
382 case NANOARROW_TYPE_DATE64:
383 {
384 if ( v.canConvert( QMetaType::QDate ) )
385 {
386 static QDate epoch = QDate( 1970, 1, 1 );
387 int64_t daysSinceEpoch = epoch.daysTo( v.toDate() );
388 int64_t msSinceEpoch = daysSinceEpoch * 24 * 60 * 60 * 1000;
389 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayAppendInt( col, msSinceEpoch ) );
390 return;
391 }
392 break;
393 }
394
395 case NANOARROW_TYPE_TIMESTAMP:
396 {
397 if ( v.canConvert( QMetaType::QDateTime ) )
398 {
399 const QDateTime dateTime = v.toDateTime().toUTC();
400 switch ( columnTypeView.time_unit )
401 {
402 case NANOARROW_TIME_UNIT_SECOND:
403 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayAppendInt( col, dateTime.toSecsSinceEpoch() ) );
404 return;
405 case NANOARROW_TIME_UNIT_MILLI:
406 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayAppendInt( col, dateTime.toMSecsSinceEpoch() ) );
407 return;
408 case NANOARROW_TIME_UNIT_MICRO:
409 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayAppendInt( col, dateTime.toMSecsSinceEpoch() * 1000 ) );
410 return;
411 case NANOARROW_TIME_UNIT_NANO:
412 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayAppendInt( col, dateTime.toMSecsSinceEpoch() * 1000 * 1000 ) );
413 return;
414 }
415 }
416
417 break;
418 }
419 case NANOARROW_TYPE_TIME32:
420 case NANOARROW_TYPE_TIME64:
421 {
422 if ( v.canConvert( QMetaType::QTime ) )
423 {
424 const QTime time = v.toTime();
425 switch ( columnTypeView.time_unit )
426 {
427 case NANOARROW_TIME_UNIT_SECOND:
428 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayAppendInt( col, time.msecsSinceStartOfDay() / 1000 ) );
429 return;
430 case NANOARROW_TIME_UNIT_MILLI:
431 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayAppendInt( col, time.msecsSinceStartOfDay() ) );
432 return;
433 case NANOARROW_TIME_UNIT_MICRO:
434 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayAppendInt( col, static_cast<int64_t>( time.msecsSinceStartOfDay() ) * 1000 ) );
435 return;
436 case NANOARROW_TIME_UNIT_NANO:
437 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayAppendInt( col, static_cast<int64_t>( time.msecsSinceStartOfDay() ) * 1000 * 1000 ) );
438 return;
439 }
440 }
441
442 break;
443 }
444
445 case NANOARROW_TYPE_LIST:
446 case NANOARROW_TYPE_FIXED_SIZE_LIST:
447 case NANOARROW_TYPE_LARGE_LIST:
448 case NANOARROW_TYPE_LIST_VIEW:
449 case NANOARROW_TYPE_LARGE_LIST_VIEW:
450 {
451 if ( v.canConvert( QMetaType::QVariantList ) )
452 {
453 const QVariantList variantList = v.toList();
454 struct ArrowSchemaView dummyListType {};
455 for ( const QVariant &item : variantList )
456 {
457 appendVariant( item, col->children[0], columnListTypeView, dummyListType );
458 }
459
460 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayFinishElement( col ) );
461 return;
462 }
463 break;
464 }
465 default:
466 break;
467 }
468
469 throw QgsException( QStringLiteral( "Can't convert variant of type '%1' to Arrow type '%2'" ).arg( v.typeName() ).arg( ArrowTypeString( columnTypeView.type ) ) );
470 }
471
472} //namespace
473
475 : mFeatureIterator( featureIterator )
476{
477}
478
480{
481 if ( !schema.isValid() )
482 {
483 throw QgsException( QStringLiteral( "Invalid or null ArrowSchema provided" ) );
484 }
485
486 mSchema = schema;
487}
488
489
491{
492 if ( n < 1 )
493 {
494 throw QgsException( QStringLiteral( "QgsArrowIterator can't iterate over less than one feature" ) );
495 }
496
497 if ( !mSchema.isValid() )
498 {
499 throw QgsException( QStringLiteral( "QgsArrowIterator schema not set" ) );
500 }
501
502 // Check the schema and cache a few things about it before we loop over features.
503 // This could also be done when setting the schema (although the struct ArrowSchemaView
504 // would have to be opaque in the header if this were cached as a class member).
505 const struct ArrowSchema *schema = mSchema.schema();
506
507 struct ArrowError error {};
508
509 // Check that the top-level schema is a struct
510 struct ArrowSchemaView schemaView;
511 QGIS_NANOARROW_THROW_NOT_OK_ERR( ArrowSchemaViewInit( &schemaView, schema, &error ), &error );
512 if ( schemaView.type != NANOARROW_TYPE_STRUCT )
513 {
514 throw QgsException( QStringLiteral( "QgsArrowIterator expected requested schema as struct but got '%1'" ).arg( ArrowTypeString( schemaView.type ) ) );
515 }
516
517 std::vector<QString> columnNames( schema->n_children );
518 std::vector<struct ArrowSchemaView> colTypeViews( schema->n_children );
519 std::vector<struct ArrowSchemaView> colListTypeViews( schema->n_children );
520 for ( int64_t i = 0; i < schema->n_children; i++ )
521 {
522 // Parse the column schema
523 columnNames[i] = QString( schema->children[i]->name != nullptr ? schema->children[i]->name : QString() );
524 QGIS_NANOARROW_THROW_NOT_OK_ERR( ArrowSchemaViewInit( &colTypeViews[i], schema->children[i], &error ), &error );
525
526 // Parse the column list type if applicable
527 switch ( colTypeViews[i].type )
528 {
529 case NANOARROW_TYPE_LIST:
530 case NANOARROW_TYPE_FIXED_SIZE_LIST:
531 case NANOARROW_TYPE_LARGE_LIST:
532 case NANOARROW_TYPE_LIST_VIEW:
533 case NANOARROW_TYPE_LARGE_LIST_VIEW:
534 {
535 struct ArrowSchemaView childView;
536 QGIS_NANOARROW_THROW_NOT_OK_ERR( ArrowSchemaViewInit( &childView, schema->children[i]->children[0], &error ), &error );
537 colListTypeViews[i] = std::move( childView );
538 break;
539 }
540 default:
541 colListTypeViews[i] = ArrowSchemaView {};
542 break;
543 }
544 }
545
546 // Create the output array
547 nanoarrow::UniqueArray tmp;
548 QGIS_NANOARROW_THROW_NOT_OK_ERR( ArrowArrayInitFromSchema( tmp.get(), schema, &error ), &error );
549 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayReserve( tmp.get(), n ) );
550 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayStartAppending( tmp.get() ) );
551
552 // Loop features
553 std::vector<int> featureAttributeIndex;
554 QgsFeature feature;
555 while ( n > 0 && mFeatureIterator.nextFeature( feature ) )
556 {
557 --n;
558
559 // Cache the attribute index per output schema index on the first feature
560 if ( featureAttributeIndex.empty() )
561 {
562 for ( int64_t i = 0; i < schema->n_children; i++ )
563 {
564 featureAttributeIndex.push_back( feature.fieldNameIndex( columnNames[i] ) );
565 }
566 }
567
568 // Loop over the output schema fields and append the appropriate attribute from the
569 // feature (or geometry, or null if the feature does not contain that field).
570 for ( int64_t i = 0; i < schema->n_children; i++ )
571 {
572 int attributeIndex = featureAttributeIndex[i];
573 struct ArrowArray *columnArray = tmp->children[i];
574
575 if ( i == mSchema.geometryColumnIndex() )
576 {
577 appendGeometry( feature, columnArray );
578 }
579 else if ( attributeIndex >= 0 && attributeIndex < feature.attributeCount() )
580 {
581 appendVariant( feature.attribute( attributeIndex ), columnArray, colTypeViews[i], colListTypeViews[i] );
582 }
583 else
584 {
585 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayAppendNull( columnArray, 1 ) );
586 }
587 }
588
589 QGIS_NANOARROW_THROW_NOT_OK( ArrowArrayFinishElement( tmp.get() ) );
590 }
591
592 QGIS_NANOARROW_THROW_NOT_OK_ERR( ArrowArrayFinishBuildingDefault( tmp.get(), &error ), &error );
593
594 QgsArrowArray out;
595 if ( tmp->length > 0 )
596 {
597 ArrowArrayMove( tmp.get(), out.array() );
598 }
599 return out;
600}
601
603{
604 bool layerHasGeometry = layer.isSpatial();
605 if ( layerHasGeometry && options.geometryColumnName().isEmpty() )
606 {
607 QgsArrowInferSchemaOptions optionsClone( options );
608 optionsClone.setGeometryColumnName( layer.dataProvider()->geometryColumnName() );
609 return inferSchema( layer.fields(), layerHasGeometry, layer.crs(), optionsClone );
610 }
611 else
612 {
613 return inferSchema( layer.fields(), layerHasGeometry, layer.crs(), options );
614 }
615}
616
617
619{
620 QgsArrowSchema out;
621 QgisPrivateArrowSchemaInit( out.schema() );
622 QGIS_NANOARROW_THROW_NOT_OK( QgisPrivateArrowSchemaSetTypeStruct( out.schema(), fields.count() + hasGeometry ) );
623 for ( int i = 0; i < fields.count(); i++ )
624 {
625 inferField( fields.field( i ), out.schema()->children[i] );
626 }
627
628 if ( hasGeometry )
629 {
630 QString geometryColumnName = options.geometryColumnName();
631 if ( geometryColumnName.isEmpty() )
632 {
633 geometryColumnName = QStringLiteral( "geometry" );
634 }
635
636 inferGeometry( out.schema()->children[fields.count()], geometryColumnName, crs );
637 out.setGeometryColumnIndex( fields.count() );
638 }
639
640 return out;
641}
@ FlagExportTrianglesAsPolygons
Triangles should be exported as polygon geometries.
Wrapper around an ArrowArray.
QgsArrowArray()=default
Construct invalid array holder.
struct ArrowArray * array()
Access the underlying ArrowArray from C++.
QgsArrowArray & operator=(QgsArrowArray &other)=delete
bool isValid() const
Returns true if this wrapper object holds a valid ArrowArray.
void exportToAddress(unsigned long long otherAddress)
Export this array to the address of an empty ArrowArray for export across boundaries.
unsigned long long cArrayAddress() const
Returns the address of the underlying ArrowArray for import or export across boundaries.
Options for inferring an ArrowSchema from a feature source.
void setGeometryColumnName(const QString &geometryColumnName)
Set the name that should be used to refer to the geometry column.
QgsArrowInferSchemaOptions()
Construct default options.
QString geometryColumnName() const
The name that should be used for a layer's geometry column.
static QgsArrowSchema inferSchema(const QgsVectorLayer &layer, const QgsArrowInferSchemaOptions &options=QgsArrowInferSchemaOptions())
Infer the QgsArrowSchema for a given QgsVectorLayer.
QgsArrowArray nextFeatures(int n)
Build an ArrowArray using the next n features (or fewer depending on the number of features remaining...
QgsArrowIterator()=default
Construct invalid iterator.
void setSchema(const QgsArrowSchema &schema)
Set the ArrowSchema for the output of all future batches.
Wrapper around an ArrowSchema.
int geometryColumnIndex() const
Returns the index of the column in this schema that should be populated with a feature geometry.
bool isValid() const
Returns true if this wrapper object holds a valid ArrowSchema.
unsigned long long cSchemaAddress() const
Returns the address of the underlying ArrowSchema for import or export across boundaries.
struct ArrowSchema * schema()
Access the underlying ArrowSchema from C++.
QgsArrowSchema & operator=(const QgsArrowSchema &other)
Assignment operator.
void exportToAddress(unsigned long long otherAddress)
Export this array to the address of an empty ArrowSchema for export across boundaries.
QgsArrowSchema()
Construct invalid schema holder.
void setGeometryColumnIndex(int geometryColumnIndex)
Set the index of the column in this schema that should be populated with a feature geometry.
Represents a coordinate reference system (CRS).
std::string toJsonString(bool multiline=false, int indentationWidth=4, const QString &schema=QString()) const
Returns a JSON string representation of this CRS.
Defines a QGIS exception class.
Wrapper for iterator of features from vector data provider or vector layer.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
int fieldNameIndex(const QString &fieldName) const
Utility method to get attribute index from name.
int attributeCount() const
Returns the number of attributes attached to the feature.
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:54
QMetaType::Type type
Definition qgsfield.h:61
QString name
Definition qgsfield.h:63
QMetaType::Type subType() const
If the field is a collection, gets its element's type.
Definition qgsfield.cpp:158
Container of fields for a vector layer.
Definition qgsfields.h:46
int count
Definition qgsfields.h:50
QgsField field(int fieldIdx) const
Returns the field at particular index (must be in range 0..N-1).
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const
Export the geometry to WKB.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:87
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
virtual QString geometryColumnName() const
Returns the name of the column storing geometry, if applicable.
Represents a vector layer which manages a vector based dataset.
bool isSpatial() const final
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
#define QGIS_NANOARROW_THROW_NOT_OK_ERR(expr, err)
#define QGIS_NANOARROW_THROW_NOT_OK(expr)