QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgsauxiliarystorage.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsauxiliarystorage.cpp - description
3 -------------------
4 begin : Aug 28, 2017
5 copyright : (C) 2017 by Paul Blottiere
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgsauxiliarystorage.h"
19
20#include <sqlite3.h>
21
22#include "qgsdiagramrenderer.h"
23#include "qgslogger.h"
25#include "qgsproject.h"
26#include "qgssqliteutils.h"
27#include "qgssymbollayer.h"
29
30#include <QFile>
31
32#include "moc_qgsauxiliarystorage.cpp"
33
34#define AS_JOINFIELD QStringLiteral( "ASPK" )
35#define AS_EXTENSION QStringLiteral( "qgd" )
36#define AS_JOINPREFIX QStringLiteral( "auxiliary_storage_" )
37
38typedef QVector<int> PalPropertyList;
39typedef QVector<int> SymbolPropertyList;
40
42{
43 static_cast< int >( QgsPalLayerSettings::Property::PositionX ),
44 static_cast< int >( QgsPalLayerSettings::Property::PositionY ),
45 static_cast< int >( QgsPalLayerSettings::Property::Show ),
47 static_cast< int >( QgsPalLayerSettings::Property::Family ),
48 static_cast< int >( QgsPalLayerSettings::Property::FontStyle ),
49 static_cast< int >( QgsPalLayerSettings::Property::Size ),
50 static_cast< int >( QgsPalLayerSettings::Property::Bold ),
51 static_cast< int >( QgsPalLayerSettings::Property::Italic ),
52 static_cast< int >( QgsPalLayerSettings::Property::Underline ),
53 static_cast< int >( QgsPalLayerSettings::Property::Color ),
54 static_cast< int >( QgsPalLayerSettings::Property::Strikeout ),
60 static_cast< int >( QgsPalLayerSettings::Property::Hali ),
61 static_cast< int >( QgsPalLayerSettings::Property::Vali ),
63 static_cast< int >( QgsPalLayerSettings::Property::MinScale ),
64 static_cast< int >( QgsPalLayerSettings::Property::MaxScale ),
68} ) )
70{
71 static_cast< int >( QgsSymbolLayer::Property::Angle ),
72 static_cast< int >( QgsSymbolLayer::Property::Offset )
73} ) )
74
75//
76// QgsAuxiliaryLayer
77//
78
79QgsAuxiliaryLayer::QgsAuxiliaryLayer( const QString &pkField, const QString &filename, const QString &table, QgsVectorLayer *vlayer )
80 : QgsVectorLayer( QStringLiteral( "%1|layername=%2" ).arg( filename, table ),
81 QStringLiteral( "%1_auxiliarystorage" ).arg( table ), QStringLiteral( "ogr" ) )
82 , mFileName( filename )
83 , mTable( table )
84 , mLayer( vlayer )
85{
86 // init join info
87 mJoinInfo.setPrefix( AS_JOINPREFIX );
88 mJoinInfo.setJoinLayer( this );
89 mJoinInfo.setJoinFieldName( AS_JOINFIELD );
90 mJoinInfo.setTargetFieldName( pkField );
91 mJoinInfo.setEditable( true );
92 mJoinInfo.setUpsertOnEdit( true );
93 mJoinInfo.setCascadedDelete( true );
94 mJoinInfo.setJoinFieldNamesBlockList( QStringList() << QStringLiteral( "rowid" ) ); // introduced by ogr provider
95}
96
98{
100 return new QgsAuxiliaryLayer( mJoinInfo.targetFieldName(), mFileName, target->id(), target );
101}
102
104{
105 const bool rc = deleteFeatures( allFeatureIds() );
107 startEditing();
108 return rc;
109}
110
112{
113 QgsVectorLayer *layer = QgsMemoryProviderUtils::createMemoryLayer( QStringLiteral( "auxiliary_layer" ), fields(), mLayer->wkbType(), mLayer->crs() );
114
115 const QString pkField = mJoinInfo.targetFieldName();
116 QgsFeature joinFeature;
117 QgsFeature targetFeature;
118 QgsFeatureIterator it = getFeatures();
119
120 layer->startEditing();
121 while ( it.nextFeature( joinFeature ) )
122 {
123 const QString filter = QgsExpression::createFieldEqualityExpression( pkField, joinFeature.attribute( AS_JOINFIELD ) );
124
125 QgsFeatureRequest request;
126 request.setFilterExpression( filter );
127
128 mLayer->getFeatures( request ).nextFeature( targetFeature );
129
130 if ( targetFeature.isValid() )
131 {
132 QgsFeature newFeature( joinFeature );
133 newFeature.setGeometry( targetFeature.geometry() );
134 layer->addFeature( newFeature );
135 }
136 }
137 layer->commitChanges();
138
139 return layer;
140}
141
143{
144 return mJoinInfo;
145}
146
147bool QgsAuxiliaryLayer::exists( const QgsPropertyDefinition &definition ) const
148{
149 return ( indexOfPropertyDefinition( definition ) >= 0 );
150}
151
153{
154 if ( ( definition.name().isEmpty() && definition.comment().isEmpty() ) || exists( definition ) )
155 return false;
156
157 const QgsField af = createAuxiliaryField( definition );
158 const bool rc = addAttribute( af );
159 updateFields();
160 mLayer->updateFields();
161
162 if ( rc )
163 {
164 const int auxIndex = indexOfPropertyDefinition( definition );
165 const int index = mLayer->fields().indexOf( nameFromProperty( definition, true ) );
166
167 if ( index >= 0 && auxIndex >= 0 )
168 {
169 if ( isHiddenProperty( auxIndex ) )
170 {
171 // update editor widget
172 const QgsEditorWidgetSetup setup = QgsEditorWidgetSetup( QStringLiteral( "Hidden" ), QVariantMap() );
173 setEditorWidgetSetup( auxIndex, setup );
174
175 // column is hidden
176 QgsAttributeTableConfig attrCfg = mLayer->attributeTableConfig();
177 attrCfg.update( mLayer->fields() );
178 QVector<QgsAttributeTableConfig::ColumnConfig> columns = attrCfg.columns();
179 QVector<QgsAttributeTableConfig::ColumnConfig>::iterator it;
180
181 for ( it = columns.begin(); it != columns.end(); ++it )
182 {
183 if ( it->name.compare( mLayer->fields().field( index ).name() ) == 0 )
184 it->hidden = true;
185 }
186
187 attrCfg.setColumns( columns );
188 mLayer->setAttributeTableConfig( attrCfg );
189 }
192 {
193 const QgsEditorWidgetSetup setup = QgsEditorWidgetSetup( QStringLiteral( "Color" ), QVariantMap() );
194 setEditorWidgetSetup( auxIndex, setup );
195 }
196
197 mLayer->setEditorWidgetSetup( index, editorWidgetSetup( auxIndex ) );
198 }
199 }
200
201 return rc;
202}
203
205{
206 QgsFields afields;
207
208 for ( int i = 2; i < fields().count(); i++ ) // ignore rowid and PK field
209 afields.append( createAuxiliaryField( fields().field( i ) ) );
210
211 return afields;
212}
213
215{
217 const bool rc = commitChanges();
218 startEditing();
219 return rc;
220}
221
223{
224 bool rc = false;
225
226 if ( isEditable() )
227 {
228 rc = commitChanges();
229 }
230
231 startEditing();
232
233 return rc;
234}
235
236int QgsAuxiliaryLayer::createProperty( QgsPalLayerSettings::Property property, QgsVectorLayer *layer, bool overwriteExisting )
237{
238 int index = -1;
239
240 if ( layer && layer->labeling() && layer->auxiliaryLayer() )
241 {
242 // property definition are identical whatever the provider id
243 const QgsPropertyDefinition def = QgsPalLayerSettings::propertyDefinitions()[static_cast< int >( property )];
244 const QString fieldName = nameFromProperty( def, true );
245
246 layer->auxiliaryLayer()->addAuxiliaryField( def );
247
248 if ( layer->auxiliaryLayer()->indexOfPropertyDefinition( def ) >= 0 )
249 {
250 const QStringList subProviderIds = layer->labeling()->subProviders();
251 for ( const QString &providerId : subProviderIds )
252 {
253 QgsPalLayerSettings *settings = new QgsPalLayerSettings( layer->labeling()->settings( providerId ) );
254
255 QgsPropertyCollection c = settings->dataDefinedProperties();
256
257 // is there an existing property?
258 const QgsProperty existingProperty = c.property( property );
259 if ( existingProperty.propertyType() == Qgis::PropertyType::Invalid
260 || ( existingProperty.propertyType() == Qgis::PropertyType::Field && existingProperty.field().isEmpty() )
261 || ( existingProperty.propertyType() == Qgis::PropertyType::Expression && existingProperty.expressionString().isEmpty() )
262 || overwriteExisting )
263 {
264 const QgsProperty prop = QgsProperty::fromField( fieldName );
265 c.setProperty( property, prop );
266 }
267 else
268 {
269 // build a new smart expression as coalesce("new aux field", 'the' || 'old' || 'expression')
270 const QgsProperty prop = QgsProperty::fromExpression( QStringLiteral( "coalesce(%1,%2)" ).arg( QgsExpression::quotedColumnRef( fieldName ),
271 existingProperty.asExpression() ) );
272 c.setProperty( property, prop );
273 }
274 settings->setDataDefinedProperties( c );
275
276 layer->labeling()->setSettings( settings, providerId );
277 }
278 }
279
280 index = layer->fields().lookupField( fieldName );
281 }
282
283 return index;
284}
285
286int QgsAuxiliaryLayer::createProperty( QgsDiagramLayerSettings::Property property, QgsVectorLayer *layer, bool overwriteExisting )
287{
288 int index = -1;
289
290 if ( layer && layer->diagramLayerSettings() && layer->auxiliaryLayer() )
291 {
292 const QgsPropertyDefinition def = QgsDiagramLayerSettings::propertyDefinitions()[static_cast<int>( property )];
293
294 if ( layer->auxiliaryLayer()->addAuxiliaryField( def ) )
295 {
296 const QString fieldName = nameFromProperty( def, true );
297
298 QgsDiagramLayerSettings settings( *layer->diagramLayerSettings() );
299
300 QgsPropertyCollection c = settings.dataDefinedProperties();
301 // is there an existing property?
302 const QgsProperty existingProperty = c.property( property );
303 if ( existingProperty.propertyType() == Qgis::PropertyType::Invalid || overwriteExisting )
304 {
305 const QgsProperty prop = QgsProperty::fromField( fieldName );
306 c.setProperty( property, prop );
307 }
308 else
309 {
310 // build a new smart expression as coalesce("new aux field", 'the' || 'old' || 'expression')
311 const QgsProperty prop = QgsProperty::fromExpression( QStringLiteral( "coalesce(%1,%2)" ).arg( QgsExpression::quotedColumnRef( fieldName ),
312 existingProperty.asExpression() ) );
313 c.setProperty( property, prop );
314 }
315 settings.setDataDefinedProperties( c );
316
317 layer->setDiagramLayerSettings( settings );
318 index = layer->fields().lookupField( fieldName );
319 }
320 }
321
322 return index;
323}
324
325int QgsAuxiliaryLayer::createProperty( QgsCallout::Property property, QgsVectorLayer *layer, bool overwriteExisting )
326{
327 int index = -1;
328
329 if ( layer && layer->labeling() && layer->labeling()->settings().callout() && layer->auxiliaryLayer() )
330 {
331 // property definition are identical whatever the provider id
332 const QgsPropertyDefinition def = QgsCallout::propertyDefinitions()[static_cast< int >( property )];
333 const QString fieldName = nameFromProperty( def, true );
334
335 layer->auxiliaryLayer()->addAuxiliaryField( def );
336
337 if ( layer->auxiliaryLayer()->indexOfPropertyDefinition( def ) >= 0 )
338 {
339 const QStringList subProviderIds = layer->labeling()->subProviders();
340 for ( const QString &providerId : subProviderIds )
341 {
342 QgsPalLayerSettings *settings = new QgsPalLayerSettings( layer->labeling()->settings( providerId ) );
343 if ( settings->callout() )
344 {
345 QgsPropertyCollection c = settings->callout()->dataDefinedProperties();
346 // is there an existing property?
347 const QgsProperty existingProperty = c.property( property );
348 if ( existingProperty.propertyType() == Qgis::PropertyType::Invalid || overwriteExisting )
349 {
350 const QgsProperty prop = QgsProperty::fromField( fieldName );
351 c.setProperty( property, prop );
352 }
353 else
354 {
355 // build a new smart expression as coalesce("new aux field", 'the' || 'old' || 'expression')
356 const QgsProperty prop = QgsProperty::fromExpression( QStringLiteral( "coalesce(%1,%2)" ).arg( QgsExpression::quotedColumnRef( fieldName ),
357 existingProperty.asExpression() ) );
358 c.setProperty( property, prop );
359 }
360 settings->callout()->setDataDefinedProperties( c );
361 }
362 layer->labeling()->setSettings( settings, providerId );
363 }
364 }
365
366 index = layer->fields().lookupField( fieldName );
367 }
368
369 return index;
370}
371
372bool QgsAuxiliaryLayer::isHiddenProperty( int index ) const
373{
374 bool hidden = false;
375 const QgsPropertyDefinition def = propertyDefinitionFromIndex( index );
376
377 if ( def.origin().compare( QLatin1String( "labeling" ) ) == 0 )
378 {
379 const PalPropertyList &palProps = *palHiddenProperties();
380 for ( const int p : palProps )
381 {
382 const QString propName = QgsPalLayerSettings::propertyDefinitions()[ p ].name();
383 if ( propName.compare( def.name() ) == 0 )
384 {
385 hidden = true;
386 break;
387 }
388 }
389 }
390 else if ( def.origin().compare( QLatin1String( "symbol" ) ) == 0 )
391 {
392 const SymbolPropertyList &symbolProps = *symbolHiddenProperties();
393 for ( int p : symbolProps )
394 {
395 const QString propName = QgsSymbolLayer::propertyDefinitions()[ p ].name();
396 if ( propName.compare( def.name() ) == 0 )
397 {
398 hidden = true;
399 break;
400 }
401 }
402 }
403
404 return hidden;
405}
406
407int QgsAuxiliaryLayer::propertyFromIndex( int index ) const
408{
409 int p = -1;
410 const QgsPropertyDefinition aDef = propertyDefinitionFromIndex( index );
411
412 if ( aDef.origin().compare( QLatin1String( "labeling" ) ) == 0 )
413 {
415 QgsPropertiesDefinition::const_iterator it = defs.constBegin();
416 for ( ; it != defs.constEnd(); ++it )
417 {
418 if ( it->name().compare( aDef.name(), Qt::CaseInsensitive ) == 0 )
419 {
420 p = it.key();
421 break;
422 }
423 }
424 }
425 else if ( aDef.origin().compare( QLatin1String( "symbol" ) ) == 0 )
426 {
428 QgsPropertiesDefinition::const_iterator it = defs.constBegin();
429 for ( ; it != defs.constEnd(); ++it )
430 {
431 if ( it->name().compare( aDef.name(), Qt::CaseInsensitive ) == 0 )
432 {
433 p = it.key();
434 break;
435 }
436 }
437 }
438 else if ( aDef.origin().compare( QLatin1String( "diagram" ) ) == 0 )
439 {
441 QgsPropertiesDefinition::const_iterator it = defs.constBegin();
442 for ( ; it != defs.constEnd(); ++it )
443 {
444 if ( it->name().compare( aDef.name(), Qt::CaseInsensitive ) == 0 )
445 {
446 p = it.key();
447 break;
448 }
449 }
450 }
451
452 return p;
453}
454
456{
457 return propertyDefinitionFromField( fields().field( index ) );
458}
459
461{
462 return fields().indexOf( nameFromProperty( def ) );
463}
464
465QString QgsAuxiliaryLayer::nameFromProperty( const QgsPropertyDefinition &def, bool joined )
466{
467 QString fieldName = def.origin();
468
469 if ( !def.name().isEmpty() )
470 fieldName = QStringLiteral( "%1_%2" ).arg( fieldName, def.name().toLower() );
471
472 if ( !def.comment().isEmpty() )
473 fieldName = QStringLiteral( "%1_%2" ).arg( fieldName, def.comment() );
474
475 if ( joined )
476 fieldName = QStringLiteral( "%1%2" ).arg( AS_JOINPREFIX, fieldName );
477
478 return fieldName;
479}
480
482{
483 QgsField afield;
484
485 if ( !def.name().isEmpty() || !def.comment().isEmpty() )
486 {
487 QMetaType::Type type = QMetaType::Type::UnknownType;
488 QString typeName;
489 int len( 0 ), precision( 0 );
490 switch ( def.dataType() )
491 {
493 type = QMetaType::Type::QString;
494 len = 50;
495 typeName = QStringLiteral( "String" );
496 break;
498 type = QMetaType::Type::Double;
499 len = 0;
500 precision = 0;
501 typeName = QStringLiteral( "Real" );
502 break;
504 type = QMetaType::Type::Int; // sqlite does not have a bool type
505 typeName = QStringLiteral( "Integer" );
506 break;
507 }
508
509 afield.setType( type );
510 afield.setName( nameFromProperty( def ) );
511 afield.setTypeName( typeName );
512 afield.setLength( len );
513 afield.setPrecision( precision );
514 }
515
516 return afield;
517}
518
520{
521 QgsPropertyDefinition def;
522 const QStringList parts = f.name().split( '_' );
523
524 if ( parts.size() <= 1 )
525 return def;
526
527 const QString origin = parts[0];
528 const QString propertyName = parts[1];
529
530 if ( origin.compare( QLatin1String( "labeling" ), Qt::CaseInsensitive ) == 0 )
531 {
533 for ( auto it = props.constBegin(); it != props.constEnd(); ++it )
534 {
535 if ( it.value().name().compare( propertyName, Qt::CaseInsensitive ) == 0 )
536 {
537 def = it.value();
538 if ( parts.size() >= 3 )
539 def.setComment( parts.mid( 2 ).join( '_' ) );
540 break;
541 }
542 }
543 }
544 else if ( origin.compare( QLatin1String( "symbol" ), Qt::CaseInsensitive ) == 0 )
545 {
547 for ( auto it = props.constBegin(); it != props.constEnd(); ++it )
548 {
549 if ( it.value().name().compare( propertyName, Qt::CaseInsensitive ) == 0 )
550 {
551 def = it.value();
552 if ( parts.size() >= 3 )
553 def.setComment( parts.mid( 2 ).join( '_' ) );
554 break;
555 }
556 }
557 }
558 else if ( origin.compare( QLatin1String( "diagram" ), Qt::CaseInsensitive ) == 0 )
559 {
561 for ( auto it = props.constBegin(); it != props.constEnd(); ++it )
562 {
563 if ( it.value().name().compare( propertyName, Qt::CaseInsensitive ) == 0 )
564 {
565 def = it.value();
566 if ( parts.size() >= 3 )
567 def.setComment( parts.mid( 2 ).join( '_' ) );
568 break;
569 }
570 }
571 }
572 else
573 {
574 def.setOrigin( origin );
575 def.setName( propertyName );
576 switch ( f.type() )
577 {
578 case QMetaType::Type::Double:
580 break;
581
582 case QMetaType::Type::Bool:
584 break;
585
586 case QMetaType::Type::QString:
587 default:
589 break;
590 }
591
592 if ( parts.size() >= 3 )
593 def.setComment( parts.mid( 2 ).join( '_' ) );
594 }
595
596 return def;
597}
598
600{
601 const QgsPropertyDefinition def = propertyDefinitionFromField( field );
602 QgsField afield;
603
604 if ( !def.name().isEmpty() || !def.comment().isEmpty() )
605 {
606 afield = createAuxiliaryField( def );
607 afield.setTypeName( field.typeName() );
608 }
609
610 return afield;
611}
612
613//
614// QgsAuxiliaryStorage
615//
616
617QgsAuxiliaryStorage::QgsAuxiliaryStorage( const QgsProject &project, bool copy )
618 : mCopy( copy )
619{
620 initTmpFileName();
621
622 if ( !project.absoluteFilePath().isEmpty() )
623 {
624 mFileName = filenameForProject( project );
625 }
626
627 open( mFileName );
628}
629
630QgsAuxiliaryStorage::QgsAuxiliaryStorage( const QString &filename, bool copy )
631 : mFileName( filename )
632 , mCopy( copy )
633{
634 initTmpFileName();
635
636 open( filename );
637}
638
640{
641 if ( QFile::exists( mTmpFileName ) )
642 QFile::remove( mTmpFileName );
643}
644
646{
647 return mValid;
648}
649
650QString QgsAuxiliaryStorage::fileName() const
651{
652 return mFileName;
653}
654
655bool QgsAuxiliaryStorage::save() const
656{
657 if ( mFileName.isEmpty() )
658 {
659 // only a saveAs is available on a new database
660 return false;
661 }
662 else if ( mCopy )
663 {
664 if ( QFile::exists( mFileName ) )
665 QFile::remove( mFileName );
666
667 return QFile::copy( mTmpFileName, mFileName );
668 }
669 else
670 {
671 // if the file is not empty the copy mode is not activated, then we're
672 // directly working on the database since the beginning (no savepoints
673 // /rollback for now)
674 return true;
675 }
676}
677
679{
680 QgsAuxiliaryLayer *alayer = nullptr;
681
682 if ( mValid && layer )
683 {
684 const QString table( layer->id() );
685 sqlite3_database_unique_ptr database;
686 database = openDB( currentFileName() );
687
688 if ( !tableExists( table, database.get() ) )
689 {
690 if ( !createTable( field.typeName(), table, database.get(), mErrorString ) )
691 {
692 return alayer;
693 }
694 }
695
696 alayer = new QgsAuxiliaryLayer( field.name(), currentFileName(), table, layer );
697 alayer->startEditing();
698 }
699
700 return alayer;
701}
702
704{
705 bool rc = false;
706 const QgsDataSourceUri uri = parseOgrUri( ogrUri );
707
708 if ( !uri.database().isEmpty() && !uri.table().isEmpty() )
709 {
710 sqlite3_database_unique_ptr database;
711 database = openDB( uri.database() );
712
713 if ( database )
714 {
715 QString sql = QStringLiteral( "DROP TABLE %1" ).arg( uri.table() );
716 rc = exec( sql, database.get() );
717
718 sql = QStringLiteral( "VACUUM" );
719 rc = exec( sql, database.get() );
720 }
721 }
722
723 return rc;
724}
725
726bool QgsAuxiliaryStorage::duplicateTable( const QgsDataSourceUri &ogrUri, const QString &newTable )
727{
728 const QgsDataSourceUri uri = parseOgrUri( ogrUri );
729 bool rc = false;
730
731 if ( !uri.table().isEmpty() && !uri.database().isEmpty() )
732 {
733 sqlite3_database_unique_ptr database;
734 database = openDB( uri.database() );
735
736 if ( database )
737 {
738 const QString sql = QStringLiteral( "CREATE TABLE %1 AS SELECT * FROM %2" ).arg( newTable, uri.table() );
739 rc = exec( sql, database.get() );
740 }
741 }
742
743 return rc;
744}
745
747{
748 return mErrorString;
749}
750
751bool QgsAuxiliaryStorage::saveAs( const QString &filename )
752{
753 mErrorString.clear();
754
755 QFile dest( filename );
756 if ( dest.exists() && !dest.remove() )
757 {
758 mErrorString = dest.errorString();
759 return false;
760 }
761
762 QFile origin( currentFileName() );
763 if ( !origin.copy( filename ) )
764 {
765 mErrorString = origin.errorString();
766 return false;
767 }
768
769 return true;
770}
771
772bool QgsAuxiliaryStorage::saveAs( const QgsProject &project )
773{
774 return saveAs( filenameForProject( project ) );
775}
776
778{
779 return AS_EXTENSION;
780}
781
782bool QgsAuxiliaryStorage::exists( const QgsProject &project )
783{
784 const QFileInfo fileinfo( filenameForProject( project ) );
785 return fileinfo.exists() && fileinfo.isFile();
786}
787
788bool QgsAuxiliaryStorage::exec( const QString &sql, sqlite3 *handler )
789{
790 bool rc = false;
791
792 if ( handler )
793 {
794 const int err = sqlite3_exec( handler, sql.toStdString().c_str(), nullptr, nullptr, nullptr );
795
796 if ( err == SQLITE_OK )
797 rc = true;
798 else
799 debugMsg( sql, handler );
800 }
801
802 return rc;
803}
804
805QString QgsAuxiliaryStorage::debugMsg( const QString &sql, sqlite3 *handler )
806{
807 const QString err = QString::fromUtf8( sqlite3_errmsg( handler ) );
808 const QString msg = QObject::tr( "Unable to execute" );
809 const QString errMsg = QObject::tr( "%1 '%2': %3" ).arg( msg, sql, err );
810 QgsDebugError( errMsg );
811 return errMsg;
812}
813
814bool QgsAuxiliaryStorage::createTable( const QString &type, const QString &table, sqlite3 *handler, QString &errorMsg )
815{
816 const QString sql = QStringLiteral( "CREATE TABLE IF NOT EXISTS '%1' ( '%2' %3 )" ).arg( table, AS_JOINFIELD, type );
817
818 if ( !exec( sql, handler ) )
819 {
820 errorMsg = QgsAuxiliaryStorage::debugMsg( sql, handler );
821 return false;
822 }
823
824 return true;
825}
826
827sqlite3_database_unique_ptr QgsAuxiliaryStorage::createDB( const QString &filename )
828{
829 sqlite3_database_unique_ptr database;
830
831 int rc;
832 rc = database.open_v2( filename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr );
833 if ( rc )
834 {
835 debugMsg( QStringLiteral( "sqlite3_open_v2" ), database.get() );
836 }
837 else
838 // activating Foreign Key constraints
839 exec( QStringLiteral( "PRAGMA foreign_keys = 1" ), database.get() );
840
841 return database;
842}
843
844sqlite3_database_unique_ptr QgsAuxiliaryStorage::openDB( const QString &filename )
845{
846 sqlite3_database_unique_ptr database;
847 const int rc = database.open_v2( filename, SQLITE_OPEN_READWRITE, nullptr );
848
849 if ( rc )
850 {
851 debugMsg( QStringLiteral( "sqlite3_open_v2" ), database.get() );
852 }
853
854 return database;
855}
856
857bool QgsAuxiliaryStorage::tableExists( const QString &table, sqlite3 *handler )
858{
859 const QString sql = QStringLiteral( "SELECT 1 FROM sqlite_master WHERE type='table' AND name='%1'" ).arg( table );
860 int rows = 0;
861 int columns = 0;
862 char **results = nullptr;
863 const int rc = sqlite3_get_table( handler, sql.toStdString().c_str(), &results, &rows, &columns, nullptr );
864 if ( rc != SQLITE_OK )
865 {
866 debugMsg( sql, handler );
867 return false;
868 }
869
870 sqlite3_free_table( results );
871 if ( rows >= 1 )
872 return true;
873
874 return false;
875}
876
877sqlite3_database_unique_ptr QgsAuxiliaryStorage::open( const QString &filename )
878{
879 sqlite3_database_unique_ptr database;
880
881 if ( filename.isEmpty() )
882 {
883 if ( ( database = createDB( currentFileName() ) ) )
884 mValid = true;
885 }
886 else if ( QFile::exists( filename ) )
887 {
888 if ( mCopy )
889 QFile::copy( filename, mTmpFileName );
890
891 if ( ( database = openDB( currentFileName() ) ) )
892 mValid = true;
893 }
894 else
895 {
896 if ( ( database = createDB( currentFileName() ) ) )
897 mValid = true;
898 }
899
900 return database;
901}
902
903sqlite3_database_unique_ptr QgsAuxiliaryStorage::open( const QgsProject &project )
904{
905 return open( filenameForProject( project ) );
906}
907
908QString QgsAuxiliaryStorage::filenameForProject( const QgsProject &project )
909{
910 const QFileInfo info( project.absoluteFilePath() );
911 const QString path = info.path() + QDir::separator() + info.baseName();
912 return path + '.' + QgsAuxiliaryStorage::extension();
913}
914
915void QgsAuxiliaryStorage::initTmpFileName()
916{
917 QTemporaryFile tmpFile;
918 if ( !tmpFile.open() )
919 {
920 QgsDebugError( QStringLiteral( "Can't open temporary file" ) );
921 return;
922 }
923 tmpFile.close();
924 mTmpFileName = tmpFile.fileName();
925}
926
928{
929 if ( mCopy || mFileName.isEmpty() )
930 return mTmpFileName;
931 else
932 return mFileName;
933}
934
935QgsDataSourceUri QgsAuxiliaryStorage::parseOgrUri( const QgsDataSourceUri &uri )
936{
937 QgsDataSourceUri newUri;
938
939 // parsing for ogr style uri :
940 // " filePath|layername='tableName' table="" sql="
941 QStringList uriParts = uri.uri().split( '|' );
942 if ( uriParts.count() < 2 )
943 return newUri;
944
945 const QString databasePath = uriParts[0].replace( ' ', QString() );
946
947 const QString table = uriParts[1];
948 QStringList tableParts = table.split( ' ' );
949
950 if ( tableParts.count() < 1 )
951 return newUri;
952
953 const QString tableName = tableParts[0].replace( QLatin1String( "layername=" ), QString() );
954
955 newUri.setDataSource( QString(), tableName, QString() );
956 newUri.setDatabase( databasePath );
957
958 return newUri;
959}
@ Invalid
Invalid (not set) property.
Definition qgis.h:683
@ Field
Field based property.
Definition qgis.h:685
@ Expression
Expression based property.
Definition qgis.h:686
virtual QStringList subProviders() const
Gets list of sub-providers within the layer's labeling.
virtual void setSettings(QgsPalLayerSettings *settings, const QString &providerId=QString())=0
Set pal settings for a specific provider (takes ownership).
virtual QgsPalLayerSettings settings(const QString &providerId=QString()) const =0
Gets associated label settings.
QVector< QgsAttributeTableConfig::ColumnConfig > columns() const
Gets the list with all columns and their configuration.
void update(const QgsFields &fields)
Update the configuration with the given fields.
void setColumns(const QVector< QgsAttributeTableConfig::ColumnConfig > &columns)
Set the list of columns visible in the attribute table.
Allows managing the auxiliary storage for a vector layer.
static QString nameFromProperty(const QgsPropertyDefinition &def, bool joined=false)
Returns the name of the auxiliary field for a property definition.
static QgsField createAuxiliaryField(const QgsPropertyDefinition &definition)
Creates a new auxiliary field from a property definition.
static QgsPropertyDefinition propertyDefinitionFromField(const QgsField &field)
Returns the property definition from an auxiliary field.
bool clear()
Deletes all features from the layer.
bool addAuxiliaryField(const QgsPropertyDefinition &definition)
Adds an auxiliary field for the given property.
bool isHiddenProperty(int index) const
Returns true if the underlying field has to be hidden from editing tools like attribute table,...
QgsVectorLayer * toSpatialLayer() const
An auxiliary layer is not spatial.
bool deleteAttribute(int attr) override
Removes attribute from the layer and commits changes.
QgsFields auxiliaryFields() const
Returns a list of all auxiliary fields currently managed by the layer.
QgsAuxiliaryLayer(const QString &pkField, const QString &filename, const QString &table, QgsVectorLayer *vlayer)
Constructor.
bool save()
Commits changes and starts editing then.
int propertyFromIndex(int index) const
Returns the underlying property key for the field index.
QgsVectorLayerJoinInfo joinInfo() const
Returns information to use for joining with primary key and so on.
QgsVectorLayer * clone() const override
Returns a new instance equivalent to this one.
QgsPropertyDefinition propertyDefinitionFromIndex(int index) const
Returns the property definition for the underlying field index.
bool exists(const QgsPropertyDefinition &definition) const
Returns true if the property is stored in the layer already, false otherwise.
static int createProperty(QgsPalLayerSettings::Property property, QgsVectorLayer *vlayer, bool overwriteExisting=true)
Creates if necessary a new auxiliary field for a PAL property and activates this property in settings...
int indexOfPropertyDefinition(const QgsPropertyDefinition &definition) const
Returns the index of the auxiliary field for a specific property definition.
static QString extension()
Returns the extension used for auxiliary databases.
QString errorString() const
Returns the underlying error string describing potential errors happening in saveAs().
bool save() const
Saves the current database.
static bool exists(const QgsProject &project)
Returns true if the auxiliary database yet exists for a project, false otherwise.
QgsAuxiliaryLayer * createAuxiliaryLayer(const QgsField &field, QgsVectorLayer *layer) const
Creates an auxiliary layer for a vector layer.
virtual ~QgsAuxiliaryStorage()
bool saveAs(const QString &filename)
Saves the current database to a new path.
bool isValid() const
Returns the status of the auxiliary storage currently defined.
QString currentFileName() const
Returns the path of the current database used.
QgsAuxiliaryStorage(const QgsProject &project, bool copy=true)
Constructor.
QString fileName() const
Returns the target filename of the database.
static bool deleteTable(const QgsDataSourceUri &uri)
Removes a table from the auxiliary storage.
static bool duplicateTable(const QgsDataSourceUri &uri, const QString &newTable)
Duplicates a table and its content.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the callout's property collection, used for data defined overrides.
Definition qgscallout.h:353
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the callout's property collection, used for data defined overrides.
Definition qgscallout.h:335
static QgsPropertiesDefinition propertyDefinitions()
Returns the definitions for data defined properties available for use in callouts.
Property
Data definable properties.
Definition qgscallout.h:87
Stores the component parts of a data source URI (e.g.
QString table() const
Returns the table name stored in the URI.
QString uri(bool expandAuthConfig=true) const
Returns the complete URI as a string.
void setDataSource(const QString &aSchema, const QString &aTable, const QString &aGeometryColumn, const QString &aSql=QString(), const QString &aKeyColumn=QString())
Sets all data source related members at once.
QString database() const
Returns the database name stored in the URI.
void setDatabase(const QString &database)
Sets the URI database name.
Property
Data definable properties.
static const QgsPropertiesDefinition & propertyDefinitions()
Returns the diagram property definitions.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QMetaType::Type fieldType=QMetaType::Type::UnknownType)
Create an expression allowing to evaluate if a field is equal to a value.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes).
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
virtual QgsFeatureIds allFeatureIds() const
Returns a list of all feature IDs for features present in the source.
QgsGeometry geometry
Definition qgsfeature.h:69
bool isValid() const
Returns the validity of this feature.
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 typeName() const
Gets the field type.
Definition qgsfield.cpp:163
QString name
Definition qgsfield.h:63
void setPrecision(int precision)
Set the field precision.
Definition qgsfield.cpp:263
void setName(const QString &name)
Set the field name.
Definition qgsfield.cpp:229
void setType(QMetaType::Type type)
Set variant type.
Definition qgsfield.cpp:234
void setLength(int len)
Set the field length.
Definition qgsfield.cpp:259
void setTypeName(const QString &typeName)
Set the field type.
Definition qgsfield.cpp:254
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:73
int count
Definition qgsfields.h:50
Q_INVOKABLE int indexOf(const QString &fieldName) const
Gets the field index from the field name.
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
QString source() const
Returns the source for the layer.
friend class QgsVectorLayer
QString id
Definition qgsmaplayer.h:83
Qgis::LayerType type
Definition qgsmaplayer.h:90
static QgsVectorLayer * createMemoryLayer(const QString &name, const QgsFields &fields, Qgis::WkbType geometryType=Qgis::WkbType::NoGeometry, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem(), bool loadDefaultStyle=true) SIP_FACTORY
Creates a new memory layer using the specified parameters.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the label's property collection, used for data defined overrides.
QgsCallout * callout() const
Returns the label callout renderer, responsible for drawing label callouts.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the label's property collection, used for data defined overrides.
Property
Data definable properties.
@ PositionX
X-coordinate data defined label position.
@ MinScale
Min scale (deprecated, for old project compatibility only).
@ PositionY
Y-coordinate data defined label position.
@ Vali
Vertical alignment for data defined label position (Bottom, Base, Half, Cap, Top).
@ MaxScale
Max scale (deprecated, for old project compatibility only).
@ LabelAllParts
Whether all parts of multi-part features should be labeled.
@ Hali
Horizontal alignment for data defined label position (Left, Center, Right).
static const QgsPropertiesDefinition & propertyDefinitions()
Returns the labeling property definitions.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:109
QString absoluteFilePath() const
Returns full absolute path to the project file if the project is stored in a file system - derived fr...
QgsProperty property(int key) const final
Returns a matching property from the collection, if one exists.
Definition for a property.
Definition qgsproperty.h:45
StandardPropertyTemplate standardTemplate() const
Returns the property's standard template, if applicable.
QString comment() const
Returns the comment of the property.
DataType dataType() const
Returns the allowable field/value data type for the property.
void setOrigin(const QString &origin)
Sets the origin of the property.
@ ColorNoAlpha
Color with no alpha channel.
Definition qgsproperty.h:63
@ ColorWithAlpha
Color with alpha channel.
Definition qgsproperty.h:62
QString name() const
Returns the name of the property.
void setDataType(DataType type)
Sets the data type.
void setName(const QString &name)
Sets the name of the property.
QString origin() const
Returns the origin of the property.
void setComment(const QString &comment)
Sets comment of the property.
@ DataTypeString
Property requires a string value.
Definition qgsproperty.h:90
@ DataTypeBoolean
Property requires a boolean value.
@ DataTypeNumeric
Property requires a numeric value.
Definition qgsproperty.h:97
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
QString expressionString() const
Returns the expression used for the property value.
Qgis::PropertyType propertyType() const
Returns the property type.
QString field() const
Returns the current field name the property references.
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
static QgsProperty fromField(const QString &fieldName, bool isActive=true)
Returns a new FieldBasedProperty created from the specified field name.
static const QgsPropertiesDefinition & propertyDefinitions()
Returns the symbol layer property definitions.
Defines left outer join from our vector layer to some other vector layer.
Represents a vector layer which manages a vector based dataset.
bool isEditable() const final
Returns true if the provider is in editing mode.
Q_INVOKABLE bool deleteFeatures(const QgsFeatureIds &fids, QgsVectorLayer::DeleteContext *context=nullptr)
Deletes a set of features from the layer (but does not commit it).
Q_INVOKABLE bool startEditing()
Makes the layer editable.
Q_INVOKABLE bool addAttribute(const QgsField &field)
Add an attribute field (but does not commit it) returns true if the field was added.
void updateFields()
Will regenerate the fields property of this layer by obtaining all fields from the dataProvider,...
const QgsDiagramLayerSettings * diagramLayerSettings() const
QgsAuxiliaryLayer * auxiliaryLayer()
Returns the current auxiliary layer.
const QgsAbstractVectorLayerLabeling * labeling() const
Access to const labeling configuration.
virtual Q_INVOKABLE bool deleteAttribute(int attr)
Deletes an attribute field (but does not commit it).
void setEditorWidgetSetup(int index, const QgsEditorWidgetSetup &setup)
Sets the editor widget setup for the field at the specified index.
Q_INVOKABLE bool commitChanges(bool stopEditing=true)
Attempts to commit to the underlying data provider any buffered changes made since the last to call t...
QgsEditorWidgetSetup editorWidgetSetup(int index) const
Returns the editor widget setup for the field at the specified index.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
void setDiagramLayerSettings(const QgsDiagramLayerSettings &s)
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) final
Adds a single feature to the sink.
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
Q_GLOBAL_STATIC_WITH_ARGS(PalPropertyList, palHiddenProperties,({ static_cast< int >(QgsPalLayerSettings::Property::PositionX), static_cast< int >(QgsPalLayerSettings::Property::PositionY), static_cast< int >(QgsPalLayerSettings::Property::Show), static_cast< int >(QgsPalLayerSettings::Property::LabelRotation), static_cast< int >(QgsPalLayerSettings::Property::Family), static_cast< int >(QgsPalLayerSettings::Property::FontStyle), static_cast< int >(QgsPalLayerSettings::Property::Size), static_cast< int >(QgsPalLayerSettings::Property::Bold), static_cast< int >(QgsPalLayerSettings::Property::Italic), static_cast< int >(QgsPalLayerSettings::Property::Underline), static_cast< int >(QgsPalLayerSettings::Property::Color), static_cast< int >(QgsPalLayerSettings::Property::Strikeout), static_cast< int >(QgsPalLayerSettings::Property::MultiLineAlignment), static_cast< int >(QgsPalLayerSettings::Property::BufferSize), static_cast< int >(QgsPalLayerSettings::Property::BufferDraw), static_cast< int >(QgsPalLayerSettings::Property::BufferColor), static_cast< int >(QgsPalLayerSettings::Property::LabelDistance), static_cast< int >(QgsPalLayerSettings::Property::Hali), static_cast< int >(QgsPalLayerSettings::Property::Vali), static_cast< int >(QgsPalLayerSettings::Property::ScaleVisibility), static_cast< int >(QgsPalLayerSettings::Property::MinScale), static_cast< int >(QgsPalLayerSettings::Property::MaxScale), static_cast< int >(QgsPalLayerSettings::Property::AlwaysShow), static_cast< int >(QgsPalLayerSettings::Property::CalloutDraw), static_cast< int >(QgsPalLayerSettings::Property::LabelAllParts) })) Q_GLOBAL_STATIC_WITH_ARGS(SymbolPropertyList
symbolHiddenProperties
QVector< int > SymbolPropertyList
#define AS_JOINFIELD
#define AS_JOINPREFIX
QVector< int > PalPropertyList
#define AS_EXTENSION
struct sqlite3 sqlite3
#define QgsDebugError(str)
Definition qgslogger.h:57
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.