QGIS API Documentation 4.3.0-Master (9ff14a2eeba)
Loading...
Searching...
No Matches
qgsmeshlayer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmeshlayer.cpp
3 ----------------
4 begin : April 2018
5 copyright : (C) 2018 by Peter Petrik
6 email : zilolv at gmail dot com
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 "qgsmeshlayer.h"
19
20#include <cstddef>
21#include <limits>
22#include <memory>
23
24#include "qgsapplication.h"
25#include "qgscolorrampimpl.h"
28#include "qgslogger.h"
29#include "qgsmaplayerfactory.h"
30#include "qgsmaplayerlegend.h"
31#include "qgsmesh3daveraging.h"
32#include "qgsmeshdataprovider.h"
34#include "qgsmesheditor.h"
40#include "qgsmeshlayerutils.h"
41#include "qgsmeshtimesettings.h"
42#include "qgsmessagelog.h"
43#include "qgspainting.h"
44#include "qgsproviderregistry.h"
45#include "qgsreadwritecontext.h"
46#include "qgsruntimeprofiler.h"
47#include "qgsstyle.h"
48#include "qgsthreadingutils.h"
49#include "qgstriangularmesh.h"
50
51#include <QString>
52#include <QUrl>
53#include <QUuid>
54
55#include "moc_qgsmeshlayer.cpp"
56
57using namespace Qt::StringLiterals;
58
59QgsMeshLayer::QgsMeshLayer( const QString &meshLayerPath, const QString &baseName, const QString &providerKey, const QgsMeshLayer::LayerOptions &options )
60 : QgsMapLayer( Qgis::LayerType::Mesh, baseName, meshLayerPath )
61 , mDatasetGroupStore( new QgsMeshDatasetGroupStore( this ) )
62 , mTemporalProperties( new QgsMeshLayerTemporalProperties( this ) )
63 , mElevationProperties( new QgsMeshLayerElevationProperties( this ) )
64{
66
67 const QgsDataProvider::ProviderOptions providerOptions { options.transformContext };
69 if ( options.loadDefaultStyle )
70 {
71 flags |= Qgis::DataProviderReadFlag::LoadDefaultStyle;
72 }
73 QgsMeshLayer::setDataSourcePrivate( meshLayerPath, baseName, providerKey, providerOptions, flags );
76
77 if ( isValid() && options.loadDefaultStyle )
78 {
79 bool result = false;
80 loadDefaultStyle( result );
81 }
82
83 connect( mDatasetGroupStore.get(), &QgsMeshDatasetGroupStore::datasetGroupsAdded, this, &QgsMeshLayer::onDatasetGroupsAdded );
84}
85
86void QgsMeshLayer::createSimplifiedMeshes()
87{
89
90 if ( mSimplificationSettings.isEnabled() && !hasSimplifiedMeshes() )
91 {
92 const double reductionFactor = mSimplificationSettings.reductionFactor();
93
94 QVector<QgsTriangularMesh *> simplifyMeshes = mTriangularMeshes[0]->simplifyMesh( reductionFactor );
95
96 for ( int i = 0; i < simplifyMeshes.count(); ++i )
97 {
98 mTriangularMeshes.emplace_back( simplifyMeshes[i] );
99 }
100 }
101}
102
103bool QgsMeshLayer::hasSimplifiedMeshes() const
104{
106
107 //First mesh is the base mesh, so if size>1, there is no simplified meshes
108 return ( mTriangularMeshes.size() > 1 );
109}
110
113
115{
117
118 return mDataProvider.get();
119}
120
122{
124
125 return mDataProvider.get();
126}
127
129{
131
133 if ( mDataProvider )
134 {
135 options.transformContext = mDataProvider->transformContext();
136 }
137 QgsMeshLayer *layer = new QgsMeshLayer( source(), name(), mProviderKey, options );
138 QgsMapLayer::clone( layer );
139
140 layer->mElevationProperties = mElevationProperties->clone();
141 layer->mElevationProperties->setParent( layer );
142
143 if ( auto *lLabeling = labeling() )
144 {
145 layer->setLabeling( lLabeling->clone() );
146 }
148
149 for ( const QString &extraDataset : mExtraDatasetUri )
150 {
151 layer->addDatasets( extraDataset );
152 }
153
154 layer->setRendererSettings( mRendererSettings );
155
156 return layer;
157}
158
160{
162
163 if ( mMeshEditor )
164 return mMeshEditor->extent();
165
166 if ( mDataProvider )
167 return mDataProvider->extent();
168 else
169 {
170 QgsRectangle rec;
171 rec.setNull();
172 return rec;
173 }
174}
175
177{
179
180 if ( !mDataProvider )
181 return false;
182
183 if ( mMeshEditor )
184 return true;
185
186 const QgsMeshDriverMetadata driverMetadata = mDataProvider->driverMetadata();
187
189}
190
191QString QgsMeshLayer::loadDefaultStyle( bool &resultFlag )
192{
194
195 const QList<int> groupsList = datasetGroupsIndexes();
196
197 for ( const int index : groupsList )
198 assignDefaultStyleToDatasetGroup( index );
199
200
201 QgsMeshRendererMeshSettings meshSettings;
202 if ( !groupsList.isEmpty() )
203 {
204 // Show data from the first dataset group
205 mRendererSettings.setActiveScalarDatasetGroup( 0 );
206 // If the first dataset group has nan min/max, display the mesh to avoid nothing displayed
208 if ( meta.maximum() == std::numeric_limits<double>::quiet_NaN() && meta.minimum() == std::numeric_limits<double>::quiet_NaN() )
209 meshSettings.setEnabled( true );
210 }
211 else
212 {
213 // show at least the mesh by default
214 meshSettings.setEnabled( true );
215 }
216
217 mRendererSettings.setNativeMeshSettings( meshSettings );
218
219 for ( const int i : groupsList )
220 {
221 assignDefaultStyleToDatasetGroup( i );
222
223 // Sets default resample method for scalar dataset
225 QgsMeshRendererScalarSettings scalarSettings = mRendererSettings.scalarSettings( i );
226 switch ( meta.dataType() )
227 {
229 case QgsMeshDatasetGroupMetadata::DataOnVolumes: // data on volumes are averaged to 2D data on faces
231 break;
234 break;
236 break;
237 }
238
239 //override color ramp if the values in the dataset group are classified
240 applyClassificationOnScalarSettings( meta, scalarSettings );
241
242 mRendererSettings.setScalarSettings( i, scalarSettings );
243 }
244
245 if ( !groupsList.isEmpty() )
246 {
247 emit rendererChanged();
249 }
250
251 return QgsMapLayer::loadDefaultStyle( resultFlag );
252}
253
254bool QgsMeshLayer::removeDatasets( const QString &name )
255{
256 const int index = mDatasetGroupStore->indexFromGroupName( name );
257
258 if ( index == -1 )
259 {
260 return false;
261 }
262
263 const QgsMeshDatasetGroupMetadata groupMetadata = datasetGroupMetadata( index );
264
265 mDatasetGroupStore->removeDatasetGroup( index );
266
267 if ( mExtraDatasetUri.contains( groupMetadata.uri() ) )
268 {
269 mExtraDatasetUri.removeOne( groupMetadata.uri() );
270 }
271
273
274 emit dataSourceChanged();
275 return true;
276}
277
278bool QgsMeshLayer::addDatasets( const QString &path, const QDateTime &defaultReferenceTime )
279{
281
283 const bool isTemporalBefore = temporalCapabilities->hasTemporalCapabilities();
284 if ( mDatasetGroupStore->addPersistentDatasets( path ) )
285 {
286 mExtraDatasetUri.append( path );
287 QgsMeshLayerTemporalProperties *temporalProperties = qobject_cast< QgsMeshLayerTemporalProperties * >( mTemporalProperties );
288 if ( !isTemporalBefore && temporalCapabilities->hasTemporalCapabilities() )
289 {
290 mTemporalProperties->setDefaultsFromDataProviderTemporalCapabilities( temporalCapabilities );
291
292 if ( !temporalProperties->referenceTime().isValid() )
293 {
294 QDateTime referenceTime = defaultReferenceTime;
295 if ( !defaultReferenceTime.isValid() ) // If project reference time is invalid, use current date
296 referenceTime = QDateTime( QDate::currentDate(), QTime( 0, 0, 0 ), Qt::UTC );
297 temporalProperties->setReferenceTime( referenceTime, temporalCapabilities );
298 }
299
300 mTemporalProperties->setIsActive( true );
301 }
302 emit dataSourceChanged();
303 return true;
304 }
305
306 return false;
307}
308
310{
312
313 if ( mDatasetGroupStore->addDatasetGroup( std::unique_ptr< QgsMeshDatasetGroup >( datasetGroup ) ) )
314 {
315 emit dataChanged();
316 return true;
317 }
318 return false;
319}
320
321bool QgsMeshLayer::saveDataset( const QString &path, int datasetGroupIndex, QString driver )
322{
324
325 return mDatasetGroupStore->saveDatasetGroup( path, datasetGroupIndex, driver );
326}
327
329{
331
332 return mNativeMesh.get();
333}
334
336{
338
339 return mNativeMesh.get();
340}
341
342QgsTriangularMesh *QgsMeshLayer::triangularMesh( double minimumTriangleSize ) const
343{
345
346 for ( const std::unique_ptr<QgsTriangularMesh> &lod : mTriangularMeshes )
347 {
348 if ( lod && lod->averageTriangleSize() > minimumTriangleSize )
349 return lod.get();
350 }
351
352 if ( !mTriangularMeshes.empty() )
353 return mTriangularMeshes.back().get();
354 else
355 return nullptr;
356}
357
359{
361
362 return mTriangularMeshes.size();
363}
364
366{
368
369 if ( mTriangularMeshes.empty() )
370 return nullptr;
371 if ( lodIndex < 0 )
372 return mTriangularMeshes.front().get();
373
374 if ( lodIndex >= int( mTriangularMeshes.size() ) )
375 return mTriangularMeshes.back().get();
376
377 return mTriangularMeshes.at( lodIndex ).get();
378}
379
381{
383
384 // Native mesh
385 if ( !mNativeMesh )
386 {
387 // lazy loading of mesh data
388 fillNativeMesh();
389 }
390
391 // Triangular mesh
392 if ( mTriangularMeshes.empty() )
393 {
394 QgsTriangularMesh *baseMesh = new QgsTriangularMesh;
395 mTriangularMeshes.emplace_back( baseMesh );
396 }
397
398 if ( mTriangularMeshes[0].get()->update( mNativeMesh.get(), transform ) )
399 mTriangularMeshes.resize( 1 ); //if the base triangular mesh is effectivly updated, remove simplified meshes
400
401 createSimplifiedMeshes();
402}
403
404QgsMeshLayerRendererCache *QgsMeshLayer::rendererCache()
405{
407
408 return mRendererCache.get();
409}
410
417
418void QgsMeshLayer::setRendererSettings( const QgsMeshRendererSettings &settings, const bool repaint )
419{
421
422 const int oldActiveScalar = mRendererSettings.activeScalarDatasetGroup();
423 const int oldActiveVector = mRendererSettings.activeVectorDatasetGroup();
424 mRendererSettings = settings;
425
426 if ( oldActiveScalar != mRendererSettings.activeScalarDatasetGroup() )
427 emit activeScalarDatasetGroupChanged( mRendererSettings.activeScalarDatasetGroup() );
428
429 if ( oldActiveVector != mRendererSettings.activeVectorDatasetGroup() )
430 emit activeVectorDatasetGroupChanged( mRendererSettings.activeVectorDatasetGroup() );
431
432 emit rendererChanged();
434
435 if ( repaint )
436 {
438 }
439}
440
447
449{
451
452 mTimeSettings = settings;
453 emit timeSettingsChanged();
454}
455
456QString QgsMeshLayer::formatTime( double hours )
457{
459
460 if ( dataProvider() && dataProvider()->temporalCapabilities()->hasReferenceTime() )
461 return QgsMeshLayerUtils::formatTime( hours, mTemporalProperties->referenceTime(), mTimeSettings );
462 else
463 return QgsMeshLayerUtils::formatTime( hours, QDateTime(), mTimeSettings );
464}
465
467{
469
470 return mDatasetGroupStore->datasetGroupCount();
471}
472
474{
476
477 return mDatasetGroupStore->extraDatasetGroupCount();
478}
479
481{
483
484 return mDatasetGroupStore->datasetGroupIndexes();
485}
486
488{
490
491 return mDatasetGroupStore->enabledDatasetGroupIndexes();
492}
493
495{
497
498 return mDatasetGroupStore->datasetGroupMetadata( index );
499}
500
502{
504
505 return mDatasetGroupStore->datasetCount( index.group() );
506}
507
509{
511
512 return mDatasetGroupStore->datasetMetadata( index );
513}
514
516{
518
519 return mDatasetGroupStore->datasetValue( index, valueIndex );
520}
521
522QgsMeshDataBlock QgsMeshLayer::datasetValues( const QgsMeshDatasetIndex &index, int valueIndex, int count ) const
523{
525
526 return mDatasetGroupStore->datasetValues( index, valueIndex, count );
527}
528
529QgsMesh3DDataBlock QgsMeshLayer::dataset3dValues( const QgsMeshDatasetIndex &index, int faceIndex, int count ) const
530{
532
533 return mDatasetGroupStore->dataset3dValues( index, faceIndex, count );
534}
535
536QgsMeshDataBlock QgsMeshLayer::areFacesActive( const QgsMeshDatasetIndex &index, int faceIndex, int count ) const
537{
539
540 return mDatasetGroupStore->areFacesActive( index, faceIndex, count );
541}
542
543bool QgsMeshLayer::isFaceActive( const QgsMeshDatasetIndex &index, int faceIndex ) const
544{
546
547 return mDatasetGroupStore->isFaceActive( index, faceIndex );
548}
549
550QgsMeshDatasetValue QgsMeshLayer::datasetValue( const QgsMeshDatasetIndex &index, const QgsPointXY &point, double searchRadius ) const
551{
553
555 const QgsTriangularMesh *mesh = triangularMesh();
556
557 if ( mesh && index.isValid() )
558 {
560 {
561 const QgsRectangle searchRectangle( point.x() - searchRadius, point.y() - searchRadius, point.x() + searchRadius, point.y() + searchRadius );
562 return dataset1dValue( index, point, searchRadius );
563 }
564 const int faceIndex = mesh->faceIndexForPoint_v2( point );
565 if ( faceIndex >= 0 )
566 {
567 const int nativeFaceIndex = mesh->trianglesToNativeFaces().at( faceIndex );
569 if ( isFaceActive( index, nativeFaceIndex ) )
570 {
571 switch ( dataType )
572 {
574 {
575 value = datasetValue( index, nativeFaceIndex );
576 }
577 break;
578
580 {
581 const QgsMeshFace &face = mesh->triangles()[faceIndex];
582 const int v1 = face[0], v2 = face[1], v3 = face[2];
583 const QgsPoint p1 = mesh->vertices()[v1], p2 = mesh->vertices()[v2], p3 = mesh->vertices()[v3];
584 const QgsMeshDatasetValue val1 = datasetValue( index, v1 );
585 const QgsMeshDatasetValue val2 = datasetValue( index, v2 );
586 const QgsMeshDatasetValue val3 = datasetValue( index, v3 );
587 const double x = QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.x(), val2.x(), val3.x(), point );
588 double y = std::numeric_limits<double>::quiet_NaN();
589 const bool isVector = datasetGroupMetadata( index ).isVector();
590 if ( isVector )
591 y = QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.y(), val2.y(), val3.y(), point );
592
593 value = QgsMeshDatasetValue( x, y );
594 }
595 break;
596
598 {
599 const QgsMesh3DAveragingMethod *avgMethod = mRendererSettings.averagingMethod();
600 if ( avgMethod )
601 {
602 const QgsMesh3DDataBlock block3d = dataset3dValues( index, nativeFaceIndex, 1 );
603 const QgsMeshDataBlock block2d = avgMethod->calculate( block3d );
604 if ( block2d.isValid() )
605 {
606 value = block2d.value( 0 );
607 }
608 }
609 }
610 break;
611
612 default:
613 break;
614 }
615 }
616 }
617 }
618
619 return value;
620}
621
623{
625
626 QgsMesh3DDataBlock block3d;
627
628 const QgsTriangularMesh *baseTriangularMesh = triangularMesh();
629
630 if ( baseTriangularMesh && dataProvider() && dataProvider()->isValid() && index.isValid() )
631 {
634 {
635 const int faceIndex = baseTriangularMesh->faceIndexForPoint_v2( point );
636 if ( faceIndex >= 0 )
637 {
638 const int nativeFaceIndex = baseTriangularMesh->trianglesToNativeFaces().at( faceIndex );
639 block3d = dataset3dValues( index, nativeFaceIndex, 1 );
640 }
641 }
642 }
643 return block3d;
644}
645
646QgsMeshDatasetValue QgsMeshLayer::dataset1dValue( const QgsMeshDatasetIndex &index, const QgsPointXY &point, double searchRadius ) const
647{
649
651 QgsPointXY projectedPoint;
652 const int selectedIndex = closestEdge( point, searchRadius, projectedPoint );
653 const QgsTriangularMesh *mesh = triangularMesh();
654 if ( selectedIndex >= 0 )
655 {
657 switch ( dataType )
658 {
660 {
661 value = datasetValue( index, selectedIndex );
662 }
663 break;
664
666 {
667 const QgsMeshEdge &edge = mesh->edges()[selectedIndex];
668 const int v1 = edge.first, v2 = edge.second;
669 const QgsPoint p1 = mesh->vertices()[v1], p2 = mesh->vertices()[v2];
670 const QgsMeshDatasetValue val1 = datasetValue( index, v1 );
671 const QgsMeshDatasetValue val2 = datasetValue( index, v2 );
672 const double edgeLength = p1.distance( p2 );
673 const double dist1 = p1.distance( projectedPoint.x(), projectedPoint.y() );
674 value = QgsMeshLayerUtils::interpolateFromVerticesData( dist1 / edgeLength, val1, val2 );
675 }
676 break;
677 default:
678 break;
679 }
680 }
681
682 return value;
683}
684
686{
688
689 if ( mDataProvider )
690 mDataProvider->setTransformContext( transformContext );
692}
693
694QgsMeshDatasetIndex QgsMeshLayer::datasetIndexAtTime( const QgsDateTimeRange &timeRange, int datasetGroupIndex ) const
695{
697
698 if ( !mTemporalProperties->isActive() )
699 return QgsMeshDatasetIndex( datasetGroupIndex, -1 );
700
701 const QDateTime layerReferenceTime = mTemporalProperties->referenceTime();
702 QDateTime utcTime = timeRange.begin();
703 if ( utcTime.timeSpec() != Qt::UTC )
704 utcTime.setTimeSpec( Qt::UTC );
705 const qint64 startTime = layerReferenceTime.msecsTo( utcTime );
706
707 return mDatasetGroupStore->datasetIndexAtTime( startTime, datasetGroupIndex, mTemporalProperties->matchingMethod() );
708}
709
710QgsMeshDatasetIndex QgsMeshLayer::datasetIndexAtRelativeTime( const QgsInterval &relativeTime, int datasetGroupIndex ) const
711{
713
714 return mDatasetGroupStore->datasetIndexAtTime( relativeTime.seconds() * 1000, datasetGroupIndex, mTemporalProperties->matchingMethod() );
715}
716
717QList<QgsMeshDatasetIndex> QgsMeshLayer::datasetIndexInRelativeTimeInterval( const QgsInterval &startRelativeTime, const QgsInterval &endRelativeTime, int datasetGroupIndex ) const
718{
720
721 qint64 usedRelativeTime1 = startRelativeTime.seconds() * 1000;
722 qint64 usedRelativeTime2 = endRelativeTime.seconds() * 1000;
723
724 //adjust relative time if layer reference time is different from provider reference time
725 if ( mTemporalProperties->referenceTime().isValid() && mDataProvider && mDataProvider->isValid() && mTemporalProperties->referenceTime() != mDataProvider->temporalCapabilities()->referenceTime() )
726 {
727 usedRelativeTime1 = usedRelativeTime1 + mTemporalProperties->referenceTime().msecsTo( mDataProvider->temporalCapabilities()->referenceTime() );
728 usedRelativeTime2 = usedRelativeTime2 + mTemporalProperties->referenceTime().msecsTo( mDataProvider->temporalCapabilities()->referenceTime() );
729 }
730
731 return mDatasetGroupStore->datasetIndexInTimeInterval( usedRelativeTime1, usedRelativeTime2, datasetGroupIndex );
732}
733
734void QgsMeshLayer::applyClassificationOnScalarSettings( const QgsMeshDatasetGroupMetadata &meta, QgsMeshRendererScalarSettings &scalarSettings ) const
735{
737
738 if ( meta.extraOptions().contains( u"classification"_s ) )
739 {
740 QgsColorRampShader colorRampShader = scalarSettings.colorRampShader();
741 QgsColorRamp *colorRamp = colorRampShader.sourceColorRamp();
742 const QStringList classes = meta.extraOptions()[u"classification"_s].split( u";;"_s );
743
744 QString units;
745 if ( meta.extraOptions().contains( u"units"_s ) )
746 units = meta.extraOptions()[u"units"_s];
747
748 QVector<QVector<double>> bounds;
749 for ( const QString &classe : classes )
750 {
751 const QStringList boundsStr = classe.split( ',' );
752 QVector<double> bound;
753 for ( const QString &boundStr : boundsStr )
754 bound.append( boundStr.toDouble() );
755 bounds.append( bound );
756 }
757
758 if ( ( bounds.count() == 1 && bounds.first().count() > 2 ) || // at least a class with two value
759 ( bounds.count() > 1 ) ) // or at least two classes
760 {
761 const QVector<double> firstClass = bounds.first();
762 const QVector<double> lastClass = bounds.last();
763 const double minValue = firstClass.count() > 1 ? ( firstClass.first() + firstClass.last() ) / 2 : firstClass.first();
764 const double maxValue = lastClass.count() > 1 ? ( lastClass.first() + lastClass.last() ) / 2 : lastClass.first();
765 const double diff = maxValue - minValue;
766 QList<QgsColorRampShader::ColorRampItem> colorRampItemlist;
767 for ( int i = 0; i < bounds.count(); ++i )
768 {
769 const QVector<double> &boundClass = bounds.at( i );
770 QgsColorRampShader::ColorRampItem item;
771 item.value = i + 1;
772 if ( !boundClass.isEmpty() )
773 {
774 const double scalarValue = ( boundClass.first() + boundClass.last() ) / 2;
775 item.color = colorRamp->color( ( scalarValue - minValue ) / diff );
776 if ( i != 0 && i < bounds.count() - 1 ) //The first and last labels are treated after
777 {
778 item.label = QString( ( "%1 - %2 %3" ) ).arg( QString::number( boundClass.first() ) ).arg( QString::number( boundClass.last() ) ).arg( units );
779 }
780 }
781 colorRampItemlist.append( item );
782 }
783 //treat first and last labels
784 if ( firstClass.count() == 1 )
785 colorRampItemlist.first().label = QObject::tr( "below %1 %2" ).arg( QString::number( firstClass.first() ) ).arg( units );
786 else
787 {
788 colorRampItemlist.first().label = QString( ( "%1 - %2 %3" ) ).arg( QString::number( firstClass.first() ) ).arg( QString::number( firstClass.last() ) ).arg( units );
789 }
790
791 if ( lastClass.count() == 1 )
792 colorRampItemlist.last().label = QObject::tr( "above %1 %2" ).arg( QString::number( lastClass.first() ) ).arg( units );
793 else
794 {
795 colorRampItemlist.last().label = QString( ( "%1 - %2 %3" ) ).arg( QString::number( lastClass.first() ) ).arg( QString::number( lastClass.last() ) ).arg( units );
796 }
797
798 colorRampShader.setMinimumValue( 0 );
799 colorRampShader.setMaximumValue( colorRampItemlist.count() - 1 );
800 scalarSettings.setClassificationMinimumMaximum( 0, colorRampItemlist.count() - 1 );
801 colorRampShader.setColorRampItemList( colorRampItemlist );
804 }
805
806 scalarSettings.setColorRampShader( colorRampShader );
808 }
809}
810
812{
814
815 if ( mTemporalProperties->isActive() )
816 return datasetIndexAtTime( timeRange, group >= 0 ? group : mRendererSettings.activeScalarDatasetGroup() );
817 else
818 return QgsMeshDatasetIndex( group >= 0 ? group : mRendererSettings.activeScalarDatasetGroup(), mStaticScalarDatasetIndex );
819}
820
822{
824
825 if ( mTemporalProperties->isActive() )
826 return datasetIndexAtTime( timeRange, group >= 0 ? group : mRendererSettings.activeVectorDatasetGroup() );
827 else
828 return QgsMeshDatasetIndex( group >= 0 ? group : mRendererSettings.activeVectorDatasetGroup(), mStaticVectorDatasetIndex );
829}
830
831void QgsMeshLayer::fillNativeMesh()
832{
834
835 Q_ASSERT( !mNativeMesh );
836
837 mNativeMesh = std::make_unique<QgsMesh>();
838
839 if ( !( dataProvider() && dataProvider()->isValid() ) )
840 return;
841
842 dataProvider()->populateMesh( mNativeMesh.get() );
843}
844
845void QgsMeshLayer::onDatasetGroupsAdded( const QList<int> &datasetGroupIndexes )
846{
848
849 // assign default style to new dataset groups
850 for ( int datasetGroupIndex : datasetGroupIndexes )
851 {
852 if ( !mRendererSettings.hasSettings( datasetGroupIndex ) )
853 assignDefaultStyleToDatasetGroup( datasetGroupIndex );
854 }
855
856 temporalProperties()->setIsActive( mDatasetGroupStore->hasTemporalCapabilities() );
857 emit rendererChanged();
858}
859
860void QgsMeshLayer::onMeshEdited()
861{
863
865 emit layerModified();
868}
869
871{
873
874 return mDatasetGroupStore->datasetGroupTreeItem();
875}
876
878{
880
881 mDatasetGroupStore->setDatasetGroupTreeItem( rootItem );
882 updateActiveDatasetGroups();
883}
884
886{
888
889 return QgsMeshDatasetIndex( group >= 0 ? group : mRendererSettings.activeVectorDatasetGroup(), mStaticVectorDatasetIndex );
890}
891
892void QgsMeshLayer::setReferenceTime( const QDateTime &referenceTime )
893{
895
896 if ( auto *lDataProvider = dataProvider() )
897 mTemporalProperties->setReferenceTime( referenceTime, lDataProvider->temporalCapabilities() );
898 else
899 mTemporalProperties->setReferenceTime( referenceTime, nullptr );
900}
901
903{
905
906 mTemporalProperties->setMatchingMethod( matchingMethod );
907}
908
909int QgsMeshLayer::closestEdge( const QgsPointXY &point, double searchRadius, QgsPointXY &projectedPoint ) const
910{
912
913 const QgsRectangle searchRectangle( point.x() - searchRadius, point.y() - searchRadius, point.x() + searchRadius, point.y() + searchRadius );
914 const QgsTriangularMesh *mesh = triangularMesh();
915 // search for the closest edge in search area from point
916 const QList<int> edgeIndexes = mesh->edgeIndexesForRectangle( searchRectangle );
917 int selectedIndex = -1;
918 projectedPoint = QgsPointXY();
919 if ( mesh->contains( QgsMesh::Edge ) && mDataProvider->isValid() )
920 {
921 double sqrMaxDistFromPoint = pow( searchRadius, 2 );
922 for ( const int edgeIndex : edgeIndexes )
923 {
924 const QgsMeshEdge &edge = mesh->edges().at( edgeIndex );
925 const QgsMeshVertex &vertex1 = mesh->vertices()[edge.first];
926 const QgsMeshVertex &vertex2 = mesh->vertices()[edge.second];
927 QgsPointXY projPoint;
928 const double sqrDist = point.sqrDistToSegment( vertex1.x(), vertex1.y(), vertex2.x(), vertex2.y(), projPoint, 0 );
929 if ( sqrDist < sqrMaxDistFromPoint )
930 {
931 selectedIndex = edgeIndex;
932 projectedPoint = projPoint;
933 sqrMaxDistFromPoint = sqrDist;
934 }
935 }
936 }
937
938 return selectedIndex;
939}
940
941int QgsMeshLayer::closestVertex( const QgsPointXY &point, double searchRadius, QgsPointXY &projectedPoint ) const
942{
944
945 const QgsTriangularMesh *mesh = triangularMesh();
946 int selectedIndex = -1;
947 projectedPoint = QgsPointXY();
948 if ( !mesh )
949 return selectedIndex;
950
951 const QgsRectangle rectangle( point.x() - searchRadius, point.y() - searchRadius, point.x() + searchRadius, point.y() + searchRadius );
952 double maxDistance = searchRadius;
953 //attempt to snap on edges's vertices
954 const QList<int> edgeIndexes = mesh->edgeIndexesForRectangle( rectangle );
955 for ( const int edgeIndex : edgeIndexes )
956 {
957 const QgsMeshEdge &edge = mesh->edges().at( edgeIndex );
958 const QgsMeshVertex &vertex1 = mesh->vertices()[edge.first];
959 const QgsMeshVertex &vertex2 = mesh->vertices()[edge.second];
960 const double dist1 = point.distance( vertex1 );
961 const double dist2 = point.distance( vertex2 );
962 if ( dist1 < maxDistance )
963 {
964 maxDistance = dist1;
965 projectedPoint = vertex1;
966 selectedIndex = edge.first;
967 }
968 if ( dist2 < maxDistance )
969 {
970 maxDistance = dist2;
971 projectedPoint = vertex2;
972 selectedIndex = edge.second;
973 }
974 }
975
976 //attempt to snap on face's vertices
977 const QList<int> faceIndexes = mesh->faceIndexesForRectangle( rectangle );
978 for ( const int faceIndex : faceIndexes )
979 {
980 const QgsMeshFace &face = mesh->triangles().at( faceIndex );
981 for ( int i = 0; i < 3; ++i )
982 {
983 const QgsMeshVertex &vertex = mesh->vertices()[face.at( i )];
984 const double dist = point.distance( vertex );
985 if ( dist < maxDistance )
986 {
987 maxDistance = dist;
988 projectedPoint = vertex;
989 selectedIndex = face.at( i );
990 }
991 }
992 }
993
994 return selectedIndex;
995}
996
997int QgsMeshLayer::closestFace( const QgsPointXY &point, double searchRadius, QgsPointXY &projectedPoint ) const
998{
1000
1001 const QgsTriangularMesh *mesh = triangularMesh();
1002 int selectedIndex = -1;
1003 projectedPoint = QgsPointXY();
1004 if ( !mesh )
1005 return selectedIndex;
1006
1007 const QgsRectangle rectangle( point.x() - searchRadius, point.y() - searchRadius, point.x() + searchRadius, point.y() + searchRadius );
1008 double maxDistance = std::numeric_limits<double>::max();
1009
1010 const QList<int> faceIndexes = mesh->faceIndexesForRectangle( rectangle );
1011 for ( const int faceIndex : faceIndexes )
1012 {
1013 const int nativefaceIndex = mesh->trianglesToNativeFaces().at( faceIndex );
1014 if ( nativefaceIndex < 0 || nativefaceIndex >= mesh->faceCentroids().count() )
1015 continue;
1016 const QgsPointXY centroid = mesh->faceCentroids()[nativefaceIndex];
1017 const double dist = point.distance( centroid );
1018 if ( dist < maxDistance )
1019 {
1020 maxDistance = dist;
1021 projectedPoint = centroid;
1022 selectedIndex = nativefaceIndex;
1023 }
1024 }
1025
1026 return selectedIndex;
1027}
1028
1030{
1032
1033 mDatasetGroupStore->resetDatasetGroupTreeItem();
1034 updateActiveDatasetGroups();
1035}
1036
1038{
1040
1041 if ( !mDataProvider )
1042 return QgsInterval();
1043 const int groupCount = mDataProvider->datasetGroupCount();
1044 for ( int i = 0; i < groupCount; ++i )
1045 {
1046 const qint64 timeStep = mDataProvider->temporalCapabilities()->firstTimeStepDuration( i );
1047 if ( timeStep > 0 )
1049 }
1050
1051 return QgsInterval();
1052}
1053
1055{
1057
1058 const qint64 time = mDatasetGroupStore->datasetRelativeTime( index );
1059
1060 if ( time == INVALID_MESHLAYER_TIME )
1061 return QgsInterval();
1062 else
1064}
1065
1067{
1069
1070 return mDatasetGroupStore->datasetRelativeTime( index );
1071}
1072
1073static QString detailsErrorMessage( const QgsMeshEditingError &error )
1074{
1075 QString message;
1076
1077 switch ( error.errorType )
1078 {
1080 break;
1082 message = QObject::tr( "Face %1 invalid" ).arg( error.elementIndex );
1083 break;
1085 message = QObject::tr( "Too many vertices for face %1" ).arg( error.elementIndex );
1086 break;
1088 message = QObject::tr( "Face %1 is flat" ).arg( error.elementIndex );
1089 break;
1091 message = QObject::tr( "Vertex %1 is a unique shared vertex" ).arg( error.elementIndex );
1092 break;
1094 message = QObject::tr( "Vertex %1 is invalid" ).arg( error.elementIndex );
1095 break;
1097 message = QObject::tr( "Face %1 is manifold" ).arg( error.elementIndex );
1098 break;
1099 }
1100
1101 return message;
1102}
1103
1105{
1107
1109 return startFrameEditing( transform, error, false );
1110}
1111
1113{
1115
1116 if ( !supportsEditing() )
1117 {
1118 QgsMessageLog::logMessage( QObject::tr( "Mesh layer \"%1\" not support mesh editing" ).arg( name() ) );
1119 return false;
1120 }
1121
1122 if ( mMeshEditor )
1123 {
1124 QgsMessageLog::logMessage( QObject::tr( "Mesh layer \"%1\" already in editing mode" ).arg( name() ) );
1125 return false;
1126 }
1127
1128 mSimplificationSettings.setEnabled( false );
1129
1130 updateTriangularMesh( transform );
1131
1132 mMeshEditor = new QgsMeshEditor( this );
1133
1134 if ( fixErrors )
1135 {
1136 invalidateRendererCache(); // fixing errors could lead to remove faces/vertices
1137 error = mMeshEditor->initializeWithErrorsFix();
1138 }
1139 else
1140 error = mMeshEditor->initialize();
1141
1142 if ( error.errorType != Qgis::MeshEditingErrorType::NoError )
1143 {
1144 mMeshEditor->deleteLater();
1145 mMeshEditor = nullptr;
1146
1147 QgsMessageLog::logMessage( QObject::tr( "Unable to start editing of mesh layer \"%1\": %2" ).arg( name(), detailsErrorMessage( error ) ), QString(), Qgis::MessageLevel::Critical );
1148 return false;
1149 }
1150
1151 // During editing, we don't need anymore the provider data. Mesh frame data is stored in the mesh editor.
1152 mDataProvider->close();
1153
1154 // All dataset group are removed and replace by a unique virtual dataset group that provide vertices elevation value.
1155 mExtraDatasetUri.clear();
1156 mDatasetGroupStore = std::make_unique<QgsMeshDatasetGroupStore>( this );
1157
1158 mDatasetGroupStore->addDatasetGroup( mMeshEditor->createZValueDatasetGroup() );
1159
1161
1162 connect( mMeshEditor, &QgsMeshEditor::meshEdited, this, &QgsMeshLayer::onMeshEdited );
1163
1164 emit dataChanged();
1165 emit editingStarted();
1166
1167 return true;
1168}
1169
1170bool QgsMeshLayer::commitFrameEditing( const QgsCoordinateTransform &transform, bool continueEditing )
1171{
1173
1175 QString detailsError;
1176 if ( !mMeshEditor->checkConsistency( error ) )
1177 {
1178 if ( error.errorType == Qgis::MeshEditingErrorType::NoError )
1179 detailsError = tr( "Unknown inconsistent mesh error" );
1180 }
1181 else
1182 {
1183 error = QgsTopologicalMesh::checkTopology( *mNativeMesh, mMeshEditor->maximumVerticesPerFace() );
1184 detailsError = detailsErrorMessage( error );
1185 }
1186
1187 if ( !detailsError.isEmpty() )
1188 {
1189 QgsMessageLog::logMessage( QObject::tr( "Edited mesh layer \"%1\" can't be save due to an error: %2" ).arg( name(), detailsError ), QString(), Qgis::MessageLevel::Critical );
1190 return false;
1191 }
1192
1193 stopFrameEditing( transform );
1194
1195 if ( !mDataProvider )
1196 return false;
1197
1198 const bool res = mDataProvider->saveMeshFrame( *mNativeMesh.get() );
1199
1200 if ( continueEditing )
1201 {
1202 mMeshEditor->initialize();
1203 emit layerModified();
1204 return res;
1205 }
1206
1207 mMeshEditor->deleteLater();
1208 mMeshEditor = nullptr;
1209 emit editingStopped();
1210
1211 mDataProvider->reloadData();
1212 mDataProvider->populateMesh( mNativeMesh.get() );
1213 mDatasetGroupStore = std::make_unique<QgsMeshDatasetGroupStore>( this );
1214 mDatasetGroupStore->setPersistentProvider( mDataProvider.get(), QStringList() );
1216 return true;
1217}
1218
1219bool QgsMeshLayer::rollBackFrameEditing( const QgsCoordinateTransform &transform, bool continueEditing )
1220{
1222
1223 stopFrameEditing( transform );
1224
1225 if ( !mDataProvider )
1226 return false;
1227
1228 mTriangularMeshes.clear();
1229 mDataProvider->reloadData();
1230 mDataProvider->populateMesh( mNativeMesh.get() );
1231 updateTriangularMesh( transform );
1234
1235 if ( continueEditing )
1236 {
1237 mMeshEditor->resetTriangularMesh( triangularMesh() );
1238 return mMeshEditor->initialize() == QgsMeshEditingError();
1239 }
1240 else
1241 {
1242 mMeshEditor->deleteLater();
1243 mMeshEditor = nullptr;
1244 emit editingStopped();
1245
1246 mDatasetGroupStore = std::make_unique<QgsMeshDatasetGroupStore>( this );
1247 mDatasetGroupStore->setPersistentProvider( mDataProvider.get(), QStringList() );
1249 emit dataChanged();
1250 return true;
1251 }
1252}
1253
1255{
1257
1258 if ( !mMeshEditor )
1259 return;
1260
1261 mMeshEditor->stopEditing();
1262 mTriangularMeshes.at( 0 )->update( mNativeMesh.get(), transform );
1264}
1265
1266bool QgsMeshLayer::reindex( const QgsCoordinateTransform &transform, bool renumber )
1267{
1269
1270 if ( !mMeshEditor )
1271 return false;
1272
1273 if ( !mMeshEditor->reindex( renumber ) )
1274 return false;
1275
1276 mTriangularMeshes.clear();
1277 mTriangularMeshes.emplace_back( new QgsTriangularMesh );
1278 mTriangularMeshes.at( 0 )->update( mNativeMesh.get(), transform );
1280 mMeshEditor->resetTriangularMesh( mTriangularMeshes.at( 0 ).get() );
1281
1282 return true;
1283}
1284
1286{
1288
1289 return mMeshEditor;
1290}
1291
1293{
1295
1296 if ( mMeshEditor )
1297 return mMeshEditor->isModified();
1298
1299 return false;
1300}
1301
1303{
1305
1306 switch ( type )
1307 {
1309 return meshVertexCount() != 0;
1311 return meshEdgeCount() != 0;
1313 return meshFaceCount() != 0;
1314 }
1315 return false;
1316}
1317
1319{
1321
1322 if ( mMeshEditor )
1323 return mMeshEditor->validVerticesCount();
1324 else if ( mDataProvider )
1325 return mDataProvider->vertexCount();
1326 else
1327 return 0;
1328}
1329
1331{
1333
1334 if ( mMeshEditor )
1335 return mMeshEditor->validFacesCount();
1336 else if ( mDataProvider )
1337 return mDataProvider->faceCount();
1338 else
1339 return 0;
1340}
1341
1343{
1345
1346 if ( mMeshEditor )
1347 return mNativeMesh->edgeCount();
1348 else if ( mDataProvider )
1349 return mDataProvider->edgeCount();
1350 else
1351 return 0;
1352}
1353
1354void QgsMeshLayer::updateActiveDatasetGroups()
1355{
1357
1358 QgsMeshDatasetGroupTreeItem *treeItem = mDatasetGroupStore->datasetGroupTreeItem();
1359
1360 if ( !mDatasetGroupStore->datasetGroupTreeItem() )
1361 return;
1362
1364 const int oldActiveScalar = settings.activeScalarDatasetGroup();
1365 const int oldActiveVector = settings.activeVectorDatasetGroup();
1366
1367 QgsMeshDatasetGroupTreeItem *activeScalarItem = treeItem->childFromDatasetGroupIndex( oldActiveScalar );
1368
1369 if ( !activeScalarItem && treeItem->childCount() > 0 && oldActiveScalar != -1 )
1370 activeScalarItem = treeItem->child( 0 );
1371
1372 if ( activeScalarItem && !activeScalarItem->isEnabled() )
1373 {
1374 for ( int i = 0; i < treeItem->childCount(); ++i )
1375 {
1376 activeScalarItem = treeItem->child( i );
1377 if ( activeScalarItem->isEnabled() )
1378 break;
1379 else
1380 activeScalarItem = nullptr;
1381 }
1382 }
1383
1384 if ( activeScalarItem )
1385 settings.setActiveScalarDatasetGroup( activeScalarItem->datasetGroupIndex() );
1386 else
1387 settings.setActiveScalarDatasetGroup( -1 );
1388
1389 QgsMeshDatasetGroupTreeItem *activeVectorItem = treeItem->childFromDatasetGroupIndex( oldActiveVector );
1390
1391 if ( !( activeVectorItem && activeVectorItem->isEnabled() ) )
1392 settings.setActiveVectorDatasetGroup( -1 );
1393
1394 setRendererSettings( settings );
1395
1396 if ( oldActiveScalar != settings.activeScalarDatasetGroup() )
1398 if ( oldActiveVector != settings.activeVectorDatasetGroup() )
1400}
1401
1402QgsMeshRendererSettings QgsMeshLayer::accordSymbologyWithGroupName( const QgsMeshRendererSettings &settings, const QMap<QString, int> &nameToIndex )
1403{
1404 QString activeScalarName;
1405 QString activeVectorName;
1406 QgsMeshRendererSettings consistentSettings = settings;
1407 int activeScalar = consistentSettings.activeScalarDatasetGroup();
1408 int activeVector = consistentSettings.activeVectorDatasetGroup();
1409
1410 for ( auto it = nameToIndex.constBegin(); it != nameToIndex.constEnd(); ++it )
1411 {
1412 int index = it.value();
1413 const QString name = it.key();
1414 int globalIndex = mDatasetGroupStore->indexFromGroupName( name );
1415 if ( globalIndex >= 0 )
1416 {
1417 QgsMeshRendererScalarSettings scalarSettings = settings.scalarSettings( index );
1418 consistentSettings.setScalarSettings( globalIndex, scalarSettings );
1419 if ( settings.hasVectorSettings( it.value() ) && mDatasetGroupStore->datasetGroupMetadata( globalIndex ).isVector() )
1420 {
1421 QgsMeshRendererVectorSettings vectorSettings = settings.vectorSettings( index );
1422 consistentSettings.setVectorSettings( globalIndex, vectorSettings );
1423 }
1424 }
1425 else
1426 {
1427 consistentSettings.removeScalarSettings( index );
1428 if ( settings.hasVectorSettings( it.value() ) )
1429 consistentSettings.removeVectorSettings( index );
1430 }
1431
1432 if ( index == activeScalar )
1433 activeScalarName = name;
1434 if ( index == activeVector )
1435 activeVectorName = name;
1436 }
1437
1438 const QList<int> globalIndexes = datasetGroupsIndexes();
1439 for ( int globalIndex : globalIndexes )
1440 {
1441 const QString name = mDatasetGroupStore->groupName( globalIndex );
1442 if ( !nameToIndex.contains( name ) )
1443 {
1444 consistentSettings.setScalarSettings( globalIndex, mRendererSettings.scalarSettings( globalIndex ) );
1445 if ( mDatasetGroupStore->datasetGroupMetadata( globalIndex ).isVector() )
1446 {
1447 consistentSettings.setVectorSettings( globalIndex, mRendererSettings.vectorSettings( globalIndex ) );
1448 }
1449 }
1450 }
1451
1452 if ( !activeScalarName.isEmpty() )
1453 consistentSettings.setActiveScalarDatasetGroup( mDatasetGroupStore->indexFromGroupName( activeScalarName ) );
1454 if ( !activeVectorName.isEmpty() )
1455 consistentSettings.setActiveVectorDatasetGroup( mDatasetGroupStore->indexFromGroupName( activeVectorName ) );
1456
1457 return consistentSettings;
1458}
1459
1460void QgsMeshLayer::setDataSourcePrivate( const QString &dataSource, const QString &baseName, const QString &provider, const QgsDataProvider::ProviderOptions &options, Qgis::DataProviderReadFlags flags )
1461{
1463
1464 mDataSource = dataSource;
1465 mLayerName = baseName;
1466 setProviderType( provider );
1467
1468 if ( !mDataSource.isEmpty() && !provider.isEmpty() )
1469 setDataProvider( provider, options, flags );
1470}
1471
1472QgsPointXY QgsMeshLayer::snapOnElement( QgsMesh::ElementType elementType, const QgsPointXY &point, double searchRadius )
1473{
1475
1476 QgsPointXY projectedPoint;
1477 closestElement( elementType, point, searchRadius, projectedPoint );
1478 return projectedPoint;
1479}
1480
1481int QgsMeshLayer::closestElement( QgsMesh::ElementType elementType, const QgsPointXY &point, double searchRadius, QgsPointXY &projectedPoint ) const
1482{
1484
1485 switch ( elementType )
1486 {
1487 case QgsMesh::Vertex:
1488 return closestVertex( point, searchRadius, projectedPoint );
1489 case QgsMesh::Edge:
1490 return closestEdge( point, searchRadius, projectedPoint );
1491 case QgsMesh::Face:
1492 return closestFace( point, searchRadius, projectedPoint );
1493 }
1494 return -1;
1495}
1496
1498{
1500
1501 if ( !mNativeMesh )
1502 {
1503 // lazy loading of mesh data
1504 fillNativeMesh();
1505 }
1506
1507 QList<int> ret;
1508
1509 if ( !mNativeMesh )
1510 return ret;
1511
1512 QgsExpressionContext context;
1513 std::unique_ptr<QgsExpressionContextScope> expScope( QgsExpressionContextUtils::meshExpressionScope( QgsMesh::Vertex ) );
1514 context.appendScope( expScope.release() );
1515 context.lastScope()->setVariable( u"_native_mesh"_s, QVariant::fromValue( *mNativeMesh ) );
1516
1517 expression.prepare( &context );
1518
1519 for ( int i = 0; i < mNativeMesh->vertexCount(); ++i )
1520 {
1521 context.lastScope()->setVariable( u"_mesh_vertex_index"_s, i, false );
1522
1523 if ( expression.evaluate( &context ).toBool() )
1524 ret.append( i );
1525 }
1526
1527 return ret;
1528}
1529
1531{
1533
1534 if ( !mNativeMesh )
1535 {
1536 // lazy loading of mesh data
1537 fillNativeMesh();
1538 }
1539
1540 QList<int> ret;
1541
1542 if ( !mNativeMesh )
1543 return ret;
1544
1545 QgsExpressionContext context;
1546 std::unique_ptr<QgsExpressionContextScope> expScope( QgsExpressionContextUtils::meshExpressionScope( QgsMesh::Face ) );
1547 context.appendScope( expScope.release() );
1548 context.lastScope()->setVariable( u"_native_mesh"_s, QVariant::fromValue( *mNativeMesh ) );
1549
1550 expression.prepare( &context );
1551
1552 for ( int i = 0; i < mNativeMesh->faceCount(); ++i )
1553 {
1554 context.lastScope()->setVariable( u"_mesh_face_index"_s, i, false );
1555
1556 if ( expression.evaluate( &context ).toBool() )
1557 ret.append( i );
1558 }
1559
1560 return ret;
1561}
1562
1564{
1566
1567 return QgsMeshDatasetIndex( group >= 0 ? group : mRendererSettings.activeScalarDatasetGroup(), mStaticScalarDatasetIndex );
1568}
1569
1571{
1573
1574 const int oldActiveVector = mRendererSettings.activeVectorDatasetGroup();
1575
1576 mStaticVectorDatasetIndex = staticVectorDatasetIndex.dataset();
1577 mRendererSettings.setActiveVectorDatasetGroup( staticVectorDatasetIndex.group() );
1578
1579 if ( oldActiveVector != mRendererSettings.activeVectorDatasetGroup() )
1580 emit activeVectorDatasetGroupChanged( mRendererSettings.activeVectorDatasetGroup() );
1581}
1582
1584{
1586
1587 const int oldActiveScalar = mRendererSettings.activeScalarDatasetGroup();
1588
1589 mStaticScalarDatasetIndex = staticScalarDatasetIndex.dataset();
1590 mRendererSettings.setActiveScalarDatasetGroup( staticScalarDatasetIndex.group() );
1591
1592 if ( oldActiveScalar != mRendererSettings.activeScalarDatasetGroup() )
1593 emit activeScalarDatasetGroupChanged( mRendererSettings.activeScalarDatasetGroup() );
1594}
1595
1602
1604{
1606
1607 mSimplificationSettings = simplifySettings;
1608}
1609
1610static QgsColorRamp *_createDefaultColorRamp()
1611{
1612 QgsColorRamp *ramp = QgsStyle::defaultStyle()->colorRamp( u"Plasma"_s );
1613 if ( ramp )
1614 return ramp;
1615
1616 // definition of "Plasma" color ramp (in case it is not available in the style for some reason)
1617 QVariantMap props;
1618 props["color1"] = "13,8,135,255";
1619 props["color2"] = "240,249,33,255";
1620 props["stops"] = "0.0196078;27,6,141,255:0.0392157;38,5,145,255:0.0588235;47,5,150,255:0.0784314;56,4,154,255:0.0980392;65,4,157,255:"
1621 "0.117647;73,3,160,255:0.137255;81,2,163,255:0.156863;89,1,165,255:0.176471;97,0,167,255:0.196078;105,0,168,255:"
1622 "0.215686;113,0,168,255:0.235294;120,1,168,255:0.254902;128,4,168,255:0.27451;135,7,166,255:0.294118;142,12,164,255:"
1623 "0.313725;149,17,161,255:0.333333;156,23,158,255:0.352941;162,29,154,255:0.372549;168,34,150,255:0.392157;174,40,146,255:"
1624 "0.411765;180,46,141,255:0.431373;186,51,136,255:0.45098;191,57,132,255:0.470588;196,62,127,255:0.490196;201,68,122,255:"
1625 "0.509804;205,74,118,255:0.529412;210,79,113,255:0.54902;214,85,109,255:0.568627;218,91,105,255:0.588235;222,97,100,255:"
1626 "0.607843;226,102,96,255:0.627451;230,108,92,255:0.647059;233,114,87,255:0.666667;237,121,83,255:0.686275;240,127,79,255:"
1627 "0.705882;243,133,75,255:0.72549;245,140,70,255:0.745098;247,147,66,255:0.764706;249,154,62,255:0.784314;251,161,57,255:"
1628 "0.803922;252,168,53,255:0.823529;253,175,49,255:0.843137;254,183,45,255:0.862745;254,190,42,255:0.882353;253,198,39,255:"
1629 "0.901961;252,206,37,255:0.921569;251,215,36,255:0.941176;248,223,37,255:0.960784;246,232,38,255:0.980392;243,240,39,255";
1630 return QgsGradientColorRamp::create( props );
1631}
1632
1633void QgsMeshLayer::assignDefaultStyleToDatasetGroup( int groupIndex )
1634{
1636
1637 const QgsMeshDatasetGroupMetadata metadata = datasetGroupMetadata( groupIndex );
1638 const double groupMin = metadata.minimum();
1639 const double groupMax = metadata.maximum();
1640
1641 QgsColorRampShader fcn( groupMin, groupMax, _createDefaultColorRamp() );
1642 fcn.classifyColorRamp( 5, -1, QgsRectangle(), nullptr );
1643
1644 QgsMeshRendererScalarSettings scalarSettings;
1645 scalarSettings.setClassificationMinimumMaximum( groupMin, groupMax );
1646 scalarSettings.setColorRampShader( fcn );
1647 QgsInterpolatedLineWidth edgeStrokeWidth;
1648 edgeStrokeWidth.setMinimumValue( groupMin );
1649 edgeStrokeWidth.setMaximumValue( groupMax );
1650 const QgsInterpolatedLineColor edgeStrokeColor( fcn );
1651 const QgsInterpolatedLineRenderer edgeStrokePen;
1652 scalarSettings.setEdgeStrokeWidth( edgeStrokeWidth );
1653 mRendererSettings.setScalarSettings( groupIndex, scalarSettings );
1654
1655 if ( metadata.isVector() )
1656 {
1657 QgsMeshRendererVectorSettings vectorSettings;
1658 vectorSettings.setColorRampShader( fcn );
1659 mRendererSettings.setVectorSettings( groupIndex, vectorSettings );
1660 }
1661}
1662
1664{
1666
1667 // Triangular mesh
1668 updateTriangularMesh( rendererContext.coordinateTransform() );
1669
1670 // Build overview triangular meshes if needed
1671 createSimplifiedMeshes();
1672
1673 // Cache
1674 if ( !mRendererCache )
1675 mRendererCache = std::make_unique<QgsMeshLayerRendererCache>();
1676
1677 return new QgsMeshLayerRenderer( this, rendererContext );
1678}
1679
1681{
1682 if ( rendererContext.isTemporal() )
1683 return activeScalarDatasetAtTime( rendererContext.temporalRange(), mRendererSettings.activeScalarDatasetGroup() );
1684 else
1685 return staticScalarDatasetIndex( mRendererSettings.activeScalarDatasetGroup() );
1686}
1687
1688bool QgsMeshLayer::minimumMaximumActiveScalarDataset( const QgsRectangle &extent, const QgsMeshDatasetIndex &datasetIndex, double &min, double &max )
1689{
1690 if ( extent.isNull() || !this->extent().intersects( extent ) )
1691 return false;
1692
1694
1695 if ( !tMesh )
1696 {
1697 return false;
1698 }
1699
1700 QVector<double> scalarDatasetValues;
1702
1703 if ( !metadata.isScalar() )
1704 {
1705 return false;
1706 }
1707
1708 QgsMeshDatasetGroupMetadata::DataType scalarDataType = QgsMeshLayerUtils::datasetValuesType( metadata.dataType() );
1709
1710 if ( !datasetIndex.isValid() )
1711 {
1712 return false;
1713 }
1714
1715 // populate scalar values
1716 const int count = QgsMeshLayerUtils::datasetValuesCount( mNativeMesh.get(), scalarDataType );
1717 const QgsMeshDataBlock vals = QgsMeshLayerUtils::datasetValues( this, datasetIndex, 0, count );
1718
1719 if ( vals.isValid() )
1720 {
1721 // vals could be scalar or vectors, for contour rendering we want always magnitude
1722 scalarDatasetValues = QgsMeshLayerUtils::calculateMagnitudes( vals );
1723 }
1724 else
1725 {
1726 scalarDatasetValues = QVector<double>( count, std::numeric_limits<double>::quiet_NaN() );
1727 }
1728
1729 QList<int> intersectedFacesIndices = tMesh->faceIndexesForRectangle( extent );
1730
1731 if ( intersectedFacesIndices.isEmpty() )
1732 {
1733 return false;
1734 }
1735
1736 min = std::numeric_limits<double>::max();
1737 max = -std::numeric_limits<double>::max();
1738
1739 double value;
1740
1741 for ( int intersectedFaceIndex : intersectedFacesIndices )
1742 {
1743 QgsMeshFace face = tMesh->triangles().at( intersectedFaceIndex );
1744
1746 {
1747 value = scalarDatasetValues.at( tMesh->trianglesToNativeFaces().at( intersectedFaceIndex ) );
1748 min = std::min( min, value );
1749 max = std::max( max, value );
1750 }
1752 {
1753 QgsMeshVertex vertex;
1754
1755 for ( int vertexIndex : face )
1756 {
1757 value = scalarDatasetValues.at( vertexIndex );
1758 min = std::min( min, value );
1759 max = std::max( max, value );
1760 }
1761 }
1762 }
1763
1764 return true;
1765}
1766
1773
1774void QgsMeshLayer::checkSymbologyConsistency()
1775{
1777
1778 const QList<int> groupIndexes = mDatasetGroupStore->datasetGroupIndexes();
1779 if ( !groupIndexes.contains( mRendererSettings.activeScalarDatasetGroup() ) && mRendererSettings.activeScalarDatasetGroup() != -1 )
1780 {
1781 if ( !groupIndexes.empty() )
1782 mRendererSettings.setActiveScalarDatasetGroup( groupIndexes.first() );
1783 else
1784 mRendererSettings.setActiveScalarDatasetGroup( -1 );
1785 }
1786
1787 if ( !groupIndexes.contains( mRendererSettings.activeVectorDatasetGroup() ) && mRendererSettings.activeVectorDatasetGroup() != -1 )
1788 {
1789 mRendererSettings.setActiveVectorDatasetGroup( -1 );
1790 }
1791}
1792
1793bool QgsMeshLayer::readSymbology( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
1794{
1796
1797 Q_UNUSED( errorMessage )
1798 // TODO: implement categories for raster layer
1799
1800 const QDomElement elem = node.toElement();
1801
1802 readCommonStyle( elem, context, categories );
1803
1805 const QDomElement elemRendererSettings = elem.firstChildElement( "mesh-renderer-settings" );
1806 if ( !elemRendererSettings.isNull() )
1807 rendererSettings.readXml( elemRendererSettings, context );
1808
1809 QMap<QString, int> groupNameToGlobalIndex;
1810 QDomElement nameToIndexElem = elem.firstChildElement( "name-to-global-index" );
1811 while ( !nameToIndexElem.isNull() )
1812 {
1813 const QString name = nameToIndexElem.attribute( u"name"_s );
1814 int globalIndex = nameToIndexElem.attribute( u"global-index"_s ).toInt();
1815 groupNameToGlobalIndex.insert( name, globalIndex );
1816 nameToIndexElem = nameToIndexElem.nextSiblingElement( u"name-to-global-index"_s );
1817 }
1818
1819 mRendererSettings = accordSymbologyWithGroupName( rendererSettings, groupNameToGlobalIndex );
1820
1821 checkSymbologyConsistency();
1822
1823 const QDomElement elemSimplifySettings = elem.firstChildElement( "mesh-simplify-settings" );
1824 if ( !elemSimplifySettings.isNull() )
1825 mSimplificationSettings.readXml( elemSimplifySettings, context );
1826
1827 // get and set the blend mode if it exists
1828 const QDomNode blendModeNode = node.namedItem( u"blendMode"_s );
1829 if ( !blendModeNode.isNull() )
1830 {
1831 const QDomElement e = blendModeNode.toElement();
1832 setBlendMode( QgsPainting::getCompositionMode( static_cast< Qgis::BlendMode >( e.text().toInt() ) ) );
1833 }
1834
1835 // read labeling definition
1836 if ( categories.testFlag( Labeling ) )
1837 {
1838 QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "Labeling" ) );
1839
1840 QDomElement labelingElement = node.firstChildElement( u"labeling"_s );
1841 if ( !labelingElement.isNull() )
1842 {
1844 mLabelsEnabled = node.toElement().attribute( u"labelsEnabled"_s, u"0"_s ).toInt();
1846 }
1847 }
1848
1849 // get and set the layer transparency
1850 if ( categories.testFlag( Rendering ) )
1851 {
1852 const QDomNode layerOpacityNode = node.namedItem( u"layerOpacity"_s );
1853 if ( !layerOpacityNode.isNull() )
1854 {
1855 const QDomElement e = layerOpacityNode.toElement();
1856 setOpacity( e.text().toDouble() );
1857 }
1858 }
1859
1860 if ( categories.testFlag( Legend ) )
1861 {
1862 QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "Legend" ) );
1863
1864 const QDomElement legendElem = node.firstChildElement( u"legend"_s );
1865 if ( QgsMapLayerLegend *l = legend(); l && !legendElem.isNull() )
1866 {
1867 l->readXml( legendElem, context );
1868 }
1869 }
1870
1871 return true;
1872}
1873
1874bool QgsMeshLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
1875{
1877
1878 Q_UNUSED( errorMessage )
1879 // TODO: implement categories for raster layer
1880
1881 QDomElement elem = node.toElement();
1882
1883 writeCommonStyle( elem, doc, context, categories );
1884
1885 const QDomElement elemRendererSettings = mRendererSettings.writeXml( doc, context );
1886 elem.appendChild( elemRendererSettings );
1887
1888 const QList<int> groupIndexes = datasetGroupsIndexes();
1889 // we store the relation between name and indexes to be able to retrieve the consistency between name and symbology
1890 for ( int index : groupIndexes )
1891 {
1892 QDomElement elemNameToIndex = doc.createElement( u"name-to-global-index"_s );
1893 elemNameToIndex.setAttribute( u"name"_s, mDatasetGroupStore->groupName( index ) );
1894 elemNameToIndex.setAttribute( u"global-index"_s, index );
1895 elem.appendChild( elemNameToIndex );
1896 }
1897
1898 const QDomElement elemSimplifySettings = mSimplificationSettings.writeXml( doc, context );
1899 elem.appendChild( elemSimplifySettings );
1900
1901 // add blend mode node
1902 QDomElement blendModeElement = doc.createElement( u"blendMode"_s );
1903 const QDomText blendModeText = doc.createTextNode( QString::number( static_cast< int >( QgsPainting::getBlendModeEnum( blendMode() ) ) ) );
1904 blendModeElement.appendChild( blendModeText );
1905 node.appendChild( blendModeElement );
1906
1907 if ( categories.testFlag( Labeling ) )
1908 {
1909 if ( mLabeling )
1910 {
1911 QDomElement labelingElement = mLabeling->save( doc, context );
1912 elem.appendChild( labelingElement );
1913 }
1914 elem.setAttribute( u"labelsEnabled"_s, mLabelsEnabled ? u"1"_s : u"0"_s );
1915 }
1916
1917 // add the layer opacity
1918 if ( categories.testFlag( Rendering ) )
1919 {
1920 QDomElement layerOpacityElem = doc.createElement( u"layerOpacity"_s );
1921 const QDomText layerOpacityText = doc.createTextNode( QString::number( opacity() ) );
1922 layerOpacityElem.appendChild( layerOpacityText );
1923 node.appendChild( layerOpacityElem );
1924 }
1925
1926 if ( categories.testFlag( Legend ) && legend() )
1927 {
1928 QDomElement legendElement = legend()->writeXml( doc, context );
1929 if ( !legendElement.isNull() )
1930 node.appendChild( legendElement );
1931 }
1932
1933 return true;
1934}
1935
1936bool QgsMeshLayer::writeStyle( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
1937{
1939
1940 return writeSymbology( node, doc, errorMessage, context, categories );
1941}
1942
1943bool QgsMeshLayer::readStyle( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
1944{
1946
1947 return readSymbology( node, errorMessage, context, categories );
1948}
1949
1950QString QgsMeshLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
1951{
1953
1954 return QgsProviderRegistry::instance()->relativeToAbsoluteUri( provider, source, context );
1955}
1956
1957QString QgsMeshLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
1958{
1960
1962}
1963
1964bool QgsMeshLayer::readXml( const QDomNode &layer_node, QgsReadWriteContext &context )
1965{
1967
1968 QgsDebugMsgLevel( u"Datasource in QgsMeshLayer::readXml: %1"_s.arg( mDataSource.toLocal8Bit().data() ), 3 );
1969
1970 //process provider key
1971 const QDomNode pkeyNode = layer_node.namedItem( u"provider"_s );
1972
1973 if ( pkeyNode.isNull() )
1974 {
1975 mProviderKey.clear();
1976 }
1977 else
1978 {
1979 const QDomElement pkeyElt = pkeyNode.toElement();
1980 mProviderKey = pkeyElt.text();
1981 }
1982
1984 {
1985 return false;
1986 }
1987
1988 const QgsDataProvider::ProviderOptions providerOptions;
1990
1991 const QDomElement elemExtraDatasets = layer_node.firstChildElement( u"extra-datasets"_s );
1992 if ( !elemExtraDatasets.isNull() )
1993 {
1994 QDomElement elemUri = elemExtraDatasets.firstChildElement( u"uri"_s );
1995 while ( !elemUri.isNull() )
1996 {
1997 const QString uri = context.pathResolver().readPath( elemUri.text() );
1998 mExtraDatasetUri.append( uri );
1999 elemUri = elemUri.nextSiblingElement( u"uri"_s );
2000 }
2001 }
2002
2003 if ( pkeyNode.toElement().hasAttribute( u"time-unit"_s ) )
2004 mTemporalUnit = static_cast<Qgis::TemporalUnit>( pkeyNode.toElement().attribute( u"time-unit"_s ).toInt() );
2005
2006 // read dataset group store
2007 const QDomElement elemDatasetGroupsStore = layer_node.firstChildElement( u"mesh-dataset-groups-store"_s );
2008 if ( elemDatasetGroupsStore.isNull() )
2010 else
2011 mDatasetGroupStore->readXml( elemDatasetGroupsStore, context );
2012
2013 setDataProvider( mProviderKey, providerOptions, flags );
2014
2015 QString errorMsg;
2016 readSymbology( layer_node, errorMsg, context );
2017
2018 if ( !mTemporalProperties->timeExtent().begin().isValid() || mTemporalProperties->alwaysLoadReferenceTimeFromSource() )
2020
2021 // read static dataset
2022 const QDomElement elemStaticDataset = layer_node.firstChildElement( u"static-active-dataset"_s );
2023 if ( elemStaticDataset.hasAttribute( u"scalar"_s ) )
2024 {
2025 mStaticScalarDatasetIndex = elemStaticDataset.attribute( u"scalar"_s ).toInt();
2026 }
2027 if ( elemStaticDataset.hasAttribute( u"vector"_s ) )
2028 {
2029 mStaticVectorDatasetIndex = elemStaticDataset.attribute( u"vector"_s ).toInt();
2030 }
2031
2032 return isValid(); // should be true if read successfully
2033}
2034
2035bool QgsMeshLayer::writeXml( QDomNode &layer_node, QDomDocument &document, const QgsReadWriteContext &context ) const
2036{
2038
2039 // first get the layer element so that we can append the type attribute
2040 QDomElement mapLayerNode = layer_node.toElement();
2041
2042 if ( mapLayerNode.isNull() || ( "maplayer"_L1 != mapLayerNode.nodeName() ) )
2043 {
2044 QgsDebugMsgLevel( u"can't find <maplayer>"_s, 2 );
2045 return false;
2046 }
2047
2048 mapLayerNode.setAttribute( u"type"_s, QgsMapLayerFactory::typeToString( Qgis::LayerType::Mesh ) );
2049
2050 // add provider node
2051 if ( mDataProvider )
2052 {
2053 QDomElement provider = document.createElement( u"provider"_s );
2054 const QDomText providerText = document.createTextNode( providerType() );
2055 provider.appendChild( providerText );
2056 layer_node.appendChild( provider );
2057 provider.setAttribute( u"time-unit"_s, static_cast< int >( mDataProvider->temporalCapabilities()->temporalUnit() ) );
2058
2059 const QStringList extraDatasetUris = mDataProvider->extraDatasets();
2060 QDomElement elemExtraDatasets = document.createElement( u"extra-datasets"_s );
2061 for ( const QString &uri : extraDatasetUris )
2062 {
2063 const QString path = context.pathResolver().writePath( uri );
2064 QDomElement elemUri = document.createElement( u"uri"_s );
2065 elemUri.appendChild( document.createTextNode( path ) );
2066 elemExtraDatasets.appendChild( elemUri );
2067 }
2068 layer_node.appendChild( elemExtraDatasets );
2069 }
2070
2071 QDomElement elemStaticDataset = document.createElement( u"static-active-dataset"_s );
2072 elemStaticDataset.setAttribute( u"scalar"_s, mStaticScalarDatasetIndex );
2073 elemStaticDataset.setAttribute( u"vector"_s, mStaticVectorDatasetIndex );
2074 layer_node.appendChild( elemStaticDataset );
2075
2076 // write dataset group store if not in edting mode
2077 if ( !isEditable() )
2078 layer_node.appendChild( mDatasetGroupStore->writeXml( document, context ) );
2079
2080 // renderer specific settings
2081 QString errorMsg;
2082 return writeSymbology( layer_node, document, errorMsg, context );
2083}
2084
2086{
2088
2089 if ( !mMeshEditor && mDataProvider && mDataProvider->isValid() )
2090 {
2091 mDataProvider->reloadData();
2092 mDatasetGroupStore->setPersistentProvider( mDataProvider.get(), QStringList() ); //extra dataset are already loaded
2093
2094 //reload the mesh structure
2095 if ( !mNativeMesh )
2096 mNativeMesh = std::make_unique<QgsMesh>();
2097
2098 dataProvider()->populateMesh( mNativeMesh.get() );
2099
2100 if ( mTemporalProperties->alwaysLoadReferenceTimeFromSource() )
2101 mTemporalProperties->setDefaultsFromDataProviderTemporalCapabilities( mDataProvider->temporalCapabilities() );
2102
2103 //clear the TriangularMeshes
2104 mTriangularMeshes.clear();
2105
2107
2108 checkSymbologyConsistency();
2109
2110 emit reloaded();
2111 }
2112}
2113
2115{
2117
2118 mRendererCache = std::make_unique<QgsMeshLayerRendererCache>();
2119}
2120
2121QStringList QgsMeshLayer::subLayers() const
2122{
2124
2125 if ( mDataProvider )
2126 return mDataProvider->subLayers();
2127 else
2128 return QStringList();
2129}
2130
2132{
2134
2135 const QgsLayerMetadataFormatter htmlFormatter( metadata() );
2136 QString myMetadata = u"<html>\n<body>\n"_s;
2137
2138 myMetadata += generalHtmlMetadata();
2139
2140 // Begin Provider section
2141 myMetadata += u"<h1>"_s + tr( "Information from provider" ) + u"</h1>\n<hr>\n"_s;
2142 myMetadata += "<table class=\"list-view\">\n"_L1;
2143
2144 // Extent
2145 myMetadata += u"<tr><td class=\"highlight\">"_s + tr( "Extent" ) + u"</td><td>"_s + extent().toString() + u"</td></tr>\n"_s;
2146
2147 // feature count
2148 QLocale locale = QLocale();
2149 locale.setNumberOptions( locale.numberOptions() &= ~QLocale::NumberOption::OmitGroupSeparator );
2150
2151 if ( const QgsMeshDataProvider *provider = dataProvider() )
2152 {
2153 myMetadata += u"<tr><td class=\"highlight\">"_s + tr( "Vertex count" ) + u"</td><td>"_s + ( locale.toString( static_cast<qlonglong>( meshVertexCount() ) ) ) + u"</td></tr>\n"_s;
2154 myMetadata += u"<tr><td class=\"highlight\">"_s + tr( "Face count" ) + u"</td><td>"_s + ( locale.toString( static_cast<qlonglong>( meshFaceCount() ) ) ) + u"</td></tr>\n"_s;
2155 myMetadata += u"<tr><td class=\"highlight\">"_s + tr( "Edge count" ) + u"</td><td>"_s + ( locale.toString( static_cast<qlonglong>( meshEdgeCount() ) ) ) + u"</td></tr>\n"_s;
2156 myMetadata += u"<tr><td class=\"highlight\">"_s + tr( "Dataset groups count" ) + u"</td><td>"_s + ( locale.toString( static_cast<qlonglong>( datasetGroupCount() ) ) ) + u"</td></tr>\n"_s;
2157 myMetadata += provider->htmlMetadata();
2158 }
2159
2160 // End Provider section
2161 myMetadata += "</table>\n<br><br>"_L1;
2162
2163 // CRS
2164 myMetadata += crsHtmlMetadata();
2165
2166 // identification section
2167 myMetadata += u"<h1>"_s + tr( "Identification" ) + u"</h1>\n<hr>\n"_s;
2168 myMetadata += htmlFormatter.identificationSectionHtml();
2169 myMetadata += "<br><br>\n"_L1;
2170
2171 // extent section
2172 myMetadata += u"<h1>"_s + tr( "Extent" ) + u"</h1>\n<hr>\n"_s;
2173 myMetadata += htmlFormatter.extentSectionHtml( isSpatial() );
2174 myMetadata += "<br><br>\n"_L1;
2175
2176 // Start the Access section
2177 myMetadata += u"<h1>"_s + tr( "Access" ) + u"</h1>\n<hr>\n"_s;
2178 myMetadata += htmlFormatter.accessSectionHtml();
2179 myMetadata += "<br><br>\n"_L1;
2180
2181 // Start the contacts section
2182 myMetadata += u"<h1>"_s + tr( "Contacts" ) + u"</h1>\n<hr>\n"_s;
2183 myMetadata += htmlFormatter.contactsSectionHtml();
2184 myMetadata += "<br><br>\n"_L1;
2185
2186 // Start the links section
2187 myMetadata += u"<h1>"_s + tr( "Links" ) + u"</h1>\n<hr>\n"_s;
2188 myMetadata += htmlFormatter.linksSectionHtml();
2189 myMetadata += "<br><br>\n"_L1;
2190
2191 // Start the history section
2192 myMetadata += u"<h1>"_s + tr( "History" ) + u"</h1>\n<hr>\n"_s;
2193 myMetadata += htmlFormatter.historySectionHtml();
2194 myMetadata += "<br><br>\n"_L1;
2195
2196 myMetadata += customPropertyHtmlMetadata();
2197
2198 myMetadata += "\n</body>\n</html>\n"_L1;
2199 return myMetadata;
2200}
2201
2203{
2205
2206 return mMeshEditor;
2207}
2208
2209bool QgsMeshLayer::setDataProvider( QString const &provider, const QgsDataProvider::ProviderOptions &options, Qgis::DataProviderReadFlags flags )
2210{
2212
2213 mDatasetGroupStore->setPersistentProvider( nullptr, QStringList() );
2214
2215 mDataProvider.reset();
2216 mProviderKey = provider;
2217 const QString dataSource = mDataSource;
2218
2219 if ( mPreloadedProvider )
2220 {
2221 mDataProvider.reset( qobject_cast< QgsMeshDataProvider * >( mPreloadedProvider.release() ) );
2222 }
2223 else
2224 {
2225 std::unique_ptr< QgsScopedRuntimeProfile > profile;
2226 if ( QgsApplication::profiler()->groupIsActive( u"projectload"_s ) )
2227 profile = std::make_unique< QgsScopedRuntimeProfile >( tr( "Create %1 provider" ).arg( provider ), u"projectload"_s );
2228
2229 mDataProvider.reset( qobject_cast<QgsMeshDataProvider *>( QgsProviderRegistry::instance()->createProvider( provider, dataSource, options, flags ) ) );
2230 }
2231
2232 if ( !mDataProvider )
2233 {
2234 QgsDebugMsgLevel( u"Unable to get mesh data provider"_s, 2 );
2235 return false;
2236 }
2237
2238 mDataProvider->setParent( this );
2239 QgsDebugMsgLevel( u"Instantiated the mesh data provider plugin"_s, 2 );
2240
2241 setValid( mDataProvider->isValid() );
2242 if ( !isValid() )
2243 {
2244 QgsDebugMsgLevel( u"Invalid mesh provider plugin %1"_s.arg( QString( mDataSource.toUtf8() ) ), 2 );
2245 return false;
2246 }
2247
2248 if ( !mTemporalProperties->isValid() )
2249 {
2250 mTemporalProperties->setDefaultsFromDataProviderTemporalCapabilities( dataProvider()->temporalCapabilities() );
2251 }
2252
2253 mDataProvider->setTemporalUnit( mTemporalUnit );
2254
2255 mDatasetGroupStore->setPersistentProvider( mDataProvider.get(), mExtraDatasetUri );
2256
2257 setCrs( mDataProvider->crs() );
2258
2259 if ( provider == "mesh_memory"_L1 )
2260 {
2261 // required so that source differs between memory layers
2262 mDataSource = mDataSource + u"&uid=%1"_s.arg( QUuid::createUuid().toString() );
2263 }
2264
2265 // set default style if required by flags or if the dataset group does not has a style yet
2266 for ( int i = 0; i < mDataProvider->datasetGroupCount(); ++i )
2267 {
2268 int globalIndex = mDatasetGroupStore->globalDatasetGroupIndexInSource( mDataProvider.get(), i );
2269 if ( globalIndex != -1 && ( !mRendererSettings.hasSettings( globalIndex ) || ( flags & Qgis::DataProviderReadFlag::LoadDefaultStyle ) ) )
2270 assignDefaultStyleToDatasetGroup( globalIndex );
2271 }
2272
2273 emit rendererChanged();
2275
2276 connect( mDataProvider.get(), &QgsMeshDataProvider::dataChanged, this, &QgsMeshLayer::dataChanged );
2277
2278 return true;
2279}
2280
2287
2294
2296{
2298
2299 return mLabelsEnabled && static_cast< bool >( mLabeling );
2300}
2301
2303{
2305
2306 mLabelsEnabled = enabled;
2307}
2308
2310{
2312
2313 if ( mLabeling.get() == labeling )
2314 return;
2315
2316 mLabeling.reset( labeling );
2317
2319}
2320
2321bool QgsMeshLayer::datasetsPathUnique( const QString &path )
2322{
2323 if ( !mDataProvider )
2324 {
2325 QgsDebugMsgLevel( u"Unable to get mesh data provider"_s, 2 );
2326 return false;
2327 }
2328
2329 if ( mDataProvider->dataSourceUri().contains( path ) )
2330 return false;
2331
2332 return !mExtraDatasetUri.contains( path );
2333}
2334
2335QString QgsMeshLayer::loadNamedStyle( const QString &uri, bool &resultFlag, QgsMapLayer::StyleCategories categories, Qgis::LoadStyleFlags flags )
2336{
2337 QString result = QgsMapLayer::loadNamedStyle( uri, resultFlag, categories, flags );
2338 emit rendererChanged();
2340 return result;
2341}
Provides global constants and enumerations for use throughout the application.
Definition qgis.h:62
@ Exact
Assigns the color of the exact matching value in the color ramp item list.
Definition qgis.h:1561
@ Critical
Critical/error message.
Definition qgis.h:163
@ TooManyVerticesInFace
A face has more vertices than the maximum number supported per face.
Definition qgis.h:1796
@ InvalidFace
An error occurs due to an invalid face (for example, vertex indexes are unordered).
Definition qgis.h:1795
@ UniqueSharedVertex
A least two faces share only one vertices.
Definition qgis.h:1798
@ ManifoldFace
ManifoldFace.
Definition qgis.h:1800
@ InvalidVertex
An error occurs due to an invalid vertex (for example, vertex index is out of range the available ver...
Definition qgis.h:1799
@ FlatFace
A flat face is present.
Definition qgis.h:1797
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition qgis.h:5381
TemporalUnit
Temporal units.
Definition qgis.h:5610
@ Milliseconds
Milliseconds.
Definition qgis.h:5611
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:512
@ EqualInterval
Uses equal interval.
Definition qgis.h:1575
@ Mesh
Mesh layer. Added in QGIS 3.2.
Definition qgis.h:210
QFlags< LoadStyleFlag > LoadStyleFlags
Flags for loading layer styles.
Definition qgis.h:263
@ LoadDefaultStyle
Reset the layer's style to the default for the datasource.
Definition qgis.h:495
QgsVertexIterator vertices() const
Returns a read-only, Java-style iterator for traversal of vertices of all the geometry,...
Abstract base class - its implementations define different approaches to the labeling of a mesh layer...
static QgsAbstractMeshLayerLabeling * create(const QDomElement &element, const QgsReadWriteContext &context)
Try to create instance of an implementation based on the XML data.
Abstract base class for objects which generate elevation profiles.
static QgsRuntimeProfiler * profiler()
Returns the application runtime profiler.
A ramp shader will color a raster pixel based on a list of values ranges in a ramp.
void setClassificationMode(Qgis::ShaderClassificationMethod classificationMode)
Sets the classification mode.
QgsColorRamp * sourceColorRamp() const
Returns the source color ramp.
void setColorRampType(Qgis::ShaderInterpolationMethod colorRampType)
Sets the color ramp interpolation method.
void setColorRampItemList(const QList< QgsColorRampShader::ColorRampItem > &list)
Sets a custom color map.
Abstract base class for color ramps.
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
Contains information about the context in which a coordinate transform is executed.
Handles coordinate transforms between two coordinate systems.
Base class for handling properties relating to a data provider's temporal capabilities.
bool hasTemporalCapabilities() const
Returns true if the provider has temporal capabilities available.
void dataChanged()
Emitted whenever a change is made to the data provider which may have caused changes in the provider'...
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
static QgsExpressionContextScope * meshExpressionScope(QgsMesh::ElementType elementType)
Creates a new scope which contains functions relating to mesh layer element elementType.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * lastScope()
Returns the last scope added to the context.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
Handles parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
QVariant evaluate()
Evaluate the feature and return the result.
static QgsColorRamp * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsColorRamp from a map of properties.
void setMaximumValue(double maximumValue)
Sets the maximum value used to defined the variable width.
void setMinimumValue(double minimumValue)
Sets the minimum value used to defined the variable width.
A representation of the interval between two datetime values.
Definition qgsinterval.h:52
double seconds() const
Returns the interval duration in seconds.
Formats layer metadata into HTML.
Base class for storage of map layer elevation properties.
static QString typeToString(Qgis::LayerType type)
Converts a map layer type to a string value.
An abstract interface for implementations of legends for one map layer.
static QgsMapLayerLegend * defaultMeshLegend(QgsMeshLayer *ml)
Create new legend implementation for mesh layer.
virtual QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Writes configuration to a DOM element, to be used later with readXml().
Base class for utility classes that encapsulate information necessary for rendering of map layers.
Base class for storage of map layer temporal properties.
virtual void setDefaultsFromDataProviderTemporalCapabilities(const QgsDataProviderTemporalCapabilities *capabilities)=0
Sets the layers temporal settings to appropriate defaults based on a provider's temporal capabilities...
QString name
Definition qgsmaplayer.h:87
virtual bool isSpatial() const
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
QgsMapLayerLegend * legend() const
Can be nullptr.
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
QString source() const
Returns the source for the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
void setBlendMode(QPainter::CompositionMode blendMode)
Set the blending mode used for rendering a layer.
void trigger3DUpdate()
Will advise any 3D maps that this layer requires to be updated in the scene.
static Qgis::DataProviderReadFlags providerReadFlags(const QDomNode &layerNode, QgsMapLayer::ReadFlags layerReadFlags)
Returns provider read flag deduced from layer read flags layerReadFlags and a dom node layerNode that...
virtual QString loadNamedStyle(const QString &theURI, bool &resultFlag, bool loadFromLocalDb, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories, Qgis::LoadStyleFlags flags=Qgis::LoadStyleFlags())
Loads a named style from file/local db/datasource db.
void editingStarted()
Emitted when editing on this layer has started.
virtual QString loadDefaultStyle(bool &resultFlag)
Retrieve the default style for this layer if one exists (either as a .qml file on disk or as a record...
QString mLayerName
Name of the layer - used for display.
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted.
QString crsHtmlMetadata() const
Returns a HTML fragment containing the layer's CRS metadata, for use in the htmlMetadata() method.
QgsLayerMetadata metadata
Definition qgsmaplayer.h:89
QgsMapLayer(Qgis::LayerType type=Qgis::LayerType::Vector, const QString &name=QString(), const QString &source=QString())
Constructor for QgsMapLayer.
Qgis::LayerType type
Definition qgsmaplayer.h:93
QPainter::CompositionMode blendMode() const
Returns the current blending mode for a layer.
virtual void setOpacity(double opacity)
Sets the opacity for the layer, where opacity is a value between 0 (totally transparent) and 1....
QFlags< StyleCategory > StyleCategories
QString mProviderKey
Data provider key (name of the data provider).
QgsCoordinateTransformContext transformContext() const
Returns the layer data provider coordinate transform context or a default transform context if the la...
std::unique_ptr< QgsDataProvider > mPreloadedProvider
Optionally used when loading a project, it is released when the layer is effectively created.
void rendererChanged()
Signal emitted when renderer is changed.
virtual QgsError error() const
Gets current status error.
void dataSourceChanged()
Emitted whenever the layer's data source has been changed.
QgsMapLayer::LayerFlags flags
Definition qgsmaplayer.h:99
void emitStyleChanged()
Triggers an emission of the styleChanged() signal.
void dataChanged()
Data of layer changed.
virtual QgsMapLayer * clone() const =0
Returns a new instance equivalent to this one except for the id which is still unique.
QString mDataSource
Data source description string, varies by layer type.
@ FlagDontResolveLayers
Don't resolve layer paths or create data providers for layers.
void setValid(bool valid)
Sets whether layer is valid or not.
void readCommonStyle(const QDomElement &layerElement, const QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories)
Read style data common to all layer types.
QgsMapLayer::ReadFlags mReadFlags
Read flags. It's up to the subclass to respect these when restoring state from XML.
void setLegend(QgsMapLayerLegend *legend)
Assign a legend controller to the map layer.
double opacity
Definition qgsmaplayer.h:95
@ Rendering
Rendering: scale visibility, simplify method, opacity.
@ Legend
Legend settings.
@ Labeling
Labeling.
void layerModified()
Emitted when modifications has been done on layer.
void setProviderType(const QString &providerType)
Sets the providerType (provider key).
QString customPropertyHtmlMetadata() const
Returns an HTML fragment containing custom property information, for use in the htmlMetadata() method...
QString generalHtmlMetadata() const
Returns an HTML fragment containing general metadata information, for use in the htmlMetadata() metho...
void writeCommonStyle(QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories) const
Write style data common to all layer types.
void invalidateWgs84Extent()
Invalidates the WGS84 extent.
bool mShouldValidateCrs
true if the layer's CRS should be validated and invalid CRSes are not permitted.
void setCrs(const QgsCoordinateReferenceSystem &srs, bool emitSignal=true)
Sets layer's spatial reference system.
Abstract class for interpolating 3d stacked mesh data to 2d data.
QgsMeshDataBlock calculate(const QgsMesh3DDataBlock &block3d, QgsFeedback *feedback=nullptr) const
Calculated 2d block values from 3d stacked mesh values.
A block of 3d stacked mesh data related N faces defined on base mesh frame.
A block of integers/doubles from a mesh dataset.
QgsMeshDatasetValue value(int index) const
Returns a value represented by the index For active flag the behavior is undefined.
bool isValid() const
Whether the block is valid.
MatchingTemporalDatasetMethod
Method for selection of temporal mesh dataset from a range time.
Base class for providing data for QgsMeshLayer.
QgsMeshDataProviderTemporalCapabilities * temporalCapabilities() override
Returns the provider's temporal capabilities.
virtual void populateMesh(QgsMesh *mesh) const =0
Populates the mesh vertices, edges and faces.
A collection of dataset group metadata such as whether the data is vector or scalar,...
QMap< QString, QString > extraOptions() const
Returns extra metadata options, for example description.
bool isVector() const
Returns whether dataset group has vector data.
DataType dataType() const
Returns whether dataset group data is defined on vertices or faces or volumes.
double minimum() const
Returns minimum scalar value/vector magnitude present for whole dataset group.
double maximum() const
Returns maximum scalar value/vector magnitude present for whole dataset group.
DataType
Location of where data is specified for datasets in the dataset group.
@ DataOnEdges
Data is defined on edges.
@ DataOnFaces
Data is defined on faces.
@ DataOnVertices
Data is defined on vertices.
@ DataOnVolumes
Data is defined on volumes.
QString uri() const
Returns the uri of the source.
Registers and accesses all the dataset groups related to a mesh layer.
void datasetGroupsAdded(QList< int > indexes)
Emitted after dataset groups are added.
Tree item for display of the mesh dataset groups.
int datasetGroupIndex() const
Returns the dataset group index.
QgsMeshDatasetGroupTreeItem * childFromDatasetGroupIndex(int index)
Returns the child with dataset group index Searches as depper as needed on the child hierarchy.
bool isEnabled() const
Returns true if the item is enabled, i.e.
QgsMeshDatasetGroupTreeItem * child(int row) const
Returns a child.
Abstract class that represents a dataset group.
An index that identifies the dataset group (e.g.
bool isValid() const
Returns whether index is valid, ie at least groups is set.
int group() const
Returns a group index.
Represents mesh dataset metadata, such as whether the data is valid or the associated time.
Represents a single mesh dataset value.
double y() const
Returns y value.
double x() const
Returns x value.
Holds metadata about mesh drivers.
@ CanWriteMeshData
If the driver can write mesh data on file.
MeshDriverCapabilities capabilities() const
Returns the capabilities for this driver.
Represents an error which occurred during mesh editing.
Qgis::MeshEditingErrorType errorType
Handles edit operations on a mesh layer.
void meshEdited()
Emitted when the mesh is edited.
Mesh layer specific subclass of QgsMapLayerElevationProperties.
QgsMeshLayerElevationProperties * clone() const override
Creates a clone of the properties.
Implementation of QgsAbstractProfileGenerator for mesh layers.
Implementation of threaded rendering for mesh layers.
Implementation of map layer temporal properties for mesh layers.
int closestElement(QgsMesh::ElementType elementType, const QgsPointXY &point, double searchRadius, QgsPointXY &projectedPoint) const
Returns the index of the snapped point on the mesh element closest to point intersecting with the sea...
~QgsMeshLayer() override
QList< int > selectVerticesByExpression(QgsExpression expression)
Returns a list of vertex indexes that meet the condition defined by expression with the context expre...
void setMeshSimplificationSettings(const QgsMeshSimplificationSettings &meshSimplificationSettings)
Sets mesh simplification settings.
int datasetCount(const QgsMeshDatasetIndex &index) const
Returns the dataset count in the dataset groups.
QList< int > datasetGroupsIndexes() const
Returns the list of indexes of dataset groups handled by the layer.
void stopFrameEditing(const QgsCoordinateTransform &transform)
Stops editing of the mesh, re-indexes the faces and vertices, rebuilds the triangular mesh and its sp...
QgsRectangle extent() const override
Returns the extent of the layer.
void setStaticVectorDatasetIndex(const QgsMeshDatasetIndex &staticVectorDatasetIndex)
Sets the static vector dataset index that is rendered if the temporal properties is not active.
void setStaticScalarDatasetIndex(const QgsMeshDatasetIndex &staticScalarDatasetIndex)
Sets the static scalar dataset index that is rendered if the temporal properties is not active.
bool contains(const QgsMesh::ElementType &type) const
Returns whether the mesh contains at mesh elements of given type.
QgsMeshRendererSettings rendererSettings() const
Returns renderer settings.
QgsMeshDatasetIndex activeScalarDatasetIndex(QgsRenderContext &rendererContext)
Returns current active scalar dataset index for current renderer context.
QgsMeshDatasetIndex activeVectorDatasetAtTime(const QgsDateTimeRange &timeRange, int group=-1) const
Returns dataset index from active vector group depending on the time range If the temporal properties...
QList< QgsMeshDatasetIndex > datasetIndexInRelativeTimeInterval(const QgsInterval &startRelativeTime, const QgsInterval &endRelativeTime, int datasetGroupIndex) const
Returns a list of dataset indexes from datasets group that are in a interval time from the layer refe...
void activeScalarDatasetGroupChanged(int index)
Emitted when active scalar group dataset is changed.
int datasetGroupCount() const
Returns the dataset groups count handle by the layer.
void updateTriangularMesh(const QgsCoordinateTransform &transform=QgsCoordinateTransform())
Gets native mesh and updates (creates if it doesn't exist) the base triangular mesh.
bool addDatasets(const QString &path, const QDateTime &defaultReferenceTime=QDateTime())
Adds datasets to the mesh from file with path.
void invalidateRendererCache()
Invalidates the cache used by the renderer to store the values of the active scalar and vector datase...
bool isModified() const override
Returns whether the mesh frame has been modified since the last save.
void activeVectorDatasetGroupChanged(int index)
Emitted when active vector group dataset is changed.
void reload() override
Synchronises with changes in the datasource.
QgsPointXY snapOnElement(QgsMesh::ElementType elementType, const QgsPointXY &point, double searchRadius)
Returns the position of the snapped point on the mesh element closest to point intersecting with the ...
bool readXml(const QDomNode &layer_node, QgsReadWriteContext &context) override
Called by readLayerXML(), used by children to read state specific to them from project files.
QStringList subLayers() const override
Returns the sublayers of this layer.
QgsMeshEditor * meshEditor()
Returns a pointer to the mesh editor own by the mesh layer.
const QgsAbstractMeshLayerLabeling * labeling() const
Access to const labeling configuration.
void setDatasetGroupTreeRootItem(QgsMeshDatasetGroupTreeItem *rootItem)
Sets the root items of the dataset group tree item.
QgsMeshDatasetValue dataset1dValue(const QgsMeshDatasetIndex &index, const QgsPointXY &point, double searchRadius) const
Returns the value of 1D mesh dataset defined on edge that are in the search area defined by point ans...
QgsMeshDatasetIndex staticVectorDatasetIndex(int group=-1) const
Returns the static vector dataset index that is rendered if the temporal properties is not active.
QgsMesh * nativeMesh()
Returns native mesh (nullptr before rendering or calling to updateMesh).
QString loadNamedStyle(const QString &uri, bool &resultFlag, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories, Qgis::LoadStyleFlags flags=Qgis::LoadStyleFlags()) override
Retrieve a named style for this layer if one exists (either as a .qml file on disk or as a record in ...
bool minimumMaximumActiveScalarDataset(const QgsRectangle &extent, const QgsMeshDatasetIndex &datasetIndex, double &min, double &max)
Extracts minimum and maximum value for active scalar dataset on mesh faces.
QgsTriangularMesh * triangularMeshByLodIndex(int lodIndex) const
Returns triangular corresponding to the index of level of details.
QString htmlMetadata() const override
Obtain a formatted HTML string containing assorted metadata for this layer.
int meshFaceCount() const
Returns the faces count of the mesh frame.
QList< int > selectFacesByExpression(QgsExpression expression)
Returns a list of faces indexes that meet the condition defined by expression with the context expres...
void setRendererSettings(const QgsMeshRendererSettings &settings, const bool repaint=true)
Sets new renderer settings.
QgsMapLayerElevationProperties * elevationProperties() override
Returns the layer's elevation properties.
QgsMeshLayer * clone() const override
Returns a new instance equivalent to this one except for the id which is still unique.
void timeSettingsChanged()
Emitted when time format is changed.
QList< int > enabledDatasetGroupsIndexes() const
Returns the list of indexes of enables dataset groups handled by the layer.
void resetDatasetGroupTreeItem()
Reset the dataset group tree item to default from provider.
int triangularMeshLevelOfDetailCount() const
Returns the count of levels of detail of the mesh simplification.
bool rollBackFrameEditing(const QgsCoordinateTransform &transform, bool continueEditing=true)
Rolls Back editing of the mesh frame.
QString encodedSource(const QString &source, const QgsReadWriteContext &context) const override
Called by writeLayerXML(), used by derived classes to encode provider's specific data source to proje...
QgsMeshDatasetIndex datasetIndexAtRelativeTime(const QgsInterval &relativeTime, int datasetGroupIndex) const
Returns dataset index from datasets group depending on the relative time from the layer reference tim...
bool writeXml(QDomNode &layer_node, QDomDocument &doc, const QgsReadWriteContext &context) const override
Called by writeLayerXML(), used by children to write state specific to them to project files.
bool commitFrameEditing(const QgsCoordinateTransform &transform, bool continueEditing=true)
Commits editing of the mesh frame, Rebuilds the triangular mesh and its spatial index with transform,...
QgsAbstractProfileGenerator * createProfileGenerator(const QgsProfileRequest &request) override
Given a profile request, returns a new profile generator ready for generating elevation profiles.
QStringList extraDatasetUris() const
Returns the list of extra dataset URIs associated with this layer.
QgsMeshDataBlock datasetValues(const QgsMeshDatasetIndex &index, int valueIndex, int count) const
Returns N vector/scalar values from the index from the dataset.
bool writeStyle(QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories) const override
Write just the symbology information for the layer into the document.
void setLabeling(QgsAbstractMeshLayerLabeling *labeling)
Sets labeling configuration.
void setTransformContext(const QgsCoordinateTransformContext &transformContext) override
Sets the coordinate transform context to transformContext.
int meshEdgeCount() const
Returns the edges count of the mesh frame.
QgsMeshSimplificationSettings meshSimplificationSettings() const
Returns mesh simplification settings.
QgsMapLayerRenderer * createMapRenderer(QgsRenderContext &rendererContext) override
Returns new instance of QgsMapLayerRenderer that will be used for rendering of given context.
bool isEditable() const override
Returns true if the layer can be edited.
QString loadDefaultStyle(bool &resultFlag) final
Retrieve the default style for this layer if one exists (either as a .qml file on disk or as a record...
QgsMeshDataBlock areFacesActive(const QgsMeshDatasetIndex &index, int faceIndex, int count) const
Returns whether the faces are active for particular dataset.
void setLabelsEnabled(bool enabled)
Sets whether labels should be enabled for the layer.
bool isFaceActive(const QgsMeshDatasetIndex &index, int faceIndex) const
Returns N vector/scalar values from the face index from the dataset for 3d stacked meshes.
QgsMeshDatasetMetadata datasetMetadata(const QgsMeshDatasetIndex &index) const
Returns the dataset metadata.
bool saveDataset(const QString &path, int datasetGroupIndex, QString driver)
Saves datasets group on file with the specified driver.
bool supportsEditing() const override
Returns whether the layer supports editing or not.
QgsMeshDataProvider * dataProvider() override
Returns the layer's data provider, it may be nullptr.
QgsInterval firstValidTimeStep() const
Returns the first valid time step of the dataset groups, invalid QgInterval if no time step is presen...
QgsInterval datasetRelativeTime(const QgsMeshDatasetIndex &index)
Returns the relative time of the dataset from the reference time of its group.
QgsMeshDatasetIndex datasetIndexAtTime(const QgsDateTimeRange &timeRange, int datasetGroupIndex) const
Returns dataset index from datasets group depending on the time range.
Q_DECL_DEPRECATED bool startFrameEditing(const QgsCoordinateTransform &transform)
Starts editing of the mesh frame.
bool reindex(const QgsCoordinateTransform &transform, bool renumber)
Re-indexes the faces and vertices, and renumber the indexes if renumber is true.
QgsMeshDatasetValue datasetValue(const QgsMeshDatasetIndex &index, int valueIndex) const
Returns vector/scalar value associated with the index from the dataset To read multiple continuous va...
QgsMeshLayer(const QString &path=QString(), const QString &baseName=QString(), const QString &providerLib=u"mesh_memory"_s, const QgsMeshLayer::LayerOptions &options=QgsMeshLayer::LayerOptions())
Constructor - creates a mesh layer.
QgsMeshTimeSettings timeSettings() const
Returns time format settings.
QgsMapLayerTemporalProperties * temporalProperties() override
Returns the layer's temporal properties.
bool writeSymbology(QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories) const override
Write the style for the layer into the document provided.
int meshVertexCount() const
Returns the vertices count of the mesh frame.
bool labelsEnabled() const
Returns whether the layer contains labels which are enabled and should be drawn.
void setReferenceTime(const QDateTime &referenceTime)
Sets the reference time of the layer.
bool readStyle(const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories) override
Read the style for the current layer from the DOM node supplied.
int extraDatasetGroupCount() const
Returns the extra dataset groups count handle by the layer.
bool datasetsPathUnique(const QString &path)
Checks whether that datasets path is already added to this mesh layer.
qint64 datasetRelativeTimeInMilliseconds(const QgsMeshDatasetIndex &index)
Returns the relative time (in milliseconds) of the dataset from the reference time of its group.
QgsMeshDatasetIndex activeScalarDatasetAtTime(const QgsDateTimeRange &timeRange, int group=-1) const
Returns dataset index from active scalar group depending on the time range.
QgsTriangularMesh * triangularMesh(double minimumTriangleSize=0) const
Returns triangular mesh (nullptr before rendering or calling to updateMesh).
QgsMeshDatasetIndex staticScalarDatasetIndex(int group=-1) const
Returns the static scalar dataset index that is rendered if the temporal properties is not active.
void setTemporalMatchingMethod(const QgsMeshDataProviderTemporalCapabilities::MatchingTemporalDatasetMethod &matchingMethod)
Sets the method used to match the temporal dataset from a requested time, see activeVectorDatasetAtTi...
bool removeDatasets(const QString &name)
Removes datasets from the mesh with given name.
QgsMeshDatasetGroupTreeItem * datasetGroupTreeRootItem() const
Returns the root items of the dataset group tree item.
QgsMesh3DDataBlock dataset3dValues(const QgsMeshDatasetIndex &index, int faceIndex, int count) const
Returns N vector/scalar values from the face index from the dataset for 3d stacked meshes.
QgsMesh3DDataBlock dataset3dValue(const QgsMeshDatasetIndex &index, const QgsPointXY &point) const
Returns the 3d values of stacked 3d mesh defined by the given point.
bool readSymbology(const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories) override
Read the symbology for the current layer from the DOM node supplied.
QString decodedSource(const QString &source, const QString &provider, const QgsReadWriteContext &context) const override
Called by readLayerXML(), used by derived classes to decode provider's specific data source from proj...
void reloaded()
Emitted when the mesh layer is reloaded, see reload().
QString formatTime(double hours)
Returns (date) time in hours formatted to human readable form.
QgsMeshDatasetGroupMetadata datasetGroupMetadata(const QgsMeshDatasetIndex &index) const
Returns the dataset groups metadata.
QgsMeshLayerRendererCache * rendererCache()
Returns native mesh (nullptr before rendering).
void setTimeSettings(const QgsMeshTimeSettings &settings)
Sets time format settings.
Represents a mesh renderer settings for mesh objects.
void setEnabled(bool enabled)
Sets whether mesh structure rendering is enabled.
Represents a mesh renderer settings for scalar datasets.
void setClassificationMinimumMaximum(double minimum, double maximum)
Sets min/max values used for creation of the color ramp shader.
void setColorRampShader(const QgsColorRampShader &shader)
Sets color ramp shader function.
QgsColorRampShader colorRampShader() const
Returns color ramp shader function.
@ NoResampling
Does not use resampling.
@ NeighbourAverage
Does a simple average of values defined for all surrounding faces/vertices.
void setEdgeStrokeWidth(const QgsInterpolatedLineWidth &strokeWidth)
Sets the stroke width used to render edges scalar dataset.
void setDataResamplingMethod(const DataResamplingMethod &dataResamplingMethod)
Sets data interpolation method.
Represents all mesh renderer settings.
void setActiveVectorDatasetGroup(int activeVectorDatasetGroup)
Sets the active vector dataset group.
QgsMeshRendererScalarSettings scalarSettings(int groupIndex) const
Returns renderer settings.
int activeVectorDatasetGroup() const
Returns the active vector dataset group.
bool hasVectorSettings(int groupIndex) const
Returns whether groupIndex has existing vector settings.
bool removeVectorSettings(int groupIndex)
Removes vector settings for groupIndex.
int activeScalarDatasetGroup() const
Returns the active scalar dataset group.
QgsMeshRendererVectorSettings vectorSettings(int groupIndex) const
Returns renderer settings.
void setActiveScalarDatasetGroup(int activeScalarDatasetGroup)
Sets the active scalar dataset group.
void setVectorSettings(int groupIndex, const QgsMeshRendererVectorSettings &settings)
Sets new renderer settings.
bool removeScalarSettings(int groupIndex)
Removes scalar settings with groupIndex.
void setScalarSettings(int groupIndex, const QgsMeshRendererScalarSettings &settings)
Sets new renderer settings.
void setColorRampShader(const QgsColorRampShader &colorRampShader)
Returns the color ramp shader used to render vector datasets.
Represents an overview renderer settings.
double reductionFactor() const
Returns the reduction factor used to build simplified mesh.
bool isEnabled() const
Returns if the overview is active.
Represents a mesh time settings for mesh datasets.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
static Qgis::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a Qgis::BlendMode corresponding to a QPainter::CompositionMode.
static QPainter::CompositionMode getCompositionMode(Qgis::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a Qgis::BlendMode.
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
Represents a 2D point.
Definition qgspointxy.h:62
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
Definition qgspointxy.h:209
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
double sqrDistToSegment(double x1, double y1, double x2, double y2, QgsPointXY &minDistPoint, double epsilon=Qgis::DEFAULT_SEGMENT_EPSILON) const
Returns the minimum distance between this point and a segment.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
double x
Definition qgspoint.h:56
double distance(double x, double y) const
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Definition qgspoint.h:466
double y
Definition qgspoint.h:57
Encapsulates properties and constraints relating to fetching elevation profiles from different source...
QString absoluteToRelativeUri(const QString &providerKey, const QString &uri, const QgsReadWriteContext &context) const
Converts absolute path(s) to relative path(s) in the given provider-specific URI.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
QString relativeToAbsoluteUri(const QString &providerKey, const QString &uri, const QgsReadWriteContext &context) const
Converts relative path(s) to absolute path(s) in the given provider-specific URI.
virtual void setMaximumValue(double value)
Sets the maximum value for the raster shader.
virtual void setMinimumValue(double value)
Sets the minimum value for the raster shader.
Allows entering a context category and takes care of leaving this category on deletion of the class.
A container for the context for various read/write operations on objects.
QgsReadWriteContextCategoryPopper enterCategory(const QString &category, const QString &details=QString()) const
Push a category to the stack.
const QgsPathResolver & pathResolver() const
Returns path resolver for conversion between relative and absolute paths.
A rectangle specified with double values.
Q_INVOKABLE QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be rounded to the spec...
void setNull()
Mark a rectangle as being null (holding no spatial information).
Contains information about the context of a rendering operation.
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
QgsColorRamp * colorRamp(const QString &name) const
Returns a new copy of the specified color ramp.
Definition qgsstyle.cpp:535
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
Definition qgsstyle.cpp:164
void setIsActive(bool active)
Sets whether the temporal property is active.
const QgsDateTimeRange & temporalRange() const
Returns the datetime range for the object.
bool isTemporal() const
Returns true if the object's temporal range is enabled, and the object will be filtered when renderin...
T begin() const
Returns the beginning of the range.
Definition qgsrange.h:408
static QgsMeshEditingError checkTopology(const QgsMesh &mesh, int maxVerticesPerFace)
Checks the topology of the mesh mesh, if error occurs, this mesh can't be edited.
A triangular/derived mesh with vertices in map coordinates.
const QVector< QgsMeshFace > & triangles() const
Returns triangles.
QList< int > edgeIndexesForRectangle(const QgsRectangle &rectangle) const
Finds indexes of edges intersecting given bounding box It uses spatial indexing.
const QVector< QgsMeshVertex > & vertices() const
Returns vertices in map coordinate system.
const QVector< QgsMeshEdge > & edges() const
Returns edges.
bool contains(const QgsMesh::ElementType &type) const
Returns whether the mesh contains mesh elements of given type.
const QVector< int > & trianglesToNativeFaces() const
Returns mapping between triangles and original faces.
QList< int > faceIndexesForRectangle(const QgsRectangle &rectangle) const
Finds indexes of triangles intersecting given bounding box It uses spatial indexing.
int faceIndexForPoint_v2(const QgsPointXY &point) const
Finds index of triangle at given point It uses spatial indexing and don't use geos to be faster.
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:80
QVector< int > QgsMeshFace
List of vertex indexes.
QPair< int, int > QgsMeshEdge
Edge is a straight line seqment between 2 points.
QgsPoint QgsMeshVertex
xyz coords of vertex
QgsTemporalRange< QDateTime > QgsDateTimeRange
QgsRange which stores a range of date times.
Definition qgsrange.h:705
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
Setting options for creating vector data providers.
Setting options for loading mesh layers.
QgsCoordinateTransformContext transformContext
Coordinate transform context.
bool loadDefaultStyle
Set to true if the default layer style should be loaded.
bool skipCrsValidation
Controls whether the layer is allowed to have an invalid/unknown CRS.
Mesh - vertices, edges and faces.
ElementType
Defines type of mesh elements.