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