QGIS API Documentation 4.1.0-Master (0cdd3ae6384)
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
112{
113 delete mLabeling;
114 delete mDataProvider;
115}
116
123
125{
127
128 return mDataProvider;
129}
130
132{
134
136 if ( mDataProvider )
137 {
138 options.transformContext = mDataProvider->transformContext();
139 }
140 QgsMeshLayer *layer = new QgsMeshLayer( source(), name(), mProviderKey, options );
141 QgsMapLayer::clone( layer );
142
143 layer->mElevationProperties = mElevationProperties->clone();
144 layer->mElevationProperties->setParent( layer );
145
146 if ( auto *lLabeling = labeling() )
147 {
148 layer->setLabeling( lLabeling->clone() );
149 }
151
152 for ( const QString &extraDataset : mExtraDatasetUri )
153 {
154 layer->addDatasets( extraDataset );
155 }
156
157 layer->setRendererSettings( mRendererSettings );
158
159 return layer;
160}
161
163{
165
166 if ( mMeshEditor )
167 return mMeshEditor->extent();
168
169 if ( mDataProvider )
170 return mDataProvider->extent();
171 else
172 {
173 QgsRectangle rec;
174 rec.setNull();
175 return rec;
176 }
177}
178
180{
182
183 if ( !mDataProvider )
184 return false;
185
186 if ( mMeshEditor )
187 return true;
188
189 const QgsMeshDriverMetadata driverMetadata = mDataProvider->driverMetadata();
190
192}
193
194QString QgsMeshLayer::loadDefaultStyle( bool &resultFlag )
195{
197
198 const QList<int> groupsList = datasetGroupsIndexes();
199
200 for ( const int index : groupsList )
201 assignDefaultStyleToDatasetGroup( index );
202
203
204 QgsMeshRendererMeshSettings meshSettings;
205 if ( !groupsList.isEmpty() )
206 {
207 // Show data from the first dataset group
208 mRendererSettings.setActiveScalarDatasetGroup( 0 );
209 // If the first dataset group has nan min/max, display the mesh to avoid nothing displayed
211 if ( meta.maximum() == std::numeric_limits<double>::quiet_NaN() && meta.minimum() == std::numeric_limits<double>::quiet_NaN() )
212 meshSettings.setEnabled( true );
213 }
214 else
215 {
216 // show at least the mesh by default
217 meshSettings.setEnabled( true );
218 }
219
220 mRendererSettings.setNativeMeshSettings( meshSettings );
221
222 for ( const int i : groupsList )
223 {
224 assignDefaultStyleToDatasetGroup( i );
225
226 // Sets default resample method for scalar dataset
228 QgsMeshRendererScalarSettings scalarSettings = mRendererSettings.scalarSettings( i );
229 switch ( meta.dataType() )
230 {
232 case QgsMeshDatasetGroupMetadata::DataOnVolumes: // data on volumes are averaged to 2D data on faces
234 break;
237 break;
239 break;
240 }
241
242 //override color ramp if the values in the dataset group are classified
243 applyClassificationOnScalarSettings( meta, scalarSettings );
244
245 mRendererSettings.setScalarSettings( i, scalarSettings );
246 }
247
248 if ( !groupsList.isEmpty() )
249 {
250 emit rendererChanged();
252 }
253
254 return QgsMapLayer::loadDefaultStyle( resultFlag );
255}
256
257bool QgsMeshLayer::removeDatasets( const QString &name )
258{
259 const int index = mDatasetGroupStore->indexFromGroupName( name );
260
261 if ( index == -1 )
262 {
263 return false;
264 }
265
266 const QgsMeshDatasetGroupMetadata groupMetadata = datasetGroupMetadata( index );
267
268 mDatasetGroupStore->removeDatasetGroup( index );
269
270 if ( mExtraDatasetUri.contains( groupMetadata.uri() ) )
271 {
272 mExtraDatasetUri.removeOne( groupMetadata.uri() );
273 }
274
276
277 emit dataSourceChanged();
278 return true;
279}
280
281bool QgsMeshLayer::addDatasets( const QString &path, const QDateTime &defaultReferenceTime )
282{
284
286 const bool isTemporalBefore = temporalCapabilities->hasTemporalCapabilities();
287 if ( mDatasetGroupStore->addPersistentDatasets( path ) )
288 {
289 mExtraDatasetUri.append( path );
290 QgsMeshLayerTemporalProperties *temporalProperties = qobject_cast< QgsMeshLayerTemporalProperties * >( mTemporalProperties );
291 if ( !isTemporalBefore && temporalCapabilities->hasTemporalCapabilities() )
292 {
293 mTemporalProperties->setDefaultsFromDataProviderTemporalCapabilities( temporalCapabilities );
294
295 if ( !temporalProperties->referenceTime().isValid() )
296 {
297 QDateTime referenceTime = defaultReferenceTime;
298 if ( !defaultReferenceTime.isValid() ) // If project reference time is invalid, use current date
299 referenceTime = QDateTime( QDate::currentDate(), QTime( 0, 0, 0 ), Qt::UTC );
300 temporalProperties->setReferenceTime( referenceTime, temporalCapabilities );
301 }
302
303 mTemporalProperties->setIsActive( true );
304 }
305 emit dataSourceChanged();
306 return true;
307 }
308
309 return false;
310}
311
313{
315
316 if ( mDatasetGroupStore->addDatasetGroup( std::unique_ptr< QgsMeshDatasetGroup >( datasetGroup ) ) )
317 {
318 emit dataChanged();
319 return true;
320 }
321 return false;
322}
323
324bool QgsMeshLayer::saveDataset( const QString &path, int datasetGroupIndex, QString driver )
325{
327
328 return mDatasetGroupStore->saveDatasetGroup( path, datasetGroupIndex, driver );
329}
330
332{
334
335 return mNativeMesh.get();
336}
337
339{
341
342 return mNativeMesh.get();
343}
344
345QgsTriangularMesh *QgsMeshLayer::triangularMesh( double minimumTriangleSize ) const
346{
348
349 for ( const std::unique_ptr<QgsTriangularMesh> &lod : mTriangularMeshes )
350 {
351 if ( lod && lod->averageTriangleSize() > minimumTriangleSize )
352 return lod.get();
353 }
354
355 if ( !mTriangularMeshes.empty() )
356 return mTriangularMeshes.back().get();
357 else
358 return nullptr;
359}
360
362{
364
365 return mTriangularMeshes.size();
366}
367
369{
371
372 if ( mTriangularMeshes.empty() )
373 return nullptr;
374 if ( lodIndex < 0 )
375 return mTriangularMeshes.front().get();
376
377 if ( lodIndex >= int( mTriangularMeshes.size() ) )
378 return mTriangularMeshes.back().get();
379
380 return mTriangularMeshes.at( lodIndex ).get();
381}
382
384{
386
387 // Native mesh
388 if ( !mNativeMesh )
389 {
390 // lazy loading of mesh data
391 fillNativeMesh();
392 }
393
394 // Triangular mesh
395 if ( mTriangularMeshes.empty() )
396 {
397 QgsTriangularMesh *baseMesh = new QgsTriangularMesh;
398 mTriangularMeshes.emplace_back( baseMesh );
399 }
400
401 if ( mTriangularMeshes[0].get()->update( mNativeMesh.get(), transform ) )
402 mTriangularMeshes.resize( 1 ); //if the base triangular mesh is effectivly updated, remove simplified meshes
403
404 createSimplifiedMeshes();
405}
406
407QgsMeshLayerRendererCache *QgsMeshLayer::rendererCache()
408{
410
411 return mRendererCache.get();
412}
413
420
421void QgsMeshLayer::setRendererSettings( const QgsMeshRendererSettings &settings, const bool repaint )
422{
424
425 const int oldActiveScalar = mRendererSettings.activeScalarDatasetGroup();
426 const int oldActiveVector = mRendererSettings.activeVectorDatasetGroup();
427 mRendererSettings = settings;
428
429 if ( oldActiveScalar != mRendererSettings.activeScalarDatasetGroup() )
430 emit activeScalarDatasetGroupChanged( mRendererSettings.activeScalarDatasetGroup() );
431
432 if ( oldActiveVector != mRendererSettings.activeVectorDatasetGroup() )
433 emit activeVectorDatasetGroupChanged( mRendererSettings.activeVectorDatasetGroup() );
434
435 emit rendererChanged();
437
438 if ( repaint )
439 {
441 }
442}
443
450
452{
454
455 mTimeSettings = settings;
456 emit timeSettingsChanged();
457}
458
459QString QgsMeshLayer::formatTime( double hours )
460{
462
463 if ( dataProvider() && dataProvider()->temporalCapabilities()->hasReferenceTime() )
464 return QgsMeshLayerUtils::formatTime( hours, mTemporalProperties->referenceTime(), mTimeSettings );
465 else
466 return QgsMeshLayerUtils::formatTime( hours, QDateTime(), mTimeSettings );
467}
468
470{
472
473 return mDatasetGroupStore->datasetGroupCount();
474}
475
477{
479
480 return mDatasetGroupStore->extraDatasetGroupCount();
481}
482
484{
486
487 return mDatasetGroupStore->datasetGroupIndexes();
488}
489
491{
493
494 return mDatasetGroupStore->enabledDatasetGroupIndexes();
495}
496
498{
500
501 return mDatasetGroupStore->datasetGroupMetadata( index );
502}
503
505{
507
508 return mDatasetGroupStore->datasetCount( index.group() );
509}
510
512{
514
515 return mDatasetGroupStore->datasetMetadata( index );
516}
517
519{
521
522 return mDatasetGroupStore->datasetValue( index, valueIndex );
523}
524
525QgsMeshDataBlock QgsMeshLayer::datasetValues( const QgsMeshDatasetIndex &index, int valueIndex, int count ) const
526{
528
529 return mDatasetGroupStore->datasetValues( index, valueIndex, count );
530}
531
532QgsMesh3DDataBlock QgsMeshLayer::dataset3dValues( const QgsMeshDatasetIndex &index, int faceIndex, int count ) const
533{
535
536 return mDatasetGroupStore->dataset3dValues( index, faceIndex, count );
537}
538
539QgsMeshDataBlock QgsMeshLayer::areFacesActive( const QgsMeshDatasetIndex &index, int faceIndex, int count ) const
540{
542
543 return mDatasetGroupStore->areFacesActive( index, faceIndex, count );
544}
545
546bool QgsMeshLayer::isFaceActive( const QgsMeshDatasetIndex &index, int faceIndex ) const
547{
549
550 return mDatasetGroupStore->isFaceActive( index, faceIndex );
551}
552
553QgsMeshDatasetValue QgsMeshLayer::datasetValue( const QgsMeshDatasetIndex &index, const QgsPointXY &point, double searchRadius ) const
554{
556
558 const QgsTriangularMesh *mesh = triangularMesh();
559
560 if ( mesh && index.isValid() )
561 {
563 {
564 const QgsRectangle searchRectangle( point.x() - searchRadius, point.y() - searchRadius, point.x() + searchRadius, point.y() + searchRadius );
565 return dataset1dValue( index, point, searchRadius );
566 }
567 const int faceIndex = mesh->faceIndexForPoint_v2( point );
568 if ( faceIndex >= 0 )
569 {
570 const int nativeFaceIndex = mesh->trianglesToNativeFaces().at( faceIndex );
572 if ( isFaceActive( index, nativeFaceIndex ) )
573 {
574 switch ( dataType )
575 {
577 {
578 value = datasetValue( index, nativeFaceIndex );
579 }
580 break;
581
583 {
584 const QgsMeshFace &face = mesh->triangles()[faceIndex];
585 const int v1 = face[0], v2 = face[1], v3 = face[2];
586 const QgsPoint p1 = mesh->vertices()[v1], p2 = mesh->vertices()[v2], p3 = mesh->vertices()[v3];
587 const QgsMeshDatasetValue val1 = datasetValue( index, v1 );
588 const QgsMeshDatasetValue val2 = datasetValue( index, v2 );
589 const QgsMeshDatasetValue val3 = datasetValue( index, v3 );
590 const double x = QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.x(), val2.x(), val3.x(), point );
591 double y = std::numeric_limits<double>::quiet_NaN();
592 const bool isVector = datasetGroupMetadata( index ).isVector();
593 if ( isVector )
594 y = QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.y(), val2.y(), val3.y(), point );
595
596 value = QgsMeshDatasetValue( x, y );
597 }
598 break;
599
601 {
602 const QgsMesh3DAveragingMethod *avgMethod = mRendererSettings.averagingMethod();
603 if ( avgMethod )
604 {
605 const QgsMesh3DDataBlock block3d = dataset3dValues( index, nativeFaceIndex, 1 );
606 const QgsMeshDataBlock block2d = avgMethod->calculate( block3d );
607 if ( block2d.isValid() )
608 {
609 value = block2d.value( 0 );
610 }
611 }
612 }
613 break;
614
615 default:
616 break;
617 }
618 }
619 }
620 }
621
622 return value;
623}
624
626{
628
629 QgsMesh3DDataBlock block3d;
630
631 const QgsTriangularMesh *baseTriangularMesh = triangularMesh();
632
633 if ( baseTriangularMesh && dataProvider() && dataProvider()->isValid() && index.isValid() )
634 {
637 {
638 const int faceIndex = baseTriangularMesh->faceIndexForPoint_v2( point );
639 if ( faceIndex >= 0 )
640 {
641 const int nativeFaceIndex = baseTriangularMesh->trianglesToNativeFaces().at( faceIndex );
642 block3d = dataset3dValues( index, nativeFaceIndex, 1 );
643 }
644 }
645 }
646 return block3d;
647}
648
649QgsMeshDatasetValue QgsMeshLayer::dataset1dValue( const QgsMeshDatasetIndex &index, const QgsPointXY &point, double searchRadius ) const
650{
652
654 QgsPointXY projectedPoint;
655 const int selectedIndex = closestEdge( point, searchRadius, projectedPoint );
656 const QgsTriangularMesh *mesh = triangularMesh();
657 if ( selectedIndex >= 0 )
658 {
660 switch ( dataType )
661 {
663 {
664 value = datasetValue( index, selectedIndex );
665 }
666 break;
667
669 {
670 const QgsMeshEdge &edge = mesh->edges()[selectedIndex];
671 const int v1 = edge.first, v2 = edge.second;
672 const QgsPoint p1 = mesh->vertices()[v1], p2 = mesh->vertices()[v2];
673 const QgsMeshDatasetValue val1 = datasetValue( index, v1 );
674 const QgsMeshDatasetValue val2 = datasetValue( index, v2 );
675 const double edgeLength = p1.distance( p2 );
676 const double dist1 = p1.distance( projectedPoint.x(), projectedPoint.y() );
677 value = QgsMeshLayerUtils::interpolateFromVerticesData( dist1 / edgeLength, val1, val2 );
678 }
679 break;
680 default:
681 break;
682 }
683 }
684
685 return value;
686}
687
689{
691
692 if ( mDataProvider )
693 mDataProvider->setTransformContext( transformContext );
695}
696
697QgsMeshDatasetIndex QgsMeshLayer::datasetIndexAtTime( const QgsDateTimeRange &timeRange, int datasetGroupIndex ) const
698{
700
701 if ( !mTemporalProperties->isActive() )
702 return QgsMeshDatasetIndex( datasetGroupIndex, -1 );
703
704 const QDateTime layerReferenceTime = mTemporalProperties->referenceTime();
705 QDateTime utcTime = timeRange.begin();
706 if ( utcTime.timeSpec() != Qt::UTC )
707 utcTime.setTimeSpec( Qt::UTC );
708 const qint64 startTime = layerReferenceTime.msecsTo( utcTime );
709
710 return mDatasetGroupStore->datasetIndexAtTime( startTime, datasetGroupIndex, mTemporalProperties->matchingMethod() );
711}
712
713QgsMeshDatasetIndex QgsMeshLayer::datasetIndexAtRelativeTime( const QgsInterval &relativeTime, int datasetGroupIndex ) const
714{
716
717 return mDatasetGroupStore->datasetIndexAtTime( relativeTime.seconds() * 1000, datasetGroupIndex, mTemporalProperties->matchingMethod() );
718}
719
720QList<QgsMeshDatasetIndex> QgsMeshLayer::datasetIndexInRelativeTimeInterval( const QgsInterval &startRelativeTime, const QgsInterval &endRelativeTime, int datasetGroupIndex ) const
721{
723
724 qint64 usedRelativeTime1 = startRelativeTime.seconds() * 1000;
725 qint64 usedRelativeTime2 = endRelativeTime.seconds() * 1000;
726
727 //adjust relative time if layer reference time is different from provider reference time
728 if ( mTemporalProperties->referenceTime().isValid() && mDataProvider && mDataProvider->isValid() && mTemporalProperties->referenceTime() != mDataProvider->temporalCapabilities()->referenceTime() )
729 {
730 usedRelativeTime1 = usedRelativeTime1 + mTemporalProperties->referenceTime().msecsTo( mDataProvider->temporalCapabilities()->referenceTime() );
731 usedRelativeTime2 = usedRelativeTime2 + mTemporalProperties->referenceTime().msecsTo( mDataProvider->temporalCapabilities()->referenceTime() );
732 }
733
734 return mDatasetGroupStore->datasetIndexInTimeInterval( usedRelativeTime1, usedRelativeTime2, datasetGroupIndex );
735}
736
737void QgsMeshLayer::applyClassificationOnScalarSettings( const QgsMeshDatasetGroupMetadata &meta, QgsMeshRendererScalarSettings &scalarSettings ) const
738{
740
741 if ( meta.extraOptions().contains( u"classification"_s ) )
742 {
743 QgsColorRampShader colorRampShader = scalarSettings.colorRampShader();
744 QgsColorRamp *colorRamp = colorRampShader.sourceColorRamp();
745 const QStringList classes = meta.extraOptions()[u"classification"_s].split( u";;"_s );
746
747 QString units;
748 if ( meta.extraOptions().contains( u"units"_s ) )
749 units = meta.extraOptions()[u"units"_s];
750
751 QVector<QVector<double>> bounds;
752 for ( const QString &classe : classes )
753 {
754 const QStringList boundsStr = classe.split( ',' );
755 QVector<double> bound;
756 for ( const QString &boundStr : boundsStr )
757 bound.append( boundStr.toDouble() );
758 bounds.append( bound );
759 }
760
761 if ( ( bounds.count() == 1 && bounds.first().count() > 2 ) || // at least a class with two value
762 ( bounds.count() > 1 ) ) // or at least two classes
763 {
764 const QVector<double> firstClass = bounds.first();
765 const QVector<double> lastClass = bounds.last();
766 const double minValue = firstClass.count() > 1 ? ( firstClass.first() + firstClass.last() ) / 2 : firstClass.first();
767 const double maxValue = lastClass.count() > 1 ? ( lastClass.first() + lastClass.last() ) / 2 : lastClass.first();
768 const double diff = maxValue - minValue;
769 QList<QgsColorRampShader::ColorRampItem> colorRampItemlist;
770 for ( int i = 0; i < bounds.count(); ++i )
771 {
772 const QVector<double> &boundClass = bounds.at( i );
773 QgsColorRampShader::ColorRampItem item;
774 item.value = i + 1;
775 if ( !boundClass.isEmpty() )
776 {
777 const double scalarValue = ( boundClass.first() + boundClass.last() ) / 2;
778 item.color = colorRamp->color( ( scalarValue - minValue ) / diff );
779 if ( i != 0 && i < bounds.count() - 1 ) //The first and last labels are treated after
780 {
781 item.label = QString( ( "%1 - %2 %3" ) ).arg( QString::number( boundClass.first() ) ).arg( QString::number( boundClass.last() ) ).arg( units );
782 }
783 }
784 colorRampItemlist.append( item );
785 }
786 //treat first and last labels
787 if ( firstClass.count() == 1 )
788 colorRampItemlist.first().label = QObject::tr( "below %1 %2" ).arg( QString::number( firstClass.first() ) ).arg( units );
789 else
790 {
791 colorRampItemlist.first().label = QString( ( "%1 - %2 %3" ) ).arg( QString::number( firstClass.first() ) ).arg( QString::number( firstClass.last() ) ).arg( units );
792 }
793
794 if ( lastClass.count() == 1 )
795 colorRampItemlist.last().label = QObject::tr( "above %1 %2" ).arg( QString::number( lastClass.first() ) ).arg( units );
796 else
797 {
798 colorRampItemlist.last().label = QString( ( "%1 - %2 %3" ) ).arg( QString::number( lastClass.first() ) ).arg( QString::number( lastClass.last() ) ).arg( units );
799 }
800
801 colorRampShader.setMinimumValue( 0 );
802 colorRampShader.setMaximumValue( colorRampItemlist.count() - 1 );
803 scalarSettings.setClassificationMinimumMaximum( 0, colorRampItemlist.count() - 1 );
804 colorRampShader.setColorRampItemList( colorRampItemlist );
807 }
808
809 scalarSettings.setColorRampShader( colorRampShader );
811 }
812}
813
815{
817
818 if ( mTemporalProperties->isActive() )
819 return datasetIndexAtTime( timeRange, group >= 0 ? group : mRendererSettings.activeScalarDatasetGroup() );
820 else
821 return QgsMeshDatasetIndex( group >= 0 ? group : mRendererSettings.activeScalarDatasetGroup(), mStaticScalarDatasetIndex );
822}
823
825{
827
828 if ( mTemporalProperties->isActive() )
829 return datasetIndexAtTime( timeRange, group >= 0 ? group : mRendererSettings.activeVectorDatasetGroup() );
830 else
831 return QgsMeshDatasetIndex( group >= 0 ? group : mRendererSettings.activeVectorDatasetGroup(), mStaticVectorDatasetIndex );
832}
833
834void QgsMeshLayer::fillNativeMesh()
835{
837
838 Q_ASSERT( !mNativeMesh );
839
840 mNativeMesh = std::make_unique<QgsMesh>();
841
842 if ( !( dataProvider() && dataProvider()->isValid() ) )
843 return;
844
845 dataProvider()->populateMesh( mNativeMesh.get() );
846}
847
848void QgsMeshLayer::onDatasetGroupsAdded( const QList<int> &datasetGroupIndexes )
849{
851
852 // assign default style to new dataset groups
853 for ( int datasetGroupIndex : datasetGroupIndexes )
854 {
855 if ( !mRendererSettings.hasSettings( datasetGroupIndex ) )
856 assignDefaultStyleToDatasetGroup( datasetGroupIndex );
857 }
858
859 temporalProperties()->setIsActive( mDatasetGroupStore->hasTemporalCapabilities() );
860 emit rendererChanged();
861}
862
863void QgsMeshLayer::onMeshEdited()
864{
866
867 mRendererCache = std::make_unique<QgsMeshLayerRendererCache>();
868 emit layerModified();
871}
872
874{
876
877 return mDatasetGroupStore->datasetGroupTreeItem();
878}
879
881{
883
884 mDatasetGroupStore->setDatasetGroupTreeItem( rootItem );
885 updateActiveDatasetGroups();
886}
887
889{
891
892 return QgsMeshDatasetIndex( group >= 0 ? group : mRendererSettings.activeVectorDatasetGroup(), mStaticVectorDatasetIndex );
893}
894
895void QgsMeshLayer::setReferenceTime( const QDateTime &referenceTime )
896{
898
899 if ( auto *lDataProvider = dataProvider() )
900 mTemporalProperties->setReferenceTime( referenceTime, lDataProvider->temporalCapabilities() );
901 else
902 mTemporalProperties->setReferenceTime( referenceTime, nullptr );
903}
904
906{
908
909 mTemporalProperties->setMatchingMethod( matchingMethod );
910}
911
912int QgsMeshLayer::closestEdge( const QgsPointXY &point, double searchRadius, QgsPointXY &projectedPoint ) const
913{
915
916 const QgsRectangle searchRectangle( point.x() - searchRadius, point.y() - searchRadius, point.x() + searchRadius, point.y() + searchRadius );
917 const QgsTriangularMesh *mesh = triangularMesh();
918 // search for the closest edge in search area from point
919 const QList<int> edgeIndexes = mesh->edgeIndexesForRectangle( searchRectangle );
920 int selectedIndex = -1;
921 projectedPoint = QgsPointXY();
922 if ( mesh->contains( QgsMesh::Edge ) && mDataProvider->isValid() )
923 {
924 double sqrMaxDistFromPoint = pow( searchRadius, 2 );
925 for ( const int edgeIndex : edgeIndexes )
926 {
927 const QgsMeshEdge &edge = mesh->edges().at( edgeIndex );
928 const QgsMeshVertex &vertex1 = mesh->vertices()[edge.first];
929 const QgsMeshVertex &vertex2 = mesh->vertices()[edge.second];
930 QgsPointXY projPoint;
931 const double sqrDist = point.sqrDistToSegment( vertex1.x(), vertex1.y(), vertex2.x(), vertex2.y(), projPoint, 0 );
932 if ( sqrDist < sqrMaxDistFromPoint )
933 {
934 selectedIndex = edgeIndex;
935 projectedPoint = projPoint;
936 sqrMaxDistFromPoint = sqrDist;
937 }
938 }
939 }
940
941 return selectedIndex;
942}
943
944int QgsMeshLayer::closestVertex( const QgsPointXY &point, double searchRadius, QgsPointXY &projectedPoint ) const
945{
947
948 const QgsTriangularMesh *mesh = triangularMesh();
949 int selectedIndex = -1;
950 projectedPoint = QgsPointXY();
951 if ( !mesh )
952 return selectedIndex;
953
954 const QgsRectangle rectangle( point.x() - searchRadius, point.y() - searchRadius, point.x() + searchRadius, point.y() + searchRadius );
955 double maxDistance = searchRadius;
956 //attempt to snap on edges's vertices
957 const QList<int> edgeIndexes = mesh->edgeIndexesForRectangle( rectangle );
958 for ( const int edgeIndex : edgeIndexes )
959 {
960 const QgsMeshEdge &edge = mesh->edges().at( edgeIndex );
961 const QgsMeshVertex &vertex1 = mesh->vertices()[edge.first];
962 const QgsMeshVertex &vertex2 = mesh->vertices()[edge.second];
963 const double dist1 = point.distance( vertex1 );
964 const double dist2 = point.distance( vertex2 );
965 if ( dist1 < maxDistance )
966 {
967 maxDistance = dist1;
968 projectedPoint = vertex1;
969 selectedIndex = edge.first;
970 }
971 if ( dist2 < maxDistance )
972 {
973 maxDistance = dist2;
974 projectedPoint = vertex2;
975 selectedIndex = edge.second;
976 }
977 }
978
979 //attempt to snap on face's vertices
980 const QList<int> faceIndexes = mesh->faceIndexesForRectangle( rectangle );
981 for ( const int faceIndex : faceIndexes )
982 {
983 const QgsMeshFace &face = mesh->triangles().at( faceIndex );
984 for ( int i = 0; i < 3; ++i )
985 {
986 const QgsMeshVertex &vertex = mesh->vertices()[face.at( i )];
987 const double dist = point.distance( vertex );
988 if ( dist < maxDistance )
989 {
990 maxDistance = dist;
991 projectedPoint = vertex;
992 selectedIndex = face.at( i );
993 }
994 }
995 }
996
997 return selectedIndex;
998}
999
1000int QgsMeshLayer::closestFace( const QgsPointXY &point, double searchRadius, QgsPointXY &projectedPoint ) const
1001{
1003
1004 const QgsTriangularMesh *mesh = triangularMesh();
1005 int selectedIndex = -1;
1006 projectedPoint = QgsPointXY();
1007 if ( !mesh )
1008 return selectedIndex;
1009
1010 const QgsRectangle rectangle( point.x() - searchRadius, point.y() - searchRadius, point.x() + searchRadius, point.y() + searchRadius );
1011 double maxDistance = std::numeric_limits<double>::max();
1012
1013 const QList<int> faceIndexes = mesh->faceIndexesForRectangle( rectangle );
1014 for ( const int faceIndex : faceIndexes )
1015 {
1016 const int nativefaceIndex = mesh->trianglesToNativeFaces().at( faceIndex );
1017 if ( nativefaceIndex < 0 && nativefaceIndex >= mesh->faceCentroids().count() )
1018 continue;
1019 const QgsPointXY centroid = mesh->faceCentroids()[nativefaceIndex];
1020 const double dist = point.distance( centroid );
1021 if ( dist < maxDistance )
1022 {
1023 maxDistance = dist;
1024 projectedPoint = centroid;
1025 selectedIndex = nativefaceIndex;
1026 }
1027 }
1028
1029 return selectedIndex;
1030}
1031
1033{
1035
1036 mDatasetGroupStore->resetDatasetGroupTreeItem();
1037 updateActiveDatasetGroups();
1038}
1039
1041{
1043
1044 if ( !mDataProvider )
1045 return QgsInterval();
1046 const int groupCount = mDataProvider->datasetGroupCount();
1047 for ( int i = 0; i < groupCount; ++i )
1048 {
1049 const qint64 timeStep = mDataProvider->temporalCapabilities()->firstTimeStepDuration( i );
1050 if ( timeStep > 0 )
1052 }
1053
1054 return QgsInterval();
1055}
1056
1058{
1060
1061 const qint64 time = mDatasetGroupStore->datasetRelativeTime( index );
1062
1063 if ( time == INVALID_MESHLAYER_TIME )
1064 return QgsInterval();
1065 else
1067}
1068
1070{
1072
1073 return mDatasetGroupStore->datasetRelativeTime( index );
1074}
1075
1076static QString detailsErrorMessage( const QgsMeshEditingError &error )
1077{
1078 QString message;
1079
1080 switch ( error.errorType )
1081 {
1083 break;
1085 message = QObject::tr( "Face %1 invalid" ).arg( error.elementIndex );
1086 break;
1088 message = QObject::tr( "Too many vertices for face %1" ).arg( error.elementIndex );
1089 break;
1091 message = QObject::tr( "Face %1 is flat" ).arg( error.elementIndex );
1092 break;
1094 message = QObject::tr( "Vertex %1 is a unique shared vertex" ).arg( error.elementIndex );
1095 break;
1097 message = QObject::tr( "Vertex %1 is invalid" ).arg( error.elementIndex );
1098 break;
1100 message = QObject::tr( "Face %1 is manifold" ).arg( error.elementIndex );
1101 break;
1102 }
1103
1104 return message;
1105}
1106
1108{
1110
1112 return startFrameEditing( transform, error, false );
1113}
1114
1116{
1118
1119 if ( !supportsEditing() )
1120 {
1121 QgsMessageLog::logMessage( QObject::tr( "Mesh layer \"%1\" not support mesh editing" ).arg( name() ) );
1122 return false;
1123 }
1124
1125 if ( mMeshEditor )
1126 {
1127 QgsMessageLog::logMessage( QObject::tr( "Mesh layer \"%1\" already in editing mode" ).arg( name() ) );
1128 return false;
1129 }
1130
1131 mSimplificationSettings.setEnabled( false );
1132
1133 updateTriangularMesh( transform );
1134
1135 mMeshEditor = new QgsMeshEditor( this );
1136
1137 if ( fixErrors )
1138 {
1139 mRendererCache.reset(); // fixing errors could lead to remove faces/vertices
1140 error = mMeshEditor->initializeWithErrorsFix();
1141 }
1142 else
1143 error = mMeshEditor->initialize();
1144
1145 if ( error.errorType != Qgis::MeshEditingErrorType::NoError )
1146 {
1147 mMeshEditor->deleteLater();
1148 mMeshEditor = nullptr;
1149
1150 QgsMessageLog::logMessage( QObject::tr( "Unable to start editing of mesh layer \"%1\": %2" ).arg( name(), detailsErrorMessage( error ) ), QString(), Qgis::MessageLevel::Critical );
1151 return false;
1152 }
1153
1154 // During editing, we don't need anymore the provider data. Mesh frame data is stored in the mesh editor.
1155 mDataProvider->close();
1156
1157 // All dataset group are removed and replace by a unique virtual dataset group that provide vertices elevation value.
1158 mExtraDatasetUri.clear();
1159 mDatasetGroupStore = std::make_unique<QgsMeshDatasetGroupStore>( this );
1160
1161 mDatasetGroupStore->addDatasetGroup( mMeshEditor->createZValueDatasetGroup() );
1162
1164
1165 connect( mMeshEditor, &QgsMeshEditor::meshEdited, this, &QgsMeshLayer::onMeshEdited );
1166
1167 emit dataChanged();
1168 emit editingStarted();
1169
1170 return true;
1171}
1172
1173bool QgsMeshLayer::commitFrameEditing( const QgsCoordinateTransform &transform, bool continueEditing )
1174{
1176
1178 QString detailsError;
1179 if ( !mMeshEditor->checkConsistency( error ) )
1180 {
1181 if ( error.errorType == Qgis::MeshEditingErrorType::NoError )
1182 detailsError = tr( "Unknown inconsistent mesh error" );
1183 }
1184 else
1185 {
1186 error = QgsTopologicalMesh::checkTopology( *mNativeMesh, mMeshEditor->maximumVerticesPerFace() );
1187 detailsError = detailsErrorMessage( error );
1188 }
1189
1190 if ( !detailsError.isEmpty() )
1191 {
1192 QgsMessageLog::logMessage( QObject::tr( "Edited mesh layer \"%1\" can't be save due to an error: %2" ).arg( name(), detailsError ), QString(), Qgis::MessageLevel::Critical );
1193 return false;
1194 }
1195
1196 stopFrameEditing( transform );
1197
1198 if ( !mDataProvider )
1199 return false;
1200
1201 const bool res = mDataProvider->saveMeshFrame( *mNativeMesh.get() );
1202
1203 if ( continueEditing )
1204 {
1205 mMeshEditor->initialize();
1206 emit layerModified();
1207 return res;
1208 }
1209
1210 mMeshEditor->deleteLater();
1211 mMeshEditor = nullptr;
1212 emit editingStopped();
1213
1214 mDataProvider->reloadData();
1215 mDataProvider->populateMesh( mNativeMesh.get() );
1216 mDatasetGroupStore = std::make_unique<QgsMeshDatasetGroupStore>( this );
1217 mDatasetGroupStore->setPersistentProvider( mDataProvider, QStringList() );
1219 return true;
1220}
1221
1222bool QgsMeshLayer::rollBackFrameEditing( const QgsCoordinateTransform &transform, bool continueEditing )
1223{
1225
1226 stopFrameEditing( transform );
1227
1228 if ( !mDataProvider )
1229 return false;
1230
1231 mTriangularMeshes.clear();
1232 mDataProvider->reloadData();
1233 mDataProvider->populateMesh( mNativeMesh.get() );
1234 updateTriangularMesh( transform );
1235 mRendererCache = std::make_unique<QgsMeshLayerRendererCache>();
1237
1238 if ( continueEditing )
1239 {
1240 mMeshEditor->resetTriangularMesh( triangularMesh() );
1241 return mMeshEditor->initialize() == QgsMeshEditingError();
1242 }
1243 else
1244 {
1245 mMeshEditor->deleteLater();
1246 mMeshEditor = nullptr;
1247 emit editingStopped();
1248
1249 mDatasetGroupStore = std::make_unique<QgsMeshDatasetGroupStore>( this );
1250 mDatasetGroupStore->setPersistentProvider( mDataProvider, QStringList() );
1252 emit dataChanged();
1253 return true;
1254 }
1255}
1256
1258{
1260
1261 if ( !mMeshEditor )
1262 return;
1263
1264 mMeshEditor->stopEditing();
1265 mTriangularMeshes.at( 0 )->update( mNativeMesh.get(), transform );
1266 mRendererCache = std::make_unique<QgsMeshLayerRendererCache>();
1267}
1268
1269bool QgsMeshLayer::reindex( const QgsCoordinateTransform &transform, bool renumber )
1270{
1272
1273 if ( !mMeshEditor )
1274 return false;
1275
1276 if ( !mMeshEditor->reindex( renumber ) )
1277 return false;
1278
1279 mTriangularMeshes.clear();
1280 mTriangularMeshes.emplace_back( new QgsTriangularMesh );
1281 mTriangularMeshes.at( 0 )->update( mNativeMesh.get(), transform );
1282 mRendererCache = std::make_unique<QgsMeshLayerRendererCache>();
1283 mMeshEditor->resetTriangularMesh( mTriangularMeshes.at( 0 ).get() );
1284
1285 return true;
1286}
1287
1289{
1291
1292 return mMeshEditor;
1293}
1294
1296{
1298
1299 if ( mMeshEditor )
1300 return mMeshEditor->isModified();
1301
1302 return false;
1303}
1304
1306{
1308
1309 switch ( type )
1310 {
1312 return meshVertexCount() != 0;
1314 return meshEdgeCount() != 0;
1316 return meshFaceCount() != 0;
1317 }
1318 return false;
1319}
1320
1322{
1324
1325 if ( mMeshEditor )
1326 return mMeshEditor->validVerticesCount();
1327 else if ( mDataProvider )
1328 return mDataProvider->vertexCount();
1329 else
1330 return 0;
1331}
1332
1334{
1336
1337 if ( mMeshEditor )
1338 return mMeshEditor->validFacesCount();
1339 else if ( mDataProvider )
1340 return mDataProvider->faceCount();
1341 else
1342 return 0;
1343}
1344
1346{
1348
1349 if ( mMeshEditor )
1350 return mNativeMesh->edgeCount();
1351 else if ( mDataProvider )
1352 return mDataProvider->edgeCount();
1353 else
1354 return 0;
1355}
1356
1357void QgsMeshLayer::updateActiveDatasetGroups()
1358{
1360
1361 QgsMeshDatasetGroupTreeItem *treeItem = mDatasetGroupStore->datasetGroupTreeItem();
1362
1363 if ( !mDatasetGroupStore->datasetGroupTreeItem() )
1364 return;
1365
1367 const int oldActiveScalar = settings.activeScalarDatasetGroup();
1368 const int oldActiveVector = settings.activeVectorDatasetGroup();
1369
1370 QgsMeshDatasetGroupTreeItem *activeScalarItem = treeItem->childFromDatasetGroupIndex( oldActiveScalar );
1371
1372 if ( !activeScalarItem && treeItem->childCount() > 0 && oldActiveScalar != -1 )
1373 activeScalarItem = treeItem->child( 0 );
1374
1375 if ( activeScalarItem && !activeScalarItem->isEnabled() )
1376 {
1377 for ( int i = 0; i < treeItem->childCount(); ++i )
1378 {
1379 activeScalarItem = treeItem->child( i );
1380 if ( activeScalarItem->isEnabled() )
1381 break;
1382 else
1383 activeScalarItem = nullptr;
1384 }
1385 }
1386
1387 if ( activeScalarItem )
1388 settings.setActiveScalarDatasetGroup( activeScalarItem->datasetGroupIndex() );
1389 else
1390 settings.setActiveScalarDatasetGroup( -1 );
1391
1392 QgsMeshDatasetGroupTreeItem *activeVectorItem = treeItem->childFromDatasetGroupIndex( oldActiveVector );
1393
1394 if ( !( activeVectorItem && activeVectorItem->isEnabled() ) )
1395 settings.setActiveVectorDatasetGroup( -1 );
1396
1397 setRendererSettings( settings );
1398
1399 if ( oldActiveScalar != settings.activeScalarDatasetGroup() )
1401 if ( oldActiveVector != settings.activeVectorDatasetGroup() )
1403}
1404
1405QgsMeshRendererSettings QgsMeshLayer::accordSymbologyWithGroupName( const QgsMeshRendererSettings &settings, const QMap<QString, int> &nameToIndex )
1406{
1407 QString activeScalarName;
1408 QString activeVectorName;
1409 QgsMeshRendererSettings consistentSettings = settings;
1410 int activeScalar = consistentSettings.activeScalarDatasetGroup();
1411 int activeVector = consistentSettings.activeVectorDatasetGroup();
1412
1413 for ( auto it = nameToIndex.constBegin(); it != nameToIndex.constEnd(); ++it )
1414 {
1415 int index = it.value();
1416 const QString name = it.key();
1417 int globalIndex = mDatasetGroupStore->indexFromGroupName( name );
1418 if ( globalIndex >= 0 )
1419 {
1420 QgsMeshRendererScalarSettings scalarSettings = settings.scalarSettings( index );
1421 consistentSettings.setScalarSettings( globalIndex, scalarSettings );
1422 if ( settings.hasVectorSettings( it.value() ) && mDatasetGroupStore->datasetGroupMetadata( globalIndex ).isVector() )
1423 {
1424 QgsMeshRendererVectorSettings vectorSettings = settings.vectorSettings( index );
1425 consistentSettings.setVectorSettings( globalIndex, vectorSettings );
1426 }
1427 }
1428 else
1429 {
1430 consistentSettings.removeScalarSettings( index );
1431 if ( settings.hasVectorSettings( it.value() ) )
1432 consistentSettings.removeVectorSettings( index );
1433 }
1434
1435 if ( index == activeScalar )
1436 activeScalarName = name;
1437 if ( index == activeVector )
1438 activeVectorName = name;
1439 }
1440
1441 const QList<int> globalIndexes = datasetGroupsIndexes();
1442 for ( int globalIndex : globalIndexes )
1443 {
1444 const QString name = mDatasetGroupStore->groupName( globalIndex );
1445 if ( !nameToIndex.contains( name ) )
1446 {
1447 consistentSettings.setScalarSettings( globalIndex, mRendererSettings.scalarSettings( globalIndex ) );
1448 if ( mDatasetGroupStore->datasetGroupMetadata( globalIndex ).isVector() )
1449 {
1450 consistentSettings.setVectorSettings( globalIndex, mRendererSettings.vectorSettings( globalIndex ) );
1451 }
1452 }
1453 }
1454
1455 if ( !activeScalarName.isEmpty() )
1456 consistentSettings.setActiveScalarDatasetGroup( mDatasetGroupStore->indexFromGroupName( activeScalarName ) );
1457 if ( !activeVectorName.isEmpty() )
1458 consistentSettings.setActiveVectorDatasetGroup( mDatasetGroupStore->indexFromGroupName( activeVectorName ) );
1459
1460 return consistentSettings;
1461}
1462
1463void QgsMeshLayer::setDataSourcePrivate( const QString &dataSource, const QString &baseName, const QString &provider, const QgsDataProvider::ProviderOptions &options, Qgis::DataProviderReadFlags flags )
1464{
1466
1467 mDataSource = dataSource;
1468 mLayerName = baseName;
1469 setProviderType( provider );
1470
1471 if ( !mDataSource.isEmpty() && !provider.isEmpty() )
1472 setDataProvider( provider, options, flags );
1473}
1474
1475QgsPointXY QgsMeshLayer::snapOnElement( QgsMesh::ElementType elementType, const QgsPointXY &point, double searchRadius )
1476{
1478
1479 QgsPointXY projectedPoint;
1480 closestElement( elementType, point, searchRadius, projectedPoint );
1481 return projectedPoint;
1482}
1483
1484int QgsMeshLayer::closestElement( QgsMesh::ElementType elementType, const QgsPointXY &point, double searchRadius, QgsPointXY &projectedPoint ) const
1485{
1487
1488 switch ( elementType )
1489 {
1490 case QgsMesh::Vertex:
1491 return closestVertex( point, searchRadius, projectedPoint );
1492 case QgsMesh::Edge:
1493 return closestEdge( point, searchRadius, projectedPoint );
1494 case QgsMesh::Face:
1495 return closestFace( point, searchRadius, projectedPoint );
1496 }
1497 return -1;
1498}
1499
1501{
1503
1504 if ( !mNativeMesh )
1505 {
1506 // lazy loading of mesh data
1507 fillNativeMesh();
1508 }
1509
1510 QList<int> ret;
1511
1512 if ( !mNativeMesh )
1513 return ret;
1514
1515 QgsExpressionContext context;
1516 std::unique_ptr<QgsExpressionContextScope> expScope( QgsExpressionContextUtils::meshExpressionScope( QgsMesh::Vertex ) );
1517 context.appendScope( expScope.release() );
1518 context.lastScope()->setVariable( u"_native_mesh"_s, QVariant::fromValue( *mNativeMesh ) );
1519
1520 expression.prepare( &context );
1521
1522 for ( int i = 0; i < mNativeMesh->vertexCount(); ++i )
1523 {
1524 context.lastScope()->setVariable( u"_mesh_vertex_index"_s, i, false );
1525
1526 if ( expression.evaluate( &context ).toBool() )
1527 ret.append( i );
1528 }
1529
1530 return ret;
1531}
1532
1534{
1536
1537 if ( !mNativeMesh )
1538 {
1539 // lazy loading of mesh data
1540 fillNativeMesh();
1541 }
1542
1543 QList<int> ret;
1544
1545 if ( !mNativeMesh )
1546 return ret;
1547
1548 QgsExpressionContext context;
1549 std::unique_ptr<QgsExpressionContextScope> expScope( QgsExpressionContextUtils::meshExpressionScope( QgsMesh::Face ) );
1550 context.appendScope( expScope.release() );
1551 context.lastScope()->setVariable( u"_native_mesh"_s, QVariant::fromValue( *mNativeMesh ) );
1552
1553 expression.prepare( &context );
1554
1555 for ( int i = 0; i < mNativeMesh->faceCount(); ++i )
1556 {
1557 context.lastScope()->setVariable( u"_mesh_face_index"_s, i, false );
1558
1559 if ( expression.evaluate( &context ).toBool() )
1560 ret.append( i );
1561 }
1562
1563 return ret;
1564}
1565
1567{
1569
1570 return QgsMeshDatasetIndex( group >= 0 ? group : mRendererSettings.activeScalarDatasetGroup(), mStaticScalarDatasetIndex );
1571}
1572
1574{
1576
1577 const int oldActiveVector = mRendererSettings.activeVectorDatasetGroup();
1578
1579 mStaticVectorDatasetIndex = staticVectorDatasetIndex.dataset();
1580 mRendererSettings.setActiveVectorDatasetGroup( staticVectorDatasetIndex.group() );
1581
1582 if ( oldActiveVector != mRendererSettings.activeVectorDatasetGroup() )
1583 emit activeVectorDatasetGroupChanged( mRendererSettings.activeVectorDatasetGroup() );
1584}
1585
1587{
1589
1590 const int oldActiveScalar = mRendererSettings.activeScalarDatasetGroup();
1591
1592 mStaticScalarDatasetIndex = staticScalarDatasetIndex.dataset();
1593 mRendererSettings.setActiveScalarDatasetGroup( staticScalarDatasetIndex.group() );
1594
1595 if ( oldActiveScalar != mRendererSettings.activeScalarDatasetGroup() )
1596 emit activeScalarDatasetGroupChanged( mRendererSettings.activeScalarDatasetGroup() );
1597}
1598
1605
1607{
1609
1610 mSimplificationSettings = simplifySettings;
1611}
1612
1613static QgsColorRamp *_createDefaultColorRamp()
1614{
1615 QgsColorRamp *ramp = QgsStyle::defaultStyle()->colorRamp( u"Plasma"_s );
1616 if ( ramp )
1617 return ramp;
1618
1619 // definition of "Plasma" color ramp (in case it is not available in the style for some reason)
1620 QVariantMap props;
1621 props["color1"] = "13,8,135,255";
1622 props["color2"] = "240,249,33,255";
1623 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:"
1624 "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:"
1625 "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:"
1626 "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:"
1627 "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:"
1628 "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:"
1629 "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:"
1630 "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:"
1631 "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:"
1632 "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";
1633 return QgsGradientColorRamp::create( props );
1634}
1635
1636void QgsMeshLayer::assignDefaultStyleToDatasetGroup( int groupIndex )
1637{
1639
1640 const QgsMeshDatasetGroupMetadata metadata = datasetGroupMetadata( groupIndex );
1641 const double groupMin = metadata.minimum();
1642 const double groupMax = metadata.maximum();
1643
1644 QgsColorRampShader fcn( groupMin, groupMax, _createDefaultColorRamp() );
1645 fcn.classifyColorRamp( 5, -1, QgsRectangle(), nullptr );
1646
1647 QgsMeshRendererScalarSettings scalarSettings;
1648 scalarSettings.setClassificationMinimumMaximum( groupMin, groupMax );
1649 scalarSettings.setColorRampShader( fcn );
1650 QgsInterpolatedLineWidth edgeStrokeWidth;
1651 edgeStrokeWidth.setMinimumValue( groupMin );
1652 edgeStrokeWidth.setMaximumValue( groupMax );
1653 const QgsInterpolatedLineColor edgeStrokeColor( fcn );
1654 const QgsInterpolatedLineRenderer edgeStrokePen;
1655 scalarSettings.setEdgeStrokeWidth( edgeStrokeWidth );
1656 mRendererSettings.setScalarSettings( groupIndex, scalarSettings );
1657
1658 if ( metadata.isVector() )
1659 {
1660 QgsMeshRendererVectorSettings vectorSettings;
1661 vectorSettings.setColorRampShader( fcn );
1662 mRendererSettings.setVectorSettings( groupIndex, vectorSettings );
1663 }
1664}
1665
1667{
1669
1670 // Triangular mesh
1671 updateTriangularMesh( rendererContext.coordinateTransform() );
1672
1673 // Build overview triangular meshes if needed
1674 createSimplifiedMeshes();
1675
1676 // Cache
1677 if ( !mRendererCache )
1678 mRendererCache = std::make_unique<QgsMeshLayerRendererCache>();
1679
1680 return new QgsMeshLayerRenderer( this, rendererContext );
1681}
1682
1684{
1685 if ( rendererContext.isTemporal() )
1686 return activeScalarDatasetAtTime( rendererContext.temporalRange(), mRendererSettings.activeScalarDatasetGroup() );
1687 else
1688 return staticScalarDatasetIndex( mRendererSettings.activeScalarDatasetGroup() );
1689}
1690
1691bool QgsMeshLayer::minimumMaximumActiveScalarDataset( const QgsRectangle &extent, const QgsMeshDatasetIndex &datasetIndex, double &min, double &max )
1692{
1693 if ( extent.isNull() || !this->extent().intersects( extent ) )
1694 return false;
1695
1697
1698 if ( !tMesh )
1699 {
1700 return false;
1701 }
1702
1703 QVector<double> scalarDatasetValues;
1705
1706 if ( !metadata.isScalar() )
1707 {
1708 return false;
1709 }
1710
1711 QgsMeshDatasetGroupMetadata::DataType scalarDataType = QgsMeshLayerUtils::datasetValuesType( metadata.dataType() );
1712
1713 if ( !datasetIndex.isValid() )
1714 {
1715 return false;
1716 }
1717
1718 // populate scalar values
1719 const int count = QgsMeshLayerUtils::datasetValuesCount( mNativeMesh.get(), scalarDataType );
1720 const QgsMeshDataBlock vals = QgsMeshLayerUtils::datasetValues( this, datasetIndex, 0, count );
1721
1722 if ( vals.isValid() )
1723 {
1724 // vals could be scalar or vectors, for contour rendering we want always magnitude
1725 scalarDatasetValues = QgsMeshLayerUtils::calculateMagnitudes( vals );
1726 }
1727 else
1728 {
1729 scalarDatasetValues = QVector<double>( count, std::numeric_limits<double>::quiet_NaN() );
1730 }
1731
1732 QList<int> intersectedFacesIndices = tMesh->faceIndexesForRectangle( extent );
1733
1734 if ( intersectedFacesIndices.isEmpty() )
1735 {
1736 return false;
1737 }
1738
1739 min = std::numeric_limits<double>::max();
1740 max = -std::numeric_limits<double>::max();
1741
1742 double value;
1743
1744 for ( int intersectedFaceIndex : intersectedFacesIndices )
1745 {
1746 QgsMeshFace face = tMesh->triangles().at( intersectedFaceIndex );
1747
1749 {
1750 value = scalarDatasetValues.at( tMesh->trianglesToNativeFaces().at( intersectedFaceIndex ) );
1751 min = std::min( min, value );
1752 max = std::max( max, value );
1753 }
1755 {
1756 QgsMeshVertex vertex;
1757
1758 for ( int vertexIndex : face )
1759 {
1760 value = scalarDatasetValues.at( vertexIndex );
1761 min = std::min( min, value );
1762 max = std::max( max, value );
1763 }
1764 }
1765 }
1766
1767 return true;
1768}
1769
1776
1777void QgsMeshLayer::checkSymbologyConsistency()
1778{
1780
1781 const QList<int> groupIndexes = mDatasetGroupStore->datasetGroupIndexes();
1782 if ( !groupIndexes.contains( mRendererSettings.activeScalarDatasetGroup() ) && mRendererSettings.activeScalarDatasetGroup() != -1 )
1783 {
1784 if ( !groupIndexes.empty() )
1785 mRendererSettings.setActiveScalarDatasetGroup( groupIndexes.first() );
1786 else
1787 mRendererSettings.setActiveScalarDatasetGroup( -1 );
1788 }
1789
1790 if ( !groupIndexes.contains( mRendererSettings.activeVectorDatasetGroup() ) && mRendererSettings.activeVectorDatasetGroup() != -1 )
1791 {
1792 mRendererSettings.setActiveVectorDatasetGroup( -1 );
1793 }
1794}
1795
1796bool QgsMeshLayer::readSymbology( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
1797{
1799
1800 Q_UNUSED( errorMessage )
1801 // TODO: implement categories for raster layer
1802
1803 const QDomElement elem = node.toElement();
1804
1805 readCommonStyle( elem, context, categories );
1806
1808 const QDomElement elemRendererSettings = elem.firstChildElement( "mesh-renderer-settings" );
1809 if ( !elemRendererSettings.isNull() )
1810 rendererSettings.readXml( elemRendererSettings, context );
1811
1812 QMap<QString, int> groupNameToGlobalIndex;
1813 QDomElement nameToIndexElem = elem.firstChildElement( "name-to-global-index" );
1814 while ( !nameToIndexElem.isNull() )
1815 {
1816 const QString name = nameToIndexElem.attribute( u"name"_s );
1817 int globalIndex = nameToIndexElem.attribute( u"global-index"_s ).toInt();
1818 groupNameToGlobalIndex.insert( name, globalIndex );
1819 nameToIndexElem = nameToIndexElem.nextSiblingElement( u"name-to-global-index"_s );
1820 }
1821
1822 mRendererSettings = accordSymbologyWithGroupName( rendererSettings, groupNameToGlobalIndex );
1823
1824 checkSymbologyConsistency();
1825
1826 const QDomElement elemSimplifySettings = elem.firstChildElement( "mesh-simplify-settings" );
1827 if ( !elemSimplifySettings.isNull() )
1828 mSimplificationSettings.readXml( elemSimplifySettings, context );
1829
1830 // get and set the blend mode if it exists
1831 const QDomNode blendModeNode = node.namedItem( u"blendMode"_s );
1832 if ( !blendModeNode.isNull() )
1833 {
1834 const QDomElement e = blendModeNode.toElement();
1835 setBlendMode( QgsPainting::getCompositionMode( static_cast< Qgis::BlendMode >( e.text().toInt() ) ) );
1836 }
1837
1838 // read labeling definition
1839 if ( categories.testFlag( Labeling ) )
1840 {
1841 QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "Labeling" ) );
1842
1843 QDomElement labelingElement = node.firstChildElement( u"labeling"_s );
1844 if ( !labelingElement.isNull() )
1845 {
1847 mLabelsEnabled = node.toElement().attribute( u"labelsEnabled"_s, u"0"_s ).toInt();
1849 }
1850 }
1851
1852 // get and set the layer transparency
1853 if ( categories.testFlag( Rendering ) )
1854 {
1855 const QDomNode layerOpacityNode = node.namedItem( u"layerOpacity"_s );
1856 if ( !layerOpacityNode.isNull() )
1857 {
1858 const QDomElement e = layerOpacityNode.toElement();
1859 setOpacity( e.text().toDouble() );
1860 }
1861 }
1862
1863 if ( categories.testFlag( Legend ) )
1864 {
1865 QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "Legend" ) );
1866
1867 const QDomElement legendElem = node.firstChildElement( u"legend"_s );
1868 if ( QgsMapLayerLegend *l = legend(); l && !legendElem.isNull() )
1869 {
1870 l->readXml( legendElem, context );
1871 }
1872 }
1873
1874 return true;
1875}
1876
1877bool QgsMeshLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
1878{
1880
1881 Q_UNUSED( errorMessage )
1882 // TODO: implement categories for raster layer
1883
1884 QDomElement elem = node.toElement();
1885
1886 writeCommonStyle( elem, doc, context, categories );
1887
1888 const QDomElement elemRendererSettings = mRendererSettings.writeXml( doc, context );
1889 elem.appendChild( elemRendererSettings );
1890
1891 const QList<int> groupIndexes = datasetGroupsIndexes();
1892 // we store the relation between name and indexes to be able to retrieve the consistency between name and symbology
1893 for ( int index : groupIndexes )
1894 {
1895 QDomElement elemNameToIndex = doc.createElement( u"name-to-global-index"_s );
1896 elemNameToIndex.setAttribute( u"name"_s, mDatasetGroupStore->groupName( index ) );
1897 elemNameToIndex.setAttribute( u"global-index"_s, index );
1898 elem.appendChild( elemNameToIndex );
1899 }
1900
1901 const QDomElement elemSimplifySettings = mSimplificationSettings.writeXml( doc, context );
1902 elem.appendChild( elemSimplifySettings );
1903
1904 // add blend mode node
1905 QDomElement blendModeElement = doc.createElement( u"blendMode"_s );
1906 const QDomText blendModeText = doc.createTextNode( QString::number( static_cast< int >( QgsPainting::getBlendModeEnum( blendMode() ) ) ) );
1907 blendModeElement.appendChild( blendModeText );
1908 node.appendChild( blendModeElement );
1909
1910 if ( categories.testFlag( Labeling ) )
1911 {
1912 if ( mLabeling )
1913 {
1914 QDomElement labelingElement = mLabeling->save( doc, context );
1915 elem.appendChild( labelingElement );
1916 }
1917 elem.setAttribute( u"labelsEnabled"_s, mLabelsEnabled ? u"1"_s : u"0"_s );
1918 }
1919
1920 // add the layer opacity
1921 if ( categories.testFlag( Rendering ) )
1922 {
1923 QDomElement layerOpacityElem = doc.createElement( u"layerOpacity"_s );
1924 const QDomText layerOpacityText = doc.createTextNode( QString::number( opacity() ) );
1925 layerOpacityElem.appendChild( layerOpacityText );
1926 node.appendChild( layerOpacityElem );
1927 }
1928
1929 if ( categories.testFlag( Legend ) && legend() )
1930 {
1931 QDomElement legendElement = legend()->writeXml( doc, context );
1932 if ( !legendElement.isNull() )
1933 node.appendChild( legendElement );
1934 }
1935
1936 return true;
1937}
1938
1939bool QgsMeshLayer::writeStyle( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
1940{
1942
1943 return writeSymbology( node, doc, errorMessage, context, categories );
1944}
1945
1946bool QgsMeshLayer::readStyle( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
1947{
1949
1950 return readSymbology( node, errorMessage, context, categories );
1951}
1952
1953QString QgsMeshLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
1954{
1956
1957 return QgsProviderRegistry::instance()->relativeToAbsoluteUri( provider, source, context );
1958}
1959
1960QString QgsMeshLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
1961{
1963
1965}
1966
1967bool QgsMeshLayer::readXml( const QDomNode &layer_node, QgsReadWriteContext &context )
1968{
1970
1971 QgsDebugMsgLevel( u"Datasource in QgsMeshLayer::readXml: %1"_s.arg( mDataSource.toLocal8Bit().data() ), 3 );
1972
1973 //process provider key
1974 const QDomNode pkeyNode = layer_node.namedItem( u"provider"_s );
1975
1976 if ( pkeyNode.isNull() )
1977 {
1978 mProviderKey.clear();
1979 }
1980 else
1981 {
1982 const QDomElement pkeyElt = pkeyNode.toElement();
1983 mProviderKey = pkeyElt.text();
1984 }
1985
1987 {
1988 return false;
1989 }
1990
1991 const QgsDataProvider::ProviderOptions providerOptions;
1993
1994 const QDomElement elemExtraDatasets = layer_node.firstChildElement( u"extra-datasets"_s );
1995 if ( !elemExtraDatasets.isNull() )
1996 {
1997 QDomElement elemUri = elemExtraDatasets.firstChildElement( u"uri"_s );
1998 while ( !elemUri.isNull() )
1999 {
2000 const QString uri = context.pathResolver().readPath( elemUri.text() );
2001 mExtraDatasetUri.append( uri );
2002 elemUri = elemUri.nextSiblingElement( u"uri"_s );
2003 }
2004 }
2005
2006 if ( pkeyNode.toElement().hasAttribute( u"time-unit"_s ) )
2007 mTemporalUnit = static_cast<Qgis::TemporalUnit>( pkeyNode.toElement().attribute( u"time-unit"_s ).toInt() );
2008
2009 // read dataset group store
2010 const QDomElement elemDatasetGroupsStore = layer_node.firstChildElement( u"mesh-dataset-groups-store"_s );
2011 if ( elemDatasetGroupsStore.isNull() )
2013 else
2014 mDatasetGroupStore->readXml( elemDatasetGroupsStore, context );
2015
2016 setDataProvider( mProviderKey, providerOptions, flags );
2017
2018 QString errorMsg;
2019 readSymbology( layer_node, errorMsg, context );
2020
2021 if ( !mTemporalProperties->timeExtent().begin().isValid() || mTemporalProperties->alwaysLoadReferenceTimeFromSource() )
2023
2024 // read static dataset
2025 const QDomElement elemStaticDataset = layer_node.firstChildElement( u"static-active-dataset"_s );
2026 if ( elemStaticDataset.hasAttribute( u"scalar"_s ) )
2027 {
2028 mStaticScalarDatasetIndex = elemStaticDataset.attribute( u"scalar"_s ).toInt();
2029 }
2030 if ( elemStaticDataset.hasAttribute( u"vector"_s ) )
2031 {
2032 mStaticVectorDatasetIndex = elemStaticDataset.attribute( u"vector"_s ).toInt();
2033 }
2034
2035 return isValid(); // should be true if read successfully
2036}
2037
2038bool QgsMeshLayer::writeXml( QDomNode &layer_node, QDomDocument &document, const QgsReadWriteContext &context ) const
2039{
2041
2042 // first get the layer element so that we can append the type attribute
2043 QDomElement mapLayerNode = layer_node.toElement();
2044
2045 if ( mapLayerNode.isNull() || ( "maplayer"_L1 != mapLayerNode.nodeName() ) )
2046 {
2047 QgsDebugMsgLevel( u"can't find <maplayer>"_s, 2 );
2048 return false;
2049 }
2050
2051 mapLayerNode.setAttribute( u"type"_s, QgsMapLayerFactory::typeToString( Qgis::LayerType::Mesh ) );
2052
2053 // add provider node
2054 if ( mDataProvider )
2055 {
2056 QDomElement provider = document.createElement( u"provider"_s );
2057 const QDomText providerText = document.createTextNode( providerType() );
2058 provider.appendChild( providerText );
2059 layer_node.appendChild( provider );
2060 provider.setAttribute( u"time-unit"_s, static_cast< int >( mDataProvider->temporalCapabilities()->temporalUnit() ) );
2061
2062 const QStringList extraDatasetUris = mDataProvider->extraDatasets();
2063 QDomElement elemExtraDatasets = document.createElement( u"extra-datasets"_s );
2064 for ( const QString &uri : extraDatasetUris )
2065 {
2066 const QString path = context.pathResolver().writePath( uri );
2067 QDomElement elemUri = document.createElement( u"uri"_s );
2068 elemUri.appendChild( document.createTextNode( path ) );
2069 elemExtraDatasets.appendChild( elemUri );
2070 }
2071 layer_node.appendChild( elemExtraDatasets );
2072 }
2073
2074 QDomElement elemStaticDataset = document.createElement( u"static-active-dataset"_s );
2075 elemStaticDataset.setAttribute( u"scalar"_s, mStaticScalarDatasetIndex );
2076 elemStaticDataset.setAttribute( u"vector"_s, mStaticVectorDatasetIndex );
2077 layer_node.appendChild( elemStaticDataset );
2078
2079 // write dataset group store if not in edting mode
2080 if ( !isEditable() )
2081 layer_node.appendChild( mDatasetGroupStore->writeXml( document, context ) );
2082
2083 // renderer specific settings
2084 QString errorMsg;
2085 return writeSymbology( layer_node, document, errorMsg, context );
2086}
2087
2089{
2091
2092 if ( !mMeshEditor && mDataProvider && mDataProvider->isValid() )
2093 {
2094 mDataProvider->reloadData();
2095 mDatasetGroupStore->setPersistentProvider( mDataProvider, QStringList() ); //extra dataset are already loaded
2096
2097 //reload the mesh structure
2098 if ( !mNativeMesh )
2099 mNativeMesh = std::make_unique<QgsMesh>();
2100
2101 dataProvider()->populateMesh( mNativeMesh.get() );
2102
2103 if ( mTemporalProperties->alwaysLoadReferenceTimeFromSource() )
2104 mTemporalProperties->setDefaultsFromDataProviderTemporalCapabilities( mDataProvider->temporalCapabilities() );
2105
2106 //clear the TriangularMeshes
2107 mTriangularMeshes.clear();
2108
2109 //clear the rendererCache
2110 mRendererCache = std::make_unique<QgsMeshLayerRendererCache>();
2111
2112 checkSymbologyConsistency();
2113
2114 emit reloaded();
2115 }
2116}
2117
2118QStringList QgsMeshLayer::subLayers() const
2119{
2121
2122 if ( mDataProvider )
2123 return mDataProvider->subLayers();
2124 else
2125 return QStringList();
2126}
2127
2129{
2131
2132 const QgsLayerMetadataFormatter htmlFormatter( metadata() );
2133 QString myMetadata = u"<html>\n<body>\n"_s;
2134
2135 myMetadata += generalHtmlMetadata();
2136
2137 // Begin Provider section
2138 myMetadata += u"<h1>"_s + tr( "Information from provider" ) + u"</h1>\n<hr>\n"_s;
2139 myMetadata += "<table class=\"list-view\">\n"_L1;
2140
2141 // Extent
2142 myMetadata += u"<tr><td class=\"highlight\">"_s + tr( "Extent" ) + u"</td><td>"_s + extent().toString() + u"</td></tr>\n"_s;
2143
2144 // feature count
2145 QLocale locale = QLocale();
2146 locale.setNumberOptions( locale.numberOptions() &= ~QLocale::NumberOption::OmitGroupSeparator );
2147
2148 if ( const QgsMeshDataProvider *provider = dataProvider() )
2149 {
2150 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;
2151 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;
2152 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;
2153 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;
2154 myMetadata += provider->htmlMetadata();
2155 }
2156
2157 // End Provider section
2158 myMetadata += "</table>\n<br><br>"_L1;
2159
2160 // CRS
2161 myMetadata += crsHtmlMetadata();
2162
2163 // identification section
2164 myMetadata += u"<h1>"_s + tr( "Identification" ) + u"</h1>\n<hr>\n"_s;
2165 myMetadata += htmlFormatter.identificationSectionHtml();
2166 myMetadata += "<br><br>\n"_L1;
2167
2168 // extent section
2169 myMetadata += u"<h1>"_s + tr( "Extent" ) + u"</h1>\n<hr>\n"_s;
2170 myMetadata += htmlFormatter.extentSectionHtml( isSpatial() );
2171 myMetadata += "<br><br>\n"_L1;
2172
2173 // Start the Access section
2174 myMetadata += u"<h1>"_s + tr( "Access" ) + u"</h1>\n<hr>\n"_s;
2175 myMetadata += htmlFormatter.accessSectionHtml();
2176 myMetadata += "<br><br>\n"_L1;
2177
2178 // Start the contacts section
2179 myMetadata += u"<h1>"_s + tr( "Contacts" ) + u"</h1>\n<hr>\n"_s;
2180 myMetadata += htmlFormatter.contactsSectionHtml();
2181 myMetadata += "<br><br>\n"_L1;
2182
2183 // Start the links section
2184 myMetadata += u"<h1>"_s + tr( "Links" ) + u"</h1>\n<hr>\n"_s;
2185 myMetadata += htmlFormatter.linksSectionHtml();
2186 myMetadata += "<br><br>\n"_L1;
2187
2188 // Start the history section
2189 myMetadata += u"<h1>"_s + tr( "History" ) + u"</h1>\n<hr>\n"_s;
2190 myMetadata += htmlFormatter.historySectionHtml();
2191 myMetadata += "<br><br>\n"_L1;
2192
2193 myMetadata += customPropertyHtmlMetadata();
2194
2195 myMetadata += "\n</body>\n</html>\n"_L1;
2196 return myMetadata;
2197}
2198
2200{
2202
2203 return mMeshEditor;
2204}
2205
2206bool QgsMeshLayer::setDataProvider( QString const &provider, const QgsDataProvider::ProviderOptions &options, Qgis::DataProviderReadFlags flags )
2207{
2209
2210 mDatasetGroupStore->setPersistentProvider( nullptr, QStringList() );
2211
2212 delete mDataProvider;
2213 mProviderKey = provider;
2214 const QString dataSource = mDataSource;
2215
2216 if ( mPreloadedProvider )
2217 {
2218 mDataProvider = qobject_cast< QgsMeshDataProvider * >( mPreloadedProvider.release() );
2219 }
2220 else
2221 {
2222 std::unique_ptr< QgsScopedRuntimeProfile > profile;
2223 if ( QgsApplication::profiler()->groupIsActive( u"projectload"_s ) )
2224 profile = std::make_unique< QgsScopedRuntimeProfile >( tr( "Create %1 provider" ).arg( provider ), u"projectload"_s );
2225
2226 mDataProvider = qobject_cast<QgsMeshDataProvider *>( QgsProviderRegistry::instance()->createProvider( provider, dataSource, options, flags ) );
2227 }
2228
2229 if ( !mDataProvider )
2230 {
2231 QgsDebugMsgLevel( u"Unable to get mesh data provider"_s, 2 );
2232 return false;
2233 }
2234
2235 mDataProvider->setParent( this );
2236 QgsDebugMsgLevel( u"Instantiated the mesh data provider plugin"_s, 2 );
2237
2238 setValid( mDataProvider->isValid() );
2239 if ( !isValid() )
2240 {
2241 QgsDebugMsgLevel( u"Invalid mesh provider plugin %1"_s.arg( QString( mDataSource.toUtf8() ) ), 2 );
2242 return false;
2243 }
2244
2245 if ( !mTemporalProperties->isValid() )
2246 {
2247 mTemporalProperties->setDefaultsFromDataProviderTemporalCapabilities( dataProvider()->temporalCapabilities() );
2248 }
2249
2250 mDataProvider->setTemporalUnit( mTemporalUnit );
2251
2252 mDatasetGroupStore->setPersistentProvider( mDataProvider, mExtraDatasetUri );
2253
2254 setCrs( mDataProvider->crs() );
2255
2256 if ( provider == "mesh_memory"_L1 )
2257 {
2258 // required so that source differs between memory layers
2259 mDataSource = mDataSource + u"&uid=%1"_s.arg( QUuid::createUuid().toString() );
2260 }
2261
2262 // set default style if required by flags or if the dataset group does not has a style yet
2263 for ( int i = 0; i < mDataProvider->datasetGroupCount(); ++i )
2264 {
2265 int globalIndex = mDatasetGroupStore->globalDatasetGroupIndexInSource( mDataProvider, i );
2266 if ( globalIndex != -1 && ( !mRendererSettings.hasSettings( globalIndex ) || ( flags & Qgis::DataProviderReadFlag::LoadDefaultStyle ) ) )
2267 assignDefaultStyleToDatasetGroup( globalIndex );
2268 }
2269
2270 emit rendererChanged();
2272
2273 connect( mDataProvider, &QgsMeshDataProvider::dataChanged, this, &QgsMeshLayer::dataChanged );
2274
2275 return true;
2276}
2277
2284
2291
2293{
2295
2296 return mLabelsEnabled && static_cast< bool >( mLabeling );
2297}
2298
2300{
2302
2303 mLabelsEnabled = enabled;
2304}
2305
2307{
2309
2310 if ( mLabeling == labeling )
2311 return;
2312
2313 delete mLabeling;
2314 mLabeling = labeling;
2316}
2317
2318bool QgsMeshLayer::datasetsPathUnique( const QString &path )
2319{
2320 if ( !mDataProvider )
2321 {
2322 QgsDebugMsgLevel( u"Unable to get mesh data provider"_s, 2 );
2323 return false;
2324 }
2325
2326 if ( mDataProvider->dataSourceUri().contains( path ) )
2327 return false;
2328
2329 return !mExtraDatasetUri.contains( path );
2330}
2331
2332QString QgsMeshLayer::loadNamedStyle( const QString &uri, bool &resultFlag, QgsMapLayer::StyleCategories categories, Qgis::LoadStyleFlags flags )
2333{
2334 QString result = QgsMapLayer::loadNamedStyle( uri, resultFlag, categories, flags );
2335 emit rendererChanged();
2337 return result;
2338}
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:1548
@ Critical
Critical/error message.
Definition qgis.h:163
@ TooManyVerticesInFace
A face has more vertices than the maximum number supported per face.
Definition qgis.h:1783
@ InvalidFace
An error occurs due to an invalid face (for example, vertex indexes are unordered).
Definition qgis.h:1782
@ UniqueSharedVertex
A least two faces share only one vertices.
Definition qgis.h:1785
@ ManifoldFace
ManifoldFace.
Definition qgis.h:1787
@ InvalidVertex
An error occurs due to an invalid vertex (for example, vertex index is out of range the available ver...
Definition qgis.h:1786
@ FlatFace
A flat face is present.
Definition qgis.h:1784
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition qgis.h:5354
TemporalUnit
Temporal units.
Definition qgis.h:5583
@ Milliseconds
Milliseconds.
Definition qgis.h:5584
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:512
@ EqualInterval
Uses equal interval.
Definition qgis.h:1562
@ 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'...
virtual bool isValid() const =0
Returns true if this is a valid layer.
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.
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.