QGIS API Documentation 4.1.0-Master (d6fb7a379fb)
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#include <QString>
32
33#include "moc_qgsauxiliarystorage.cpp"
34
35using namespace Qt::StringLiterals;
36
37#define AS_JOINFIELD u"ASPK"_s
38#define AS_EXTENSION u"qgd"_s
39#define AS_JOINPREFIX u"auxiliary_storage_"_s
40
41typedef QVector<int> PalPropertyList;
42typedef QVector<int> SymbolPropertyList;
43
46 palHiddenProperties,
47 (
48 { static_cast< int >( QgsPalLayerSettings::Property::PositionX ),
49 static_cast< int >( QgsPalLayerSettings::Property::PositionY ),
50 static_cast< int >( QgsPalLayerSettings::Property::Show ),
52 static_cast< int >( QgsPalLayerSettings::Property::Family ),
53 static_cast< int >( QgsPalLayerSettings::Property::FontStyle ),
54 static_cast< int >( QgsPalLayerSettings::Property::Size ),
55 static_cast< int >( QgsPalLayerSettings::Property::Bold ),
56 static_cast< int >( QgsPalLayerSettings::Property::Italic ),
57 static_cast< int >( QgsPalLayerSettings::Property::Underline ),
58 static_cast< int >( QgsPalLayerSettings::Property::Color ),
59 static_cast< int >( QgsPalLayerSettings::Property::Strikeout ),
65 static_cast< int >( QgsPalLayerSettings::Property::Hali ),
66 static_cast< int >( QgsPalLayerSettings::Property::Vali ),
68 static_cast< int >( QgsPalLayerSettings::Property::MinScale ),
69 static_cast< int >( QgsPalLayerSettings::Property::MaxScale ),
73 )
74)
76
77//
78// QgsAuxiliaryLayer
79//
80
81QgsAuxiliaryLayer::QgsAuxiliaryLayer( const QString &pkField, const QString &filename, const QString &table, QgsVectorLayer *vlayer )
82 : QgsVectorLayer( u"%1|layername=%2"_s.arg( filename, table ), u"%1_auxiliarystorage"_s.arg( table ), u"ogr"_s )
83 , mFileName( filename )
84 , mTable( table )
85 , mLayer( vlayer )
86{
87 // init join info
88 mJoinInfo.setPrefix( AS_JOINPREFIX );
89 mJoinInfo.setJoinLayer( this );
90 mJoinInfo.setJoinFieldName( AS_JOINFIELD );
91 mJoinInfo.setTargetFieldName( pkField );
92 mJoinInfo.setEditable( true );
93 mJoinInfo.setUpsertOnEdit( true );
94 mJoinInfo.setCascadedDelete( true );
95 mJoinInfo.setJoinFieldNamesBlockList( QStringList() << u"rowid"_s ); // introduced by ogr provider
96}
97
99{
101 return new QgsAuxiliaryLayer( mJoinInfo.targetFieldName(), mFileName, target->id(), target );
102}
103
105{
106 const bool rc = deleteFeatures( allFeatureIds() );
108 startEditing();
109 return rc;
110}
111
113{
114 QgsVectorLayer *layer = QgsMemoryProviderUtils::createMemoryLayer( u"auxiliary_layer"_s, fields(), mLayer->wkbType(), mLayer->crs() );
115
116 const QString pkField = mJoinInfo.targetFieldName();
117 QgsFeature joinFeature;
118 QgsFeature targetFeature;
119 QgsFeatureIterator it = getFeatures();
120
121 layer->startEditing();
122 while ( it.nextFeature( joinFeature ) )
123 {
124 const QString filter = QgsExpression::createFieldEqualityExpression( pkField, joinFeature.attribute( AS_JOINFIELD ) );
125
126 QgsFeatureRequest request;
127 request.setFilterExpression( filter );
128
129 mLayer->getFeatures( request ).nextFeature( targetFeature );
130
131 if ( targetFeature.isValid() )
132 {
133 QgsFeature newFeature( joinFeature );
134 newFeature.setGeometry( targetFeature.geometry() );
135 layer->addFeature( newFeature );
136 }
137 }
138 layer->commitChanges();
139
140 return layer;
141}
142
144{
145 return mJoinInfo;
146}
147
148bool QgsAuxiliaryLayer::exists( const QgsPropertyDefinition &definition ) const
149{
150 return ( indexOfPropertyDefinition( definition ) >= 0 );
151}
152
154{
155 if ( ( definition.name().isEmpty() && definition.comment().isEmpty() ) || exists( definition ) )
156 return false;
157
158 const QgsField af = createAuxiliaryField( definition );
159 const bool rc = addAttribute( af );
160 updateFields();
161 mLayer->updateFields();
162
163 if ( rc )
164 {
165 const int auxIndex = indexOfPropertyDefinition( definition );
166 const int index = mLayer->fields().indexOf( nameFromProperty( definition, true ) );
167
168 if ( index >= 0 && auxIndex >= 0 )
169 {
170 if ( isHiddenProperty( auxIndex ) )
171 {
172 // update editor widget
173 const QgsEditorWidgetSetup setup = QgsEditorWidgetSetup( u"Hidden"_s, QVariantMap() );
174 setEditorWidgetSetup( auxIndex, setup );
175
176 // column is hidden
177 QgsAttributeTableConfig attrCfg = mLayer->attributeTableConfig();
178 attrCfg.update( mLayer->fields() );
179 QVector<QgsAttributeTableConfig::ColumnConfig> columns = attrCfg.columns();
180 QVector<QgsAttributeTableConfig::ColumnConfig>::iterator it;
181
182 for ( it = columns.begin(); it != columns.end(); ++it )
183 {
184 if ( it->name.compare( mLayer->fields().field( index ).name() ) == 0 )
185 it->hidden = true;
186 }
187
188 attrCfg.setColumns( columns );
189 mLayer->setAttributeTableConfig( attrCfg );
190 }
192 {
193 const QgsEditorWidgetSetup setup = QgsEditorWidgetSetup( u"Color"_s, 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( u"coalesce(%1,%2)"_s.arg( QgsExpression::quotedColumnRef( fieldName ), existingProperty.asExpression() ) );
271 c.setProperty( property, prop );
272 }
273 settings->setDataDefinedProperties( c );
274
275 layer->labeling()->setSettings( settings, providerId );
276 }
277 }
278
279 index = layer->fields().lookupField( fieldName );
280 }
281
282 return index;
283}
284
285int QgsAuxiliaryLayer::createProperty( QgsDiagramLayerSettings::Property property, QgsVectorLayer *layer, bool overwriteExisting )
286{
287 int index = -1;
288
289 if ( layer && layer->diagramLayerSettings() && layer->auxiliaryLayer() )
290 {
291 const QgsPropertyDefinition def = QgsDiagramLayerSettings::propertyDefinitions()[static_cast<int>( property )];
292
293 if ( layer->auxiliaryLayer()->addAuxiliaryField( def ) )
294 {
295 const QString fieldName = nameFromProperty( def, true );
296
297 QgsDiagramLayerSettings settings( *layer->diagramLayerSettings() );
298
299 QgsPropertyCollection c = settings.dataDefinedProperties();
300 // is there an existing property?
301 const QgsProperty existingProperty = c.property( property );
302 if ( existingProperty.propertyType() == Qgis::PropertyType::Invalid || overwriteExisting )
303 {
304 const QgsProperty prop = QgsProperty::fromField( fieldName );
305 c.setProperty( property, prop );
306 }
307 else
308 {
309 // build a new smart expression as coalesce("new aux field", 'the' || 'old' || 'expression')
310 const QgsProperty prop = QgsProperty::fromExpression( u"coalesce(%1,%2)"_s.arg( QgsExpression::quotedColumnRef( fieldName ), existingProperty.asExpression() ) );
311 c.setProperty( property, prop );
312 }
313 settings.setDataDefinedProperties( c );
314
315 layer->setDiagramLayerSettings( settings );
316 index = layer->fields().lookupField( fieldName );
317 }
318 }
319
320 return index;
321}
322
323int QgsAuxiliaryLayer::createProperty( QgsCallout::Property property, QgsVectorLayer *layer, bool overwriteExisting )
324{
325 int index = -1;
326
327 if ( layer && layer->labeling() && layer->labeling()->settings().callout() && layer->auxiliaryLayer() )
328 {
329 // property definition are identical whatever the provider id
330 const QgsPropertyDefinition def = QgsCallout::propertyDefinitions()[static_cast< int >( property )];
331 const QString fieldName = nameFromProperty( def, true );
332
333 layer->auxiliaryLayer()->addAuxiliaryField( def );
334
335 if ( layer->auxiliaryLayer()->indexOfPropertyDefinition( def ) >= 0 )
336 {
337 const QStringList subProviderIds = layer->labeling()->subProviders();
338 for ( const QString &providerId : subProviderIds )
339 {
340 QgsPalLayerSettings *settings = new QgsPalLayerSettings( layer->labeling()->settings( providerId ) );
341 if ( settings->callout() )
342 {
343 QgsPropertyCollection c = settings->callout()->dataDefinedProperties();
344 // is there an existing property?
345 const QgsProperty existingProperty = c.property( property );
346 if ( existingProperty.propertyType() == Qgis::PropertyType::Invalid || overwriteExisting )
347 {
348 const QgsProperty prop = QgsProperty::fromField( fieldName );
349 c.setProperty( property, prop );
350 }
351 else
352 {
353 // build a new smart expression as coalesce("new aux field", 'the' || 'old' || 'expression')
354 const QgsProperty prop = QgsProperty::fromExpression( u"coalesce(%1,%2)"_s.arg( QgsExpression::quotedColumnRef( fieldName ), existingProperty.asExpression() ) );
355 c.setProperty( property, prop );
356 }
357 settings->callout()->setDataDefinedProperties( c );
358 }
359 layer->labeling()->setSettings( settings, providerId );
360 }
361 }
362
363 index = layer->fields().lookupField( fieldName );
364 }
365
366 return index;
367}
368
369int QgsAuxiliaryLayer::createProperty( QgsSymbolLayer::Property propertyKey, QgsVectorLayer *layer, QgsSymbolLayer *symbolLayer, bool overwriteExisting )
370{
371 if ( !layer || !symbolLayer || !layer->auxiliaryLayer() )
372 return -1;
373
374 // compute a unique name for the new property
375 QgsPropertyDefinition def = QgsSymbolLayer::propertyDefinitions()[static_cast<int>( propertyKey )];
376 const QString baseName = def.name();
377 int iDef = 2; // first name doesn't have number suffix so the next one is 2
378 while ( layer->auxiliaryLayer()->exists( def ) )
379 {
380 def.setName( u"%1_%2"_s.arg( baseName ).arg( iDef++ ) );
381 }
382
383 const QString fieldName = QgsAuxiliaryLayer::nameFromProperty( def, true );
384 if ( !layer->auxiliaryLayer()->addAuxiliaryField( def ) )
385 return -1;
386
387 // is there an existing property?
388 QgsPropertyCollection c = symbolLayer->dataDefinedProperties();
389 QgsProperty property = c.property( propertyKey );
390 if ( property.propertyType() == Qgis::PropertyType::Invalid || overwriteExisting )
391 {
392 property = QgsProperty::fromField( fieldName );
393 }
394 else
395 {
396 // build a new smart expression as coalesce("new aux field", 'the' || 'old' || 'expression')
397 property = QgsProperty::fromExpression( u"coalesce(%1,%2)"_s.arg( QgsExpression::quotedColumnRef( fieldName ), property.asExpression() ) );
398 }
399
400 c.setProperty( propertyKey, property );
401 symbolLayer->setDataDefinedProperties( c );
402
403 return layer->fields().lookupField( fieldName );
404}
405
406bool QgsAuxiliaryLayer::isHiddenProperty( int index ) const
407{
408 bool hidden = false;
409 const QgsPropertyDefinition def = propertyDefinitionFromIndex( index );
410
411 if ( def.origin().compare( "labeling"_L1 ) == 0 )
412 {
413 const PalPropertyList &palProps = *palHiddenProperties();
414 for ( const int p : palProps )
415 {
416 const QString propName = QgsPalLayerSettings::propertyDefinitions()[p].name();
417 if ( propName.compare( def.name() ) == 0 )
418 {
419 hidden = true;
420 break;
421 }
422 }
423 }
424 else if ( def.origin().compare( "symbol"_L1 ) == 0 )
425 {
426 const SymbolPropertyList &symbolProps = *symbolHiddenProperties();
427 for ( int p : symbolProps )
428 {
429 const QString propName = QgsSymbolLayer::propertyDefinitions()[p].name();
430 if ( propName.compare( def.name() ) == 0 )
431 {
432 hidden = true;
433 break;
434 }
435 }
436 }
437
438 return hidden;
439}
440
441int QgsAuxiliaryLayer::propertyFromIndex( int index ) const
442{
443 int p = -1;
444 const QgsPropertyDefinition aDef = propertyDefinitionFromIndex( index );
445
446 if ( aDef.origin().compare( "labeling"_L1 ) == 0 )
447 {
449 QgsPropertiesDefinition::const_iterator it = defs.constBegin();
450 for ( ; it != defs.constEnd(); ++it )
451 {
452 if ( it->name().compare( aDef.name(), Qt::CaseInsensitive ) == 0 )
453 {
454 p = it.key();
455 break;
456 }
457 }
458 }
459 else if ( aDef.origin().compare( "symbol"_L1 ) == 0 )
460 {
462 QgsPropertiesDefinition::const_iterator it = defs.constBegin();
463 for ( ; it != defs.constEnd(); ++it )
464 {
465 if ( it->name().compare( aDef.name(), Qt::CaseInsensitive ) == 0 )
466 {
467 p = it.key();
468 break;
469 }
470 }
471 }
472 else if ( aDef.origin().compare( "diagram"_L1 ) == 0 )
473 {
475 QgsPropertiesDefinition::const_iterator it = defs.constBegin();
476 for ( ; it != defs.constEnd(); ++it )
477 {
478 if ( it->name().compare( aDef.name(), Qt::CaseInsensitive ) == 0 )
479 {
480 p = it.key();
481 break;
482 }
483 }
484 }
485
486 return p;
487}
488
490{
491 return propertyDefinitionFromField( fields().field( index ) );
492}
493
495{
496 return fields().indexOf( nameFromProperty( def ) );
497}
498
499QString QgsAuxiliaryLayer::nameFromProperty( const QgsPropertyDefinition &def, bool joined )
500{
501 QString fieldName = def.origin();
502
503 if ( !def.name().isEmpty() )
504 fieldName = u"%1_%2"_s.arg( fieldName, def.name().toLower() );
505
506 if ( !def.comment().isEmpty() )
507 fieldName = u"%1_%2"_s.arg( fieldName, def.comment() );
508
509 if ( joined )
510 fieldName = u"%1%2"_s.arg( AS_JOINPREFIX, fieldName );
511
512 return fieldName;
513}
514
516{
517 QgsField afield;
518
519 if ( !def.name().isEmpty() || !def.comment().isEmpty() )
520 {
521 QMetaType::Type type = QMetaType::Type::UnknownType;
522 QString typeName;
523 int len( 0 ), precision( 0 );
524 switch ( def.dataType() )
525 {
527 type = QMetaType::Type::QString;
528 len = 50;
529 typeName = u"String"_s;
530 break;
532 type = QMetaType::Type::Double;
533 len = 0;
534 precision = 0;
535 typeName = u"Real"_s;
536 break;
538 type = QMetaType::Type::Int; // sqlite does not have a bool type
539 typeName = u"Integer"_s;
540 break;
541 }
542
543 afield.setType( type );
544 afield.setName( nameFromProperty( def ) );
545 afield.setTypeName( typeName );
546 afield.setLength( len );
547 afield.setPrecision( precision );
548 }
549
550 return afield;
551}
552
554{
555 QgsPropertyDefinition def;
556 const QStringList parts = f.name().split( '_' );
557
558 if ( parts.size() <= 1 )
559 return def;
560
561 const QString origin = parts[0];
562 const QString propertyName = parts[1];
563
564 if ( origin.compare( "labeling"_L1, Qt::CaseInsensitive ) == 0 )
565 {
567 for ( auto it = props.constBegin(); it != props.constEnd(); ++it )
568 {
569 if ( it.value().name().compare( propertyName, Qt::CaseInsensitive ) == 0 )
570 {
571 def = it.value();
572 if ( parts.size() >= 3 )
573 def.setComment( parts.mid( 2 ).join( '_' ) );
574 break;
575 }
576 }
577 }
578 else if ( origin.compare( "symbol"_L1, Qt::CaseInsensitive ) == 0 )
579 {
581 for ( auto it = props.constBegin(); it != props.constEnd(); ++it )
582 {
583 if ( it.value().name().compare( propertyName, Qt::CaseInsensitive ) == 0 )
584 {
585 def = it.value();
586 if ( parts.size() >= 3 )
587 def.setComment( parts.mid( 2 ).join( '_' ) );
588 break;
589 }
590 }
591 }
592 else if ( origin.compare( "diagram"_L1, Qt::CaseInsensitive ) == 0 )
593 {
595 for ( auto it = props.constBegin(); it != props.constEnd(); ++it )
596 {
597 if ( it.value().name().compare( propertyName, Qt::CaseInsensitive ) == 0 )
598 {
599 def = it.value();
600 if ( parts.size() >= 3 )
601 def.setComment( parts.mid( 2 ).join( '_' ) );
602 break;
603 }
604 }
605 }
606 else
607 {
608 def.setOrigin( origin );
609 def.setName( propertyName );
610 switch ( f.type() )
611 {
612 case QMetaType::Type::Double:
614 break;
615
616 case QMetaType::Type::Bool:
618 break;
619
620 case QMetaType::Type::QString:
621 default:
623 break;
624 }
625
626 if ( parts.size() >= 3 )
627 def.setComment( parts.mid( 2 ).join( '_' ) );
628 }
629
630 return def;
631}
632
634{
635 const QgsPropertyDefinition def = propertyDefinitionFromField( field );
636 QgsField afield;
637
638 if ( !def.name().isEmpty() || !def.comment().isEmpty() )
639 {
640 afield = createAuxiliaryField( def );
641 afield.setTypeName( field.typeName() );
642 }
643
644 return afield;
645}
646
647//
648// QgsAuxiliaryStorage
649//
650
651QgsAuxiliaryStorage::QgsAuxiliaryStorage( const QgsProject &project, bool copy )
652 : mCopy( copy )
653{
654 initTmpFileName();
655
656 if ( !project.absoluteFilePath().isEmpty() )
657 {
658 mFileName = filenameForProject( project );
659 }
660
661 open( mFileName );
662}
663
664QgsAuxiliaryStorage::QgsAuxiliaryStorage( const QString &filename, bool copy )
665 : mFileName( filename )
666 , mCopy( copy )
667{
668 initTmpFileName();
669
670 open( filename );
671}
672
674{
675 if ( QFile::exists( mTmpFileName ) )
676 QFile::remove( mTmpFileName );
677}
678
680{
681 return mValid;
682}
683
684QString QgsAuxiliaryStorage::fileName() const
685{
686 return mFileName;
687}
688
689bool QgsAuxiliaryStorage::save() const
690{
691 if ( mFileName.isEmpty() )
692 {
693 // only a saveAs is available on a new database
694 return false;
695 }
696 else if ( mCopy )
697 {
698 if ( QFile::exists( mFileName ) )
699 QFile::remove( mFileName );
700
701 return QFile::copy( mTmpFileName, mFileName );
702 }
703 else
704 {
705 // if the file is not empty the copy mode is not activated, then we're
706 // directly working on the database since the beginning (no savepoints
707 // /rollback for now)
708 return true;
709 }
710}
711
713{
714 QgsAuxiliaryLayer *alayer = nullptr;
715
716 if ( mValid && layer )
717 {
718 const QString table( layer->id() );
719 sqlite3_database_unique_ptr database;
720 database = openDB( currentFileName() );
721
722 if ( !tableExists( table, database.get() ) )
723 {
724 if ( !createTable( field.typeName(), table, database.get(), mErrorString ) )
725 {
726 return alayer;
727 }
728 }
729
730 alayer = new QgsAuxiliaryLayer( field.name(), currentFileName(), table, layer );
731 alayer->startEditing();
732 }
733
734 return alayer;
735}
736
738{
739 bool rc = false;
740 const QgsDataSourceUri uri = parseOgrUri( ogrUri );
741
742 if ( !uri.database().isEmpty() && !uri.table().isEmpty() )
743 {
744 sqlite3_database_unique_ptr database;
745 database = openDB( uri.database() );
746
747 if ( database )
748 {
749 QString sql = u"DROP TABLE %1"_s.arg( uri.table() );
750 rc = exec( sql, database.get() );
751
752 sql = u"VACUUM"_s;
753 rc = exec( sql, database.get() );
754 }
755 }
756
757 return rc;
758}
759
760bool QgsAuxiliaryStorage::duplicateTable( const QgsDataSourceUri &ogrUri, const QString &newTable )
761{
762 const QgsDataSourceUri uri = parseOgrUri( ogrUri );
763 bool rc = false;
764
765 if ( !uri.table().isEmpty() && !uri.database().isEmpty() )
766 {
767 sqlite3_database_unique_ptr database;
768 database = openDB( uri.database() );
769
770 if ( database )
771 {
772 const QString sql = u"CREATE TABLE %1 AS SELECT * FROM %2"_s.arg( newTable, uri.table() );
773 rc = exec( sql, database.get() );
774 }
775 }
776
777 return rc;
778}
779
781{
782 return mErrorString;
783}
784
785bool QgsAuxiliaryStorage::saveAs( const QString &filename )
786{
787 mErrorString.clear();
788
789 QFile dest( filename );
790 if ( dest.exists() && !dest.remove() )
791 {
792 mErrorString = dest.errorString();
793 return false;
794 }
795
796 QFile origin( currentFileName() );
797 if ( !origin.copy( filename ) )
798 {
799 mErrorString = origin.errorString();
800 return false;
801 }
802
803 return true;
804}
805
806bool QgsAuxiliaryStorage::saveAs( const QgsProject &project )
807{
808 return saveAs( filenameForProject( project ) );
809}
810
812{
813 return AS_EXTENSION;
814}
815
816bool QgsAuxiliaryStorage::exists( const QgsProject &project )
817{
818 const QFileInfo fileinfo( filenameForProject( project ) );
819 return fileinfo.exists() && fileinfo.isFile();
820}
821
822bool QgsAuxiliaryStorage::exec( const QString &sql, sqlite3 *handler )
823{
824 bool rc = false;
825
826 if ( handler )
827 {
828 const int err = sqlite3_exec( handler, sql.toStdString().c_str(), nullptr, nullptr, nullptr );
829
830 if ( err == SQLITE_OK )
831 rc = true;
832 else
833 debugMsg( sql, handler );
834 }
835
836 return rc;
837}
838
839QString QgsAuxiliaryStorage::debugMsg( const QString &sql, sqlite3 *handler )
840{
841 const QString err = QString::fromUtf8( sqlite3_errmsg( handler ) );
842 const QString msg = QObject::tr( "Unable to execute" );
843 const QString errMsg = QObject::tr( "%1 '%2': %3" ).arg( msg, sql, err );
844 QgsDebugError( errMsg );
845 return errMsg;
846}
847
848bool QgsAuxiliaryStorage::createTable( const QString &type, const QString &table, sqlite3 *handler, QString &errorMsg )
849{
850 const QString sql = u"CREATE TABLE IF NOT EXISTS '%1' ( '%2' %3 )"_s.arg( table, AS_JOINFIELD, type );
851
852 if ( !exec( sql, handler ) )
853 {
854 errorMsg = QgsAuxiliaryStorage::debugMsg( sql, handler );
855 return false;
856 }
857
858 return true;
859}
860
861sqlite3_database_unique_ptr QgsAuxiliaryStorage::createDB( const QString &filename )
862{
863 sqlite3_database_unique_ptr database;
864
865 int rc;
866 rc = database.open_v2( filename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr );
867 if ( rc )
868 {
869 debugMsg( u"sqlite3_open_v2"_s, database.get() );
870 }
871 else
872 // activating Foreign Key constraints
873 exec( u"PRAGMA foreign_keys = 1"_s, database.get() );
874
875 return database;
876}
877
878sqlite3_database_unique_ptr QgsAuxiliaryStorage::openDB( const QString &filename )
879{
880 sqlite3_database_unique_ptr database;
881 const int rc = database.open_v2( filename, SQLITE_OPEN_READWRITE, nullptr );
882
883 if ( rc )
884 {
885 debugMsg( u"sqlite3_open_v2"_s, database.get() );
886 }
887
888 return database;
889}
890
891bool QgsAuxiliaryStorage::tableExists( const QString &table, sqlite3 *handler )
892{
893 const QString sql = u"SELECT 1 FROM sqlite_master WHERE type='table' AND name='%1'"_s.arg( table );
894 int rows = 0;
895 int columns = 0;
896 char **results = nullptr;
897 const int rc = sqlite3_get_table( handler, sql.toStdString().c_str(), &results, &rows, &columns, nullptr );
898 if ( rc != SQLITE_OK )
899 {
900 debugMsg( sql, handler );
901 return false;
902 }
903
904 sqlite3_free_table( results );
905 if ( rows >= 1 )
906 return true;
907
908 return false;
909}
910
911sqlite3_database_unique_ptr QgsAuxiliaryStorage::open( const QString &filename )
912{
913 sqlite3_database_unique_ptr database;
914
915 if ( filename.isEmpty() )
916 {
917 if ( ( database = createDB( currentFileName() ) ) )
918 mValid = true;
919 }
920 else if ( QFile::exists( filename ) )
921 {
922 if ( mCopy )
923 QFile::copy( filename, mTmpFileName );
924
925 if ( ( database = openDB( currentFileName() ) ) )
926 mValid = true;
927 }
928 else
929 {
930 if ( ( database = createDB( currentFileName() ) ) )
931 mValid = true;
932 }
933
934 return database;
935}
936
937sqlite3_database_unique_ptr QgsAuxiliaryStorage::open( const QgsProject &project )
938{
939 return open( filenameForProject( project ) );
940}
941
942QString QgsAuxiliaryStorage::filenameForProject( const QgsProject &project )
943{
944 const QFileInfo info( project.absoluteFilePath() );
945 const QString path = info.path() + QDir::separator() + info.baseName();
946 return path + '.' + QgsAuxiliaryStorage::extension();
947}
948
949void QgsAuxiliaryStorage::initTmpFileName()
950{
951 QTemporaryFile tmpFile;
952 if ( !tmpFile.open() )
953 {
954 QgsDebugError( u"Can't open temporary file"_s );
955 return;
956 }
957 tmpFile.close();
958 mTmpFileName = tmpFile.fileName();
959}
960
962{
963 if ( mCopy || mFileName.isEmpty() )
964 return mTmpFileName;
965 else
966 return mFileName;
967}
968
969QgsDataSourceUri QgsAuxiliaryStorage::parseOgrUri( const QgsDataSourceUri &uri )
970{
971 QgsDataSourceUri newUri;
972
973 // parsing for ogr style uri :
974 // " filePath|layername='tableName' table="" sql="
975 QStringList uriParts = uri.uri().split( '|' );
976 if ( uriParts.count() < 2 )
977 return newUri;
978
979 const QString databasePath = uriParts[0].replace( ' ', QString() );
980
981 const QString table = uriParts[1];
982 QStringList tableParts = table.split( ' ' );
983
984 if ( tableParts.count() < 1 )
985 return newUri;
986
987 const QString tableName = tableParts[0].replace( "layername="_L1, QString() );
988
989 newUri.setDataSource( QString(), tableName, QString() );
990 newUri.setDatabase( databasePath );
991
992 return newUri;
993}
@ Invalid
Invalid (not set) property.
Definition qgis.h:710
@ Field
Field based property.
Definition qgis.h:712
@ Expression
Expression based property.
Definition qgis.h:713
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:351
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the callout's property collection, used for data defined overrides.
Definition qgscallout.h:333
static QgsPropertiesDefinition propertyDefinitions()
Returns the definitions for data defined properties available for use in callouts.
Property
Data definable properties.
Definition qgscallout.h:85
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:71
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:56
QMetaType::Type type
Definition qgsfield.h:63
QString typeName() const
Gets the field type.
Definition qgsfield.cpp:158
QString name
Definition qgsfield.h:65
void setPrecision(int precision)
Set the field precision.
Definition qgsfield.cpp:258
void setName(const QString &name)
Set the field name.
Definition qgsfield.cpp:224
void setType(QMetaType::Type type)
Set variant type.
Definition qgsfield.cpp:229
void setLength(int len)
Set the field length.
Definition qgsfield.cpp:254
void setTypeName(const QString &typeName)
Set the field type.
Definition qgsfield.cpp:249
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:75
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:86
Qgis::LayerType type
Definition qgsmaplayer.h:93
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
Multipart geometry behavior.
@ 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:114
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:47
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:64
@ ColorWithAlpha
Color with alpha channel.
Definition qgsproperty.h:63
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:91
@ DataTypeBoolean
Property requires a boolean value.
@ DataTypeNumeric
Property requires a numeric value.
Definition qgsproperty.h:98
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.
Abstract base class for symbol layers.
Property
Data definable properties.
static const QgsPropertiesDefinition & propertyDefinitions()
Returns the symbol layer property definitions.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the symbol layer's property collection, used for data defined overrides.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the symbol layer's property collection, used for data defined overrides.
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:59
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.