Branch data Line data Source code
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 <cstddef>
19 : : #include <limits>
20 : :
21 : : #include <QUuid>
22 : : #include <QUrl>
23 : :
24 : : #include "qgscolorramp.h"
25 : : #include "qgslogger.h"
26 : : #include "qgsmaplayerlegend.h"
27 : : #include "qgsmaplayerfactory.h"
28 : : #include "qgsmeshdataprovider.h"
29 : : #include "qgsmeshdatasetgroupstore.h"
30 : : #include "qgsmeshlayer.h"
31 : : #include "qgsmeshlayerrenderer.h"
32 : : #include "qgsmeshlayertemporalproperties.h"
33 : : #include "qgsmeshlayerutils.h"
34 : : #include "qgsmeshtimesettings.h"
35 : : #include "qgspainting.h"
36 : : #include "qgsproviderregistry.h"
37 : : #include "qgsreadwritecontext.h"
38 : : #include "qgsstyle.h"
39 : : #include "qgstriangularmesh.h"
40 : : #include "qgsmesh3daveraging.h"
41 : : #include "qgslayermetadataformatter.h"
42 : :
43 : 0 : QgsMeshLayer::QgsMeshLayer( const QString &meshLayerPath,
44 : : const QString &baseName,
45 : : const QString &providerKey,
46 : : const QgsMeshLayer::LayerOptions &options )
47 : 0 : : QgsMapLayer( QgsMapLayerType::MeshLayer, baseName, meshLayerPath ),
48 : 0 : mDatasetGroupStore( new QgsMeshDatasetGroupStore( this ) ),
49 : 0 : mTemporalProperties( new QgsMeshLayerTemporalProperties( this ) )
50 : :
51 : 0 : {
52 : 0 : mShouldValidateCrs = !options.skipCrsValidation;
53 : :
54 : 0 : setProviderType( providerKey );
55 : : // if we’re given a provider type, try to create and bind one to this layer
56 : 0 : bool ok = false;
57 : 0 : if ( !meshLayerPath.isEmpty() && !providerKey.isEmpty() )
58 : : {
59 : 0 : QgsDataProvider::ProviderOptions providerOptions { options.transformContext };
60 : 0 : QgsDataProvider::ReadFlags flags = QgsDataProvider::ReadFlags();
61 : 0 : if ( mReadFlags & QgsMapLayer::FlagTrustLayerMetadata )
62 : : {
63 : 0 : flags |= QgsDataProvider::FlagTrustDataSource;
64 : 0 : }
65 : 0 : ok = setDataProvider( providerKey, providerOptions, flags );
66 : 0 : }
67 : :
68 : 0 : setLegend( QgsMapLayerLegend::defaultMeshLegend( this ) );
69 : 0 : if ( ok )
70 : : {
71 : 0 : setDefaultRendererSettings( mDatasetGroupStore->datasetGroupIndexes() );
72 : :
73 : 0 : if ( mDataProvider )
74 : : {
75 : 0 : mTemporalProperties->setDefaultsFromDataProviderTemporalCapabilities( mDataProvider->temporalCapabilities() );
76 : 0 : resetDatasetGroupTreeItem();
77 : 0 : }
78 : 0 : }
79 : :
80 : 0 : connect( mDatasetGroupStore.get(), &QgsMeshDatasetGroupStore::datasetGroupsAdded, this, &QgsMeshLayer::onDatasetGroupsAdded );
81 : 0 : }
82 : :
83 : :
84 : 0 : void QgsMeshLayer::setDefaultRendererSettings( const QList<int> &groupIndexes )
85 : : {
86 : 0 : QgsMeshRendererMeshSettings meshSettings;
87 : 0 : if ( groupIndexes.count() > 0 )
88 : : {
89 : : // Show data from the first dataset group
90 : 0 : mRendererSettings.setActiveScalarDatasetGroup( 0 );
91 : : // If the first dataset group has nan min/max, display the mesh to avoid nothing displayed
92 : 0 : const QgsMeshDatasetGroupMetadata &meta = datasetGroupMetadata( 0 );
93 : 0 : if ( meta.maximum() == std::numeric_limits<double>::quiet_NaN() &&
94 : 0 : meta.minimum() == std::numeric_limits<double>::quiet_NaN() )
95 : 0 : meshSettings.setEnabled( true );
96 : 0 : }
97 : : else
98 : : {
99 : : // show at least the mesh by default
100 : 0 : meshSettings.setEnabled( true );
101 : 0 : return;
102 : : }
103 : 0 : mRendererSettings.setNativeMeshSettings( meshSettings );
104 : :
105 : : // Sets default resample method for scalar dataset
106 : 0 : for ( int i : groupIndexes )
107 : : {
108 : 0 : QgsMeshDatasetGroupMetadata meta = datasetGroupMetadata( i );
109 : 0 : QgsMeshRendererScalarSettings scalarSettings = mRendererSettings.scalarSettings( i );
110 : 0 : switch ( meta.dataType() )
111 : : {
112 : : case QgsMeshDatasetGroupMetadata::DataOnFaces:
113 : : case QgsMeshDatasetGroupMetadata::DataOnVolumes: // data on volumes are averaged to 2D data on faces
114 : 0 : scalarSettings.setDataResamplingMethod( QgsMeshRendererScalarSettings::NeighbourAverage );
115 : 0 : break;
116 : : case QgsMeshDatasetGroupMetadata::DataOnVertices:
117 : 0 : scalarSettings.setDataResamplingMethod( QgsMeshRendererScalarSettings::None );
118 : 0 : break;
119 : : case QgsMeshDatasetGroupMetadata::DataOnEdges:
120 : 0 : break;
121 : : }
122 : :
123 : : //override color ramp if the values in the dataset group are classified
124 : 0 : applyClassificationOnScalarSettings( meta, scalarSettings );
125 : :
126 : 0 : mRendererSettings.setScalarSettings( i, scalarSettings );
127 : 0 : }
128 : :
129 : 0 : }
130 : :
131 : 0 : void QgsMeshLayer::createSimplifiedMeshes()
132 : : {
133 : 0 : if ( mSimplificationSettings.isEnabled() && !hasSimplifiedMeshes() )
134 : : {
135 : 0 : double reductionFactor = mSimplificationSettings.reductionFactor();
136 : :
137 : : QVector<QgsTriangularMesh *> simplifyMeshes =
138 : 0 : mTriangularMeshes[0]->simplifyMesh( reductionFactor );
139 : :
140 : 0 : for ( int i = 0; i < simplifyMeshes.count() ; ++i )
141 : : {
142 : 0 : mTriangularMeshes.emplace_back( simplifyMeshes[i] );
143 : 0 : }
144 : 0 : }
145 : 0 : }
146 : :
147 : 0 : bool QgsMeshLayer::hasSimplifiedMeshes() const
148 : : {
149 : : //First mesh is the base mesh, so if size>1, there is no simplified meshes
150 : 0 : return ( mTriangularMeshes.size() > 1 );
151 : : }
152 : :
153 : 0 : QgsMeshLayer::~QgsMeshLayer()
154 : 0 : {
155 : 0 : delete mDataProvider;
156 : 0 : }
157 : :
158 : 0 : QgsMeshDataProvider *QgsMeshLayer::dataProvider()
159 : : {
160 : 0 : return mDataProvider;
161 : : }
162 : :
163 : 0 : const QgsMeshDataProvider *QgsMeshLayer::dataProvider() const
164 : : {
165 : 0 : return mDataProvider;
166 : : }
167 : :
168 : 0 : QgsMeshLayer *QgsMeshLayer::clone() const
169 : : {
170 : 0 : QgsMeshLayer::LayerOptions options;
171 : 0 : if ( mDataProvider )
172 : : {
173 : 0 : options.transformContext = mDataProvider->transformContext();
174 : 0 : }
175 : 0 : QgsMeshLayer *layer = new QgsMeshLayer( source(), name(), mProviderKey, options );
176 : 0 : QgsMapLayer::clone( layer );
177 : 0 : return layer;
178 : 0 : }
179 : :
180 : 0 : QgsRectangle QgsMeshLayer::extent() const
181 : : {
182 : 0 : if ( mDataProvider )
183 : 0 : return mDataProvider->extent();
184 : : else
185 : : {
186 : 0 : QgsRectangle rec;
187 : 0 : rec.setMinimal();
188 : 0 : return rec;
189 : : }
190 : 0 : }
191 : :
192 : 0 : QString QgsMeshLayer::providerType() const
193 : : {
194 : 0 : return mProviderKey;
195 : : }
196 : :
197 : 0 : bool QgsMeshLayer::addDatasets( const QString &path, const QDateTime &defaultReferenceTime )
198 : : {
199 : 0 : bool isTemporalBefore = dataProvider()->temporalCapabilities()->hasTemporalCapabilities();
200 : 0 : if ( mDatasetGroupStore->addPersistentDatasets( path ) )
201 : : {
202 : 0 : QgsMeshLayerTemporalProperties *temporalProperties = qobject_cast< QgsMeshLayerTemporalProperties * >( mTemporalProperties );
203 : 0 : if ( !isTemporalBefore && dataProvider()->temporalCapabilities()->hasTemporalCapabilities() )
204 : : {
205 : 0 : mTemporalProperties->setDefaultsFromDataProviderTemporalCapabilities(
206 : 0 : dataProvider()->temporalCapabilities() );
207 : :
208 : 0 : if ( ! temporalProperties->referenceTime().isValid() )
209 : : {
210 : 0 : QDateTime referenceTime = defaultReferenceTime;
211 : 0 : if ( !defaultReferenceTime.isValid() ) // If project reference time is invalid, use current date
212 : 0 : referenceTime = QDateTime( QDate::currentDate(), QTime( 0, 0, 0 ), Qt::UTC );
213 : 0 : temporalProperties->setReferenceTime( referenceTime, dataProvider()->temporalCapabilities() );
214 : 0 : }
215 : :
216 : 0 : mTemporalProperties->setIsActive( true );
217 : 0 : }
218 : 0 : emit dataSourceChanged();
219 : 0 : return true;
220 : : }
221 : :
222 : 0 : return false;
223 : 0 : }
224 : :
225 : 0 : bool QgsMeshLayer::addDatasets( QgsMeshDatasetGroup *datasetGroup )
226 : : {
227 : 0 : if ( mDatasetGroupStore->addDatasetGroup( datasetGroup ) )
228 : : {
229 : 0 : emit dataChanged();
230 : 0 : return true;
231 : : }
232 : 0 : return false;
233 : 0 : }
234 : :
235 : 0 : bool QgsMeshLayer::saveDataset( const QString &path, int datasetGroupIndex, QString driver )
236 : : {
237 : 0 : return mDatasetGroupStore->saveDatasetGroup( path, datasetGroupIndex, driver );
238 : 0 : }
239 : :
240 : 0 : QgsMesh *QgsMeshLayer::nativeMesh()
241 : : {
242 : 0 : return mNativeMesh.get();
243 : : }
244 : :
245 : 0 : const QgsMesh *QgsMeshLayer::nativeMesh() const
246 : : {
247 : 0 : return mNativeMesh.get();
248 : : }
249 : :
250 : 0 : QgsTriangularMesh *QgsMeshLayer::triangularMesh( double minimumTriangleSize ) const
251 : : {
252 : 0 : for ( const std::unique_ptr<QgsTriangularMesh> &lod : mTriangularMeshes )
253 : : {
254 : 0 : if ( lod && lod->averageTriangleSize() > minimumTriangleSize )
255 : 0 : return lod.get();
256 : : }
257 : :
258 : 0 : if ( !mTriangularMeshes.empty() )
259 : 0 : return mTriangularMeshes.back().get();
260 : : else
261 : 0 : return nullptr;
262 : 0 : }
263 : :
264 : 0 : int QgsMeshLayer::triangularMeshLevelOfDetailCount() const SIP_SKIP
265 : : {
266 : 0 : return mTriangularMeshes.size();
267 : : }
268 : :
269 : 0 : QgsTriangularMesh *QgsMeshLayer::triangularMeshByLodIndex( int lodIndex ) const SIP_SKIP
270 : : {
271 : 0 : if ( mTriangularMeshes.empty() )
272 : 0 : return nullptr;
273 : 0 : if ( lodIndex < 0 )
274 : 0 : return mTriangularMeshes.front().get();
275 : :
276 : 0 : if ( lodIndex >= int( mTriangularMeshes.size() ) )
277 : 0 : return mTriangularMeshes.back().get();
278 : :
279 : 0 : return mTriangularMeshes.at( lodIndex ).get();
280 : 0 : }
281 : :
282 : 0 : void QgsMeshLayer::updateTriangularMesh( const QgsCoordinateTransform &transform )
283 : : {
284 : : // Native mesh
285 : 0 : if ( !mNativeMesh )
286 : : {
287 : : // lazy loading of mesh data
288 : 0 : fillNativeMesh();
289 : 0 : }
290 : :
291 : : // Triangular mesh
292 : 0 : if ( mTriangularMeshes.empty() )
293 : : {
294 : 0 : QgsTriangularMesh *baseMesh = new QgsTriangularMesh;
295 : 0 : mTriangularMeshes.emplace_back( baseMesh );
296 : 0 : }
297 : :
298 : 0 : if ( mTriangularMeshes[0].get()->update( mNativeMesh.get(), transform ) )
299 : 0 : mTriangularMeshes.resize( 1 ); //if the base triangular mesh is effectivly updated, remove simplified meshes
300 : :
301 : 0 : createSimplifiedMeshes();
302 : 0 : }
303 : :
304 : 0 : QgsMeshLayerRendererCache *QgsMeshLayer::rendererCache()
305 : : {
306 : 0 : return mRendererCache.get();
307 : : }
308 : :
309 : 0 : QgsMeshRendererSettings QgsMeshLayer::rendererSettings() const
310 : : {
311 : 0 : return mRendererSettings;
312 : : }
313 : :
314 : 0 : void QgsMeshLayer::setRendererSettings( const QgsMeshRendererSettings &settings )
315 : : {
316 : 0 : int oldActiveScalar = mRendererSettings.activeScalarDatasetGroup();
317 : 0 : int oldActiveVector = mRendererSettings.activeVectorDatasetGroup();
318 : 0 : mRendererSettings = settings;
319 : :
320 : 0 : if ( oldActiveScalar != mRendererSettings.activeScalarDatasetGroup() )
321 : 0 : emit activeScalarDatasetGroupChanged( mRendererSettings.activeScalarDatasetGroup() );
322 : :
323 : 0 : if ( oldActiveVector != mRendererSettings.activeVectorDatasetGroup() )
324 : 0 : emit activeVectorDatasetGroupChanged( mRendererSettings.activeVectorDatasetGroup() );
325 : :
326 : 0 : emit rendererChanged();
327 : 0 : triggerRepaint();
328 : 0 : }
329 : :
330 : 0 : QgsMeshTimeSettings QgsMeshLayer::timeSettings() const
331 : : {
332 : 0 : return mTimeSettings;
333 : : }
334 : :
335 : 0 : void QgsMeshLayer::setTimeSettings( const QgsMeshTimeSettings &settings )
336 : : {
337 : 0 : mTimeSettings = settings;
338 : 0 : emit timeSettingsChanged();
339 : 0 : }
340 : :
341 : 0 : QString QgsMeshLayer::formatTime( double hours )
342 : : {
343 : 0 : if ( dataProvider() && dataProvider()->temporalCapabilities()->hasReferenceTime() )
344 : 0 : return QgsMeshLayerUtils::formatTime( hours, mTemporalProperties->referenceTime(), mTimeSettings );
345 : : else
346 : 0 : return QgsMeshLayerUtils::formatTime( hours, QDateTime(), mTimeSettings );
347 : 0 : }
348 : :
349 : 0 : int QgsMeshLayer::datasetGroupCount() const
350 : : {
351 : 0 : return mDatasetGroupStore->datasetGroupCount();
352 : : }
353 : :
354 : 0 : int QgsMeshLayer::extraDatasetGroupCount() const
355 : : {
356 : 0 : return mDatasetGroupStore->extraDatasetGroupCount();
357 : : }
358 : :
359 : 0 : QList<int> QgsMeshLayer::datasetGroupsIndexes() const
360 : : {
361 : 0 : return mDatasetGroupStore->datasetGroupIndexes();
362 : : }
363 : :
364 : 0 : QList<int> QgsMeshLayer::enabledDatasetGroupsIndexes() const
365 : : {
366 : 0 : return mDatasetGroupStore->enabledDatasetGroupIndexes();
367 : : }
368 : :
369 : 0 : QgsMeshDatasetGroupMetadata QgsMeshLayer::datasetGroupMetadata( const QgsMeshDatasetIndex &index ) const
370 : : {
371 : 0 : return mDatasetGroupStore->datasetGroupMetadata( index );
372 : : }
373 : :
374 : 0 : int QgsMeshLayer::datasetCount( const QgsMeshDatasetIndex &index ) const
375 : : {
376 : 0 : return mDatasetGroupStore->datasetCount( index.group() );
377 : : }
378 : :
379 : 0 : QgsMeshDatasetMetadata QgsMeshLayer::datasetMetadata( const QgsMeshDatasetIndex &index ) const
380 : : {
381 : 0 : return mDatasetGroupStore->datasetMetadata( index );
382 : : }
383 : :
384 : 0 : QgsMeshDatasetValue QgsMeshLayer::datasetValue( const QgsMeshDatasetIndex &index, int valueIndex ) const
385 : : {
386 : 0 : return mDatasetGroupStore->datasetValue( index, valueIndex );
387 : : }
388 : :
389 : 0 : QgsMeshDataBlock QgsMeshLayer::datasetValues( const QgsMeshDatasetIndex &index, int valueIndex, int count ) const
390 : : {
391 : 0 : return mDatasetGroupStore->datasetValues( index, valueIndex, count );
392 : : }
393 : :
394 : 0 : QgsMesh3dDataBlock QgsMeshLayer::dataset3dValues( const QgsMeshDatasetIndex &index, int faceIndex, int count ) const
395 : : {
396 : 0 : return mDatasetGroupStore->dataset3dValues( index, faceIndex, count );
397 : : }
398 : :
399 : 0 : QgsMeshDataBlock QgsMeshLayer::areFacesActive( const QgsMeshDatasetIndex &index, int faceIndex, int count ) const
400 : : {
401 : 0 : return mDatasetGroupStore->areFacesActive( index, faceIndex, count );
402 : : }
403 : :
404 : 0 : bool QgsMeshLayer::isFaceActive( const QgsMeshDatasetIndex &index, int faceIndex ) const
405 : : {
406 : 0 : return mDatasetGroupStore->isFaceActive( index, faceIndex );
407 : : }
408 : :
409 : 0 : QgsMeshDatasetValue QgsMeshLayer::datasetValue( const QgsMeshDatasetIndex &index, const QgsPointXY &point, double searchRadius ) const
410 : : {
411 : 0 : QgsMeshDatasetValue value;
412 : 0 : const QgsTriangularMesh *mesh = triangularMesh();
413 : :
414 : 0 : if ( mesh && dataProvider() && dataProvider()->isValid() && index.isValid() )
415 : : {
416 : 0 : if ( dataProvider()->contains( QgsMesh::ElementType::Edge ) )
417 : : {
418 : 0 : QgsRectangle searchRectangle( point.x() - searchRadius, point.y() - searchRadius, point.x() + searchRadius, point.y() + searchRadius );
419 : 0 : return dataset1dValue( index, point, searchRadius );
420 : : }
421 : 0 : int faceIndex = mesh->faceIndexForPoint_v2( point ) ;
422 : 0 : if ( faceIndex >= 0 )
423 : : {
424 : 0 : int nativeFaceIndex = mesh->trianglesToNativeFaces().at( faceIndex );
425 : 0 : const QgsMeshDatasetGroupMetadata::DataType dataType = datasetGroupMetadata( index ).dataType();
426 : 0 : if ( isFaceActive( index, nativeFaceIndex ) )
427 : : {
428 : 0 : switch ( dataType )
429 : : {
430 : : case QgsMeshDatasetGroupMetadata::DataOnFaces:
431 : : {
432 : 0 : value = datasetValue( index, nativeFaceIndex );
433 : : }
434 : 0 : break;
435 : :
436 : : case QgsMeshDatasetGroupMetadata::DataOnVertices:
437 : : {
438 : 0 : const QgsMeshFace &face = mesh->triangles()[faceIndex];
439 : 0 : const int v1 = face[0], v2 = face[1], v3 = face[2];
440 : 0 : const QgsPoint p1 = mesh->vertices()[v1], p2 = mesh->vertices()[v2], p3 = mesh->vertices()[v3];
441 : 0 : const QgsMeshDatasetValue val1 = datasetValue( index, v1 );
442 : 0 : const QgsMeshDatasetValue val2 = datasetValue( index, v2 );
443 : 0 : const QgsMeshDatasetValue val3 = datasetValue( index, v3 );
444 : 0 : const double x = QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.x(), val2.x(), val3.x(), point );
445 : 0 : double y = std::numeric_limits<double>::quiet_NaN();
446 : 0 : bool isVector = datasetGroupMetadata( index ).isVector();
447 : 0 : if ( isVector )
448 : 0 : y = QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.y(), val2.y(), val3.y(), point );
449 : :
450 : 0 : value = QgsMeshDatasetValue( x, y );
451 : 0 : }
452 : 0 : break;
453 : :
454 : : case QgsMeshDatasetGroupMetadata::DataOnVolumes:
455 : : {
456 : 0 : const QgsMesh3dAveragingMethod *avgMethod = mRendererSettings.averagingMethod();
457 : 0 : if ( avgMethod )
458 : : {
459 : 0 : const QgsMesh3dDataBlock block3d = dataset3dValues( index, nativeFaceIndex, 1 );
460 : 0 : const QgsMeshDataBlock block2d = avgMethod->calculate( block3d );
461 : 0 : if ( block2d.isValid() )
462 : : {
463 : 0 : value = block2d.value( 0 );
464 : 0 : }
465 : 0 : }
466 : : }
467 : 0 : break;
468 : :
469 : : default:
470 : 0 : break;
471 : : }
472 : 0 : }
473 : 0 : }
474 : 0 : }
475 : :
476 : 0 : return value;
477 : 0 : }
478 : :
479 : 0 : QgsMesh3dDataBlock QgsMeshLayer::dataset3dValue( const QgsMeshDatasetIndex &index, const QgsPointXY &point ) const
480 : : {
481 : 0 : QgsMesh3dDataBlock block3d;
482 : :
483 : 0 : const QgsTriangularMesh *baseTriangularMesh = triangularMesh();
484 : :
485 : 0 : if ( baseTriangularMesh && dataProvider() && dataProvider()->isValid() && index.isValid() )
486 : : {
487 : 0 : const QgsMeshDatasetGroupMetadata::DataType dataType = datasetGroupMetadata( index ).dataType();
488 : 0 : if ( dataType == QgsMeshDatasetGroupMetadata::DataOnVolumes )
489 : : {
490 : 0 : int faceIndex = baseTriangularMesh->faceIndexForPoint_v2( point );
491 : 0 : if ( faceIndex >= 0 )
492 : : {
493 : 0 : int nativeFaceIndex = baseTriangularMesh->trianglesToNativeFaces().at( faceIndex );
494 : 0 : block3d = dataset3dValues( index, nativeFaceIndex, 1 );
495 : 0 : }
496 : 0 : }
497 : 0 : }
498 : 0 : return block3d;
499 : 0 : }
500 : :
501 : 0 : QgsMeshDatasetValue QgsMeshLayer::dataset1dValue( const QgsMeshDatasetIndex &index, const QgsPointXY &point, double searchRadius ) const
502 : : {
503 : 0 : QgsMeshDatasetValue value;
504 : 0 : QgsPointXY projectedPoint;
505 : 0 : int selectedIndex = closestEdge( point, searchRadius, projectedPoint );
506 : 0 : const QgsTriangularMesh *mesh = triangularMesh();
507 : 0 : if ( selectedIndex >= 0 )
508 : : {
509 : 0 : const QgsMeshDatasetGroupMetadata::DataType dataType = datasetGroupMetadata( index ).dataType();
510 : 0 : switch ( dataType )
511 : : {
512 : : case QgsMeshDatasetGroupMetadata::DataOnEdges:
513 : : {
514 : 0 : value = datasetValue( index, selectedIndex );
515 : : }
516 : 0 : break;
517 : :
518 : : case QgsMeshDatasetGroupMetadata::DataOnVertices:
519 : : {
520 : 0 : const QgsMeshEdge &edge = mesh->edges()[selectedIndex];
521 : 0 : const int v1 = edge.first, v2 = edge.second;
522 : 0 : const QgsPoint p1 = mesh->vertices()[v1], p2 = mesh->vertices()[v2];
523 : 0 : const QgsMeshDatasetValue val1 = datasetValue( index, v1 );
524 : 0 : const QgsMeshDatasetValue val2 = datasetValue( index, v2 );
525 : 0 : double edgeLength = p1.distance( p2 );
526 : 0 : double dist1 = p1.distance( projectedPoint.x(), projectedPoint.y() );
527 : 0 : value = QgsMeshLayerUtils::interpolateFromVerticesData( dist1 / edgeLength, val1, val2 );
528 : 0 : }
529 : 0 : break;
530 : : default:
531 : 0 : break;
532 : : }
533 : 0 : }
534 : :
535 : 0 : return value;
536 : 0 : }
537 : :
538 : 0 : void QgsMeshLayer::setTransformContext( const QgsCoordinateTransformContext &transformContext )
539 : : {
540 : 0 : if ( mDataProvider )
541 : 0 : mDataProvider->setTransformContext( transformContext );
542 : 0 : invalidateWgs84Extent();
543 : 0 : }
544 : :
545 : 0 : QgsMeshDatasetIndex QgsMeshLayer::datasetIndexAtTime( const QgsDateTimeRange &timeRange, int datasetGroupIndex ) const
546 : : {
547 : 0 : if ( ! mTemporalProperties->isActive() )
548 : 0 : return QgsMeshDatasetIndex( datasetGroupIndex, -1 );
549 : :
550 : 0 : const QDateTime layerReferenceTime = mTemporalProperties->referenceTime();
551 : 0 : qint64 startTime = layerReferenceTime.msecsTo( timeRange.begin() );
552 : :
553 : 0 : return mDatasetGroupStore->datasetIndexAtTime( startTime, datasetGroupIndex, mTemporalProperties->matchingMethod() );
554 : 0 : }
555 : :
556 : 0 : QgsMeshDatasetIndex QgsMeshLayer::datasetIndexAtRelativeTime( const QgsInterval &relativeTime, int datasetGroupIndex ) const
557 : : {
558 : 0 : qint64 usedRelativeTime = relativeTime.seconds() * 1000;
559 : :
560 : : //adjust relative time if layer reference time is different from provider reference time
561 : 0 : if ( mTemporalProperties->referenceTime().isValid() &&
562 : 0 : mDataProvider &&
563 : 0 : mDataProvider->isValid() &&
564 : 0 : mTemporalProperties->referenceTime() != mDataProvider->temporalCapabilities()->referenceTime() )
565 : 0 : usedRelativeTime = usedRelativeTime + mTemporalProperties->referenceTime().msecsTo( mDataProvider->temporalCapabilities()->referenceTime() );
566 : :
567 : 0 : return mDatasetGroupStore->datasetIndexAtTime( relativeTime.seconds() * 1000, datasetGroupIndex, mTemporalProperties->matchingMethod() );
568 : 0 : }
569 : :
570 : 0 : void QgsMeshLayer::applyClassificationOnScalarSettings( const QgsMeshDatasetGroupMetadata &meta, QgsMeshRendererScalarSettings &scalarSettings ) const
571 : : {
572 : 0 : if ( meta.extraOptions().contains( QStringLiteral( "classification" ) ) )
573 : : {
574 : 0 : QgsColorRampShader colorRampShader = scalarSettings.colorRampShader();
575 : 0 : QgsColorRamp *colorRamp = colorRampShader.sourceColorRamp();
576 : 0 : QStringList classes = meta.extraOptions()[QStringLiteral( "classification" )].split( QStringLiteral( ";;" ) );
577 : :
578 : 0 : QString units;
579 : 0 : if ( meta.extraOptions().contains( QStringLiteral( "units" ) ) )
580 : 0 : units = meta.extraOptions()[ QStringLiteral( "units" )];
581 : :
582 : 0 : QVector<QVector<double>> bounds;
583 : 0 : for ( const QString &classe : classes )
584 : : {
585 : 0 : QStringList boundsStr = classe.split( ',' );
586 : 0 : QVector<double> bound;
587 : 0 : for ( const QString &boundStr : boundsStr )
588 : 0 : bound.append( boundStr.toDouble() );
589 : 0 : bounds.append( bound );
590 : 0 : }
591 : :
592 : 0 : if ( ( bounds.count() == 1 && bounds.first().count() > 2 ) || // at least a class with two value
593 : 0 : ( bounds.count() > 1 ) ) // or at least two classes
594 : : {
595 : 0 : const QVector<double> firstClass = bounds.first();
596 : 0 : const QVector<double> lastClass = bounds.last();
597 : 0 : double minValue = firstClass.count() > 1 ? ( firstClass.first() + firstClass.last() ) / 2 : firstClass.first();
598 : 0 : double maxValue = lastClass.count() > 1 ? ( lastClass.first() + lastClass.last() ) / 2 : lastClass.first();
599 : 0 : double diff = maxValue - minValue;
600 : 0 : QList<QgsColorRampShader::ColorRampItem> colorRampItemlist;
601 : 0 : for ( int i = 0; i < bounds.count(); ++i )
602 : : {
603 : 0 : const QVector<double> &boundClass = bounds.at( i );
604 : 0 : QgsColorRampShader::ColorRampItem item;
605 : 0 : item.value = i + 1;
606 : 0 : if ( !boundClass.isEmpty() )
607 : : {
608 : 0 : double scalarValue = ( boundClass.first() + boundClass.last() ) / 2;
609 : 0 : item.color = colorRamp->color( ( scalarValue - minValue ) / diff );
610 : 0 : if ( i != 0 && i < bounds.count() - 1 ) //The first and last labels are treated after
611 : : {
612 : 0 : item.label = QString( ( "%1 - %2 %3" ) ).
613 : 0 : arg( QString::number( boundClass.first() ) ).
614 : 0 : arg( QString::number( boundClass.last() ) ).
615 : 0 : arg( units );
616 : 0 : }
617 : 0 : }
618 : 0 : colorRampItemlist.append( item );
619 : 0 : }
620 : : //treat first and last labels
621 : 0 : if ( firstClass.count() == 1 )
622 : 0 : colorRampItemlist.first().label = QObject::tr( "below %1 %2" ).
623 : 0 : arg( QString::number( firstClass.first() ) ).
624 : 0 : arg( units );
625 : : else
626 : : {
627 : 0 : colorRampItemlist.first().label = QString( ( "%1 - %2 %3" ) ).
628 : 0 : arg( QString::number( firstClass.first() ) ).
629 : 0 : arg( QString::number( firstClass.last() ) ).
630 : 0 : arg( units );
631 : : }
632 : :
633 : 0 : if ( lastClass.count() == 1 )
634 : 0 : colorRampItemlist.last().label = QObject::tr( "above %1 %2" ).
635 : 0 : arg( QString::number( lastClass.first() ) ).
636 : 0 : arg( units );
637 : : else
638 : : {
639 : 0 : colorRampItemlist.last().label = QString( ( "%1 - %2 %3" ) ).
640 : 0 : arg( QString::number( lastClass.first() ) ).
641 : 0 : arg( QString::number( lastClass.last() ) ).
642 : 0 : arg( units );
643 : : }
644 : :
645 : 0 : colorRampShader.setMinimumValue( 0 );
646 : 0 : colorRampShader.setMaximumValue( colorRampItemlist.count() - 1 );
647 : 0 : scalarSettings.setClassificationMinimumMaximum( 0, colorRampItemlist.count() - 1 );
648 : 0 : colorRampShader.setColorRampItemList( colorRampItemlist );
649 : 0 : colorRampShader.setColorRampType( QgsColorRampShader::Exact );
650 : 0 : colorRampShader.setClassificationMode( QgsColorRampShader::EqualInterval );
651 : 0 : }
652 : :
653 : 0 : scalarSettings.setColorRampShader( colorRampShader );
654 : 0 : scalarSettings.setDataResamplingMethod( QgsMeshRendererScalarSettings::None );
655 : 0 : }
656 : 0 : }
657 : :
658 : 0 : QgsMeshDatasetIndex QgsMeshLayer::activeScalarDatasetAtTime( const QgsDateTimeRange &timeRange ) const
659 : : {
660 : 0 : if ( mTemporalProperties->isActive() )
661 : 0 : return datasetIndexAtTime( timeRange, mRendererSettings.activeScalarDatasetGroup() );
662 : : else
663 : 0 : return QgsMeshDatasetIndex( mRendererSettings.activeScalarDatasetGroup(), mStaticScalarDatasetIndex );
664 : 0 : }
665 : :
666 : 0 : QgsMeshDatasetIndex QgsMeshLayer::activeVectorDatasetAtTime( const QgsDateTimeRange &timeRange ) const
667 : : {
668 : 0 : if ( mTemporalProperties->isActive() )
669 : 0 : return datasetIndexAtTime( timeRange, mRendererSettings.activeVectorDatasetGroup() );
670 : : else
671 : 0 : return QgsMeshDatasetIndex( mRendererSettings.activeVectorDatasetGroup(), mStaticVectorDatasetIndex );
672 : 0 : }
673 : :
674 : 0 : void QgsMeshLayer::fillNativeMesh()
675 : : {
676 : : Q_ASSERT( !mNativeMesh );
677 : :
678 : 0 : mNativeMesh.reset( new QgsMesh() );
679 : :
680 : 0 : if ( !( dataProvider() && dataProvider()->isValid() ) )
681 : 0 : return;
682 : :
683 : 0 : dataProvider()->populateMesh( mNativeMesh.get() );
684 : 0 : }
685 : :
686 : 0 : void QgsMeshLayer::onDatasetGroupsAdded( const QList<int> &datasetGroupIndexes )
687 : : {
688 : : // assign default style to new dataset groups
689 : 0 : for ( int i = 0; i < datasetGroupIndexes.count(); ++i )
690 : 0 : assignDefaultStyleToDatasetGroup( datasetGroupIndexes.at( i ) );
691 : :
692 : 0 : temporalProperties()->setIsActive( mDatasetGroupStore->hasTemporalCapabilities() );
693 : 0 : emit rendererChanged();
694 : 0 : }
695 : :
696 : 0 : QgsMeshDatasetGroupTreeItem *QgsMeshLayer::datasetGroupTreeRootItem() const
697 : : {
698 : 0 : return mDatasetGroupStore->datasetGroupTreeItem();
699 : : }
700 : :
701 : 0 : void QgsMeshLayer::setDatasetGroupTreeRootItem( QgsMeshDatasetGroupTreeItem *rootItem )
702 : : {
703 : 0 : mDatasetGroupStore->setDatasetGroupTreeItem( rootItem );
704 : 0 : updateActiveDatasetGroups();
705 : 0 : }
706 : :
707 : 0 : int QgsMeshLayer::closestEdge( const QgsPointXY &point, double searchRadius, QgsPointXY &projectedPoint ) const
708 : : {
709 : 0 : QgsRectangle searchRectangle( point.x() - searchRadius, point.y() - searchRadius, point.x() + searchRadius, point.y() + searchRadius );
710 : 0 : const QgsTriangularMesh *mesh = triangularMesh();
711 : : // search for the closest edge in search area from point
712 : 0 : const QList<int> edgeIndexes = mesh->edgeIndexesForRectangle( searchRectangle );
713 : 0 : int selectedIndex = -1;
714 : 0 : if ( mesh->contains( QgsMesh::Edge ) &&
715 : 0 : mDataProvider->isValid() )
716 : : {
717 : 0 : double sqrMaxDistFromPoint = pow( searchRadius, 2 );
718 : 0 : for ( const int edgeIndex : edgeIndexes )
719 : : {
720 : 0 : const QgsMeshEdge &edge = mesh->edges().at( edgeIndex );
721 : 0 : const QgsMeshVertex &vertex1 = mesh->vertices()[edge.first];
722 : 0 : const QgsMeshVertex &vertex2 = mesh->vertices()[edge.second];
723 : 0 : QgsPointXY projPoint;
724 : 0 : double sqrDist = point.sqrDistToSegment( vertex1.x(), vertex1.y(), vertex2.x(), vertex2.y(), projPoint );
725 : 0 : if ( sqrDist < sqrMaxDistFromPoint )
726 : : {
727 : 0 : selectedIndex = edgeIndex;
728 : 0 : projectedPoint = projPoint;
729 : 0 : sqrMaxDistFromPoint = sqrDist;
730 : 0 : }
731 : : }
732 : 0 : }
733 : :
734 : 0 : return selectedIndex;
735 : 0 : }
736 : :
737 : 0 : QgsMeshDatasetIndex QgsMeshLayer::staticVectorDatasetIndex() const
738 : : {
739 : 0 : return QgsMeshDatasetIndex( mRendererSettings.activeVectorDatasetGroup(), mStaticVectorDatasetIndex );
740 : : }
741 : :
742 : 0 : void QgsMeshLayer::setReferenceTime( const QDateTime &referenceTime )
743 : : {
744 : 0 : if ( auto *lDataProvider = dataProvider() )
745 : 0 : mTemporalProperties->setReferenceTime( referenceTime, lDataProvider->temporalCapabilities() );
746 : : else
747 : 0 : mTemporalProperties->setReferenceTime( referenceTime, nullptr );
748 : 0 : }
749 : :
750 : 0 : void QgsMeshLayer::setTemporalMatchingMethod( const QgsMeshDataProviderTemporalCapabilities::MatchingTemporalDatasetMethod &matchingMethod )
751 : : {
752 : 0 : mTemporalProperties->setMatchingMethod( matchingMethod );
753 : 0 : }
754 : :
755 : 0 : QgsPointXY QgsMeshLayer::snapOnVertex( const QgsPointXY &point, double searchRadius )
756 : : {
757 : 0 : const QgsTriangularMesh *mesh = triangularMesh();
758 : 0 : QgsPointXY exactPosition;
759 : 0 : if ( !mesh )
760 : 0 : return exactPosition;
761 : 0 : QgsRectangle rectangle( point.x() - searchRadius, point.y() - searchRadius, point.x() + searchRadius, point.y() + searchRadius );
762 : 0 : double maxDistance = searchRadius;
763 : : //attempt to snap on edges's vertices
764 : 0 : QList<int> edgeIndexes = mesh->edgeIndexesForRectangle( rectangle );
765 : 0 : for ( const int edgeIndex : edgeIndexes )
766 : : {
767 : 0 : const QgsMeshEdge &edge = mesh->edges().at( edgeIndex );
768 : 0 : const QgsMeshVertex &vertex1 = mesh->vertices()[edge.first];
769 : 0 : const QgsMeshVertex &vertex2 = mesh->vertices()[edge.second];
770 : 0 : double dist1 = point.distance( vertex1 );
771 : 0 : double dist2 = point.distance( vertex2 );
772 : 0 : if ( dist1 < maxDistance )
773 : : {
774 : 0 : maxDistance = dist1;
775 : 0 : exactPosition = vertex1;
776 : 0 : }
777 : 0 : if ( dist2 < maxDistance )
778 : : {
779 : 0 : maxDistance = dist2;
780 : 0 : exactPosition = vertex2;
781 : 0 : }
782 : : }
783 : :
784 : : //attempt to snap on face's vertices
785 : 0 : QList<int> faceIndexes = mesh->faceIndexesForRectangle( rectangle );
786 : 0 : for ( const int faceIndex : faceIndexes )
787 : : {
788 : 0 : const QgsMeshFace &face = mesh->triangles().at( faceIndex );
789 : 0 : for ( int i = 0; i < 3; ++i )
790 : : {
791 : 0 : const QgsMeshVertex &vertex = mesh->vertices()[face.at( i )];
792 : 0 : double dist = point.distance( vertex );
793 : 0 : if ( dist < maxDistance )
794 : : {
795 : 0 : maxDistance = dist;
796 : 0 : exactPosition = vertex;
797 : 0 : }
798 : 0 : }
799 : : }
800 : :
801 : : return exactPosition;
802 : 0 : }
803 : :
804 : 0 : QgsPointXY QgsMeshLayer::snapOnEdge( const QgsPointXY &point, double searchRadius )
805 : : {
806 : 0 : QgsPointXY projectedPoint;
807 : 0 : closestEdge( point, searchRadius, projectedPoint );
808 : :
809 : 0 : return projectedPoint;
810 : : }
811 : :
812 : 0 : QgsPointXY QgsMeshLayer::snapOnFace( const QgsPointXY &point, double searchRadius )
813 : 0 : {
814 : 0 : const QgsTriangularMesh *mesh = triangularMesh();
815 : 0 : QgsPointXY centroidPosition;
816 : 0 : if ( !mesh )
817 : 0 : return centroidPosition;
818 : 0 : QgsRectangle rectangle( point.x() - searchRadius, point.y() - searchRadius, point.x() + searchRadius, point.y() + searchRadius );
819 : 0 : double maxDistance = std::numeric_limits<double>::max();
820 : :
821 : 0 : QList<int> faceIndexes = mesh->faceIndexesForRectangle( rectangle );
822 : 0 : for ( const int faceIndex : faceIndexes )
823 : : {
824 : 0 : int nativefaceIndex = mesh->trianglesToNativeFaces().at( faceIndex );
825 : 0 : if ( nativefaceIndex < 0 && nativefaceIndex >= mesh->faceCentroids().count() )
826 : 0 : continue;
827 : 0 : const QgsPointXY centroid = mesh->faceCentroids()[nativefaceIndex];
828 : 0 : double dist = point.distance( centroid );
829 : 0 : if ( dist < maxDistance )
830 : : {
831 : 0 : maxDistance = dist;
832 : 0 : centroidPosition = centroid;
833 : 0 : }
834 : : }
835 : :
836 : : return centroidPosition;
837 : 0 : }
838 : :
839 : 0 : void QgsMeshLayer::resetDatasetGroupTreeItem()
840 : : {
841 : 0 : mDatasetGroupStore->resetDatasetGroupTreeItem();
842 : 0 : updateActiveDatasetGroups();
843 : 0 : }
844 : :
845 : 0 : QgsInterval QgsMeshLayer::firstValidTimeStep() const
846 : : {
847 : 0 : if ( !mDataProvider )
848 : 0 : return QgsInterval();
849 : 0 : int groupCount = mDataProvider->datasetGroupCount();
850 : 0 : for ( int i = 0; i < groupCount; ++i )
851 : : {
852 : 0 : qint64 timeStep = mDataProvider->temporalCapabilities()->firstTimeStepDuration( i );
853 : 0 : if ( timeStep > 0 )
854 : 0 : return QgsInterval( timeStep, QgsUnitTypes::TemporalMilliseconds );
855 : 0 : }
856 : :
857 : 0 : return QgsInterval();
858 : 0 : }
859 : :
860 : 0 : QgsInterval QgsMeshLayer::datasetRelativeTime( const QgsMeshDatasetIndex &index )
861 : : {
862 : 0 : qint64 time = mDatasetGroupStore->datasetRelativeTime( index );
863 : :
864 : 0 : if ( time == INVALID_MESHLAYER_TIME )
865 : 0 : return QgsInterval();
866 : : else
867 : 0 : return QgsInterval( time, QgsUnitTypes::TemporalMilliseconds );
868 : 0 : }
869 : :
870 : 0 : qint64 QgsMeshLayer::datasetRelativeTimeInMilliseconds( const QgsMeshDatasetIndex &index )
871 : : {
872 : 0 : return mDatasetGroupStore->datasetRelativeTime( index );
873 : : }
874 : :
875 : 0 : void QgsMeshLayer::updateActiveDatasetGroups()
876 : : {
877 : 0 : QgsMeshDatasetGroupTreeItem *treeItem = mDatasetGroupStore->datasetGroupTreeItem();
878 : :
879 : 0 : if ( !mDatasetGroupStore->datasetGroupTreeItem() )
880 : 0 : return;
881 : :
882 : 0 : QgsMeshRendererSettings settings = rendererSettings();
883 : 0 : int oldActiveScalar = settings.activeScalarDatasetGroup();
884 : 0 : int oldActiveVector = settings.activeVectorDatasetGroup();
885 : :
886 : 0 : QgsMeshDatasetGroupTreeItem *activeScalarItem =
887 : 0 : treeItem->childFromDatasetGroupIndex( oldActiveScalar );
888 : :
889 : 0 : if ( !activeScalarItem && treeItem->childCount() > 0 )
890 : 0 : activeScalarItem = treeItem->child( 0 );
891 : :
892 : 0 : if ( activeScalarItem && !activeScalarItem->isEnabled() )
893 : : {
894 : 0 : for ( int i = 0; i < treeItem->childCount(); ++i )
895 : : {
896 : 0 : activeScalarItem = treeItem->child( i );
897 : 0 : if ( activeScalarItem->isEnabled() )
898 : 0 : break;
899 : : else
900 : 0 : activeScalarItem = nullptr;
901 : 0 : }
902 : 0 : }
903 : :
904 : 0 : if ( activeScalarItem )
905 : 0 : settings.setActiveScalarDatasetGroup( activeScalarItem->datasetGroupIndex() );
906 : : else
907 : 0 : settings.setActiveScalarDatasetGroup( -1 );
908 : :
909 : 0 : QgsMeshDatasetGroupTreeItem *activeVectorItem =
910 : 0 : treeItem->childFromDatasetGroupIndex( oldActiveVector );
911 : :
912 : 0 : if ( !( activeVectorItem && activeVectorItem->isEnabled() ) )
913 : 0 : settings.setActiveVectorDatasetGroup( -1 );
914 : :
915 : 0 : setRendererSettings( settings );
916 : :
917 : 0 : if ( oldActiveScalar != settings.activeScalarDatasetGroup() )
918 : 0 : emit activeScalarDatasetGroupChanged( settings.activeScalarDatasetGroup() );
919 : 0 : if ( oldActiveVector != settings.activeVectorDatasetGroup() )
920 : 0 : emit activeVectorDatasetGroupChanged( settings.activeVectorDatasetGroup() );
921 : 0 : }
922 : :
923 : 0 : QgsPointXY QgsMeshLayer::snapOnElement( QgsMesh::ElementType elementType, const QgsPointXY &point, double searchRadius )
924 : : {
925 : 0 : switch ( elementType )
926 : : {
927 : : case QgsMesh::Vertex:
928 : 0 : return snapOnVertex( point, searchRadius );
929 : : case QgsMesh::Edge:
930 : 0 : return snapOnEdge( point, searchRadius );
931 : : case QgsMesh::Face:
932 : 0 : return snapOnFace( point, searchRadius );
933 : : }
934 : 0 : return QgsPointXY(); // avoid warnings
935 : 0 : }
936 : :
937 : 0 : QgsMeshDatasetIndex QgsMeshLayer::staticScalarDatasetIndex() const
938 : : {
939 : 0 : return QgsMeshDatasetIndex( mRendererSettings.activeScalarDatasetGroup(), mStaticScalarDatasetIndex );
940 : : }
941 : :
942 : 0 : void QgsMeshLayer::setStaticVectorDatasetIndex( const QgsMeshDatasetIndex &staticVectorDatasetIndex )
943 : : {
944 : 0 : int oldActiveVector = mRendererSettings.activeVectorDatasetGroup();
945 : :
946 : 0 : mStaticVectorDatasetIndex = staticVectorDatasetIndex.dataset();
947 : 0 : mRendererSettings.setActiveVectorDatasetGroup( staticVectorDatasetIndex.group() );
948 : :
949 : 0 : if ( oldActiveVector != mRendererSettings.activeVectorDatasetGroup() )
950 : 0 : emit activeVectorDatasetGroupChanged( mRendererSettings.activeVectorDatasetGroup() );
951 : 0 : }
952 : :
953 : 0 : void QgsMeshLayer::setStaticScalarDatasetIndex( const QgsMeshDatasetIndex &staticScalarDatasetIndex )
954 : : {
955 : 0 : int oldActiveScalar = mRendererSettings.activeScalarDatasetGroup();
956 : :
957 : 0 : mStaticScalarDatasetIndex = staticScalarDatasetIndex.dataset();
958 : 0 : mRendererSettings.setActiveScalarDatasetGroup( staticScalarDatasetIndex.group() );
959 : :
960 : 0 : if ( oldActiveScalar != mRendererSettings.activeScalarDatasetGroup() )
961 : 0 : emit activeScalarDatasetGroupChanged( mRendererSettings.activeScalarDatasetGroup() );
962 : 0 : }
963 : :
964 : 0 : QgsMeshSimplificationSettings QgsMeshLayer::meshSimplificationSettings() const
965 : : {
966 : 0 : return mSimplificationSettings;
967 : : }
968 : :
969 : 0 : void QgsMeshLayer::setMeshSimplificationSettings( const QgsMeshSimplificationSettings &simplifySettings )
970 : : {
971 : 0 : mSimplificationSettings = simplifySettings;
972 : 0 : }
973 : :
974 : 0 : static QgsColorRamp *_createDefaultColorRamp()
975 : : {
976 : 0 : QgsColorRamp *ramp = QgsStyle::defaultStyle()->colorRamp( QStringLiteral( "Plasma" ) );
977 : 0 : if ( ramp )
978 : 0 : return ramp;
979 : :
980 : : // definition of "Plasma" color ramp (in case it is not available in the style for some reason)
981 : 0 : QVariantMap props;
982 : 0 : props["color1"] = "13,8,135,255";
983 : 0 : props["color2"] = "240,249,33,255";
984 : 0 : props["stops"] =
985 : 0 : "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:"
986 : : "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:"
987 : : "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:"
988 : : "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:"
989 : : "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:"
990 : : "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:"
991 : : "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:"
992 : : "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:"
993 : : "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:"
994 : : "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";
995 : 0 : return QgsGradientColorRamp::create( props );
996 : 0 : }
997 : :
998 : 0 : void QgsMeshLayer::assignDefaultStyleToDatasetGroup( int groupIndex )
999 : : {
1000 : 0 : const QgsMeshDatasetGroupMetadata metadata = datasetGroupMetadata( groupIndex );
1001 : 0 : double groupMin = metadata.minimum();
1002 : 0 : double groupMax = metadata.maximum();
1003 : :
1004 : 0 : QgsColorRampShader fcn( groupMin, groupMax, _createDefaultColorRamp() );
1005 : 0 : fcn.classifyColorRamp( 5, -1, QgsRectangle(), nullptr );
1006 : :
1007 : 0 : QgsMeshRendererScalarSettings scalarSettings;
1008 : 0 : scalarSettings.setClassificationMinimumMaximum( groupMin, groupMax );
1009 : 0 : scalarSettings.setColorRampShader( fcn );
1010 : 0 : QgsInterpolatedLineWidth edgeStrokeWidth;
1011 : 0 : edgeStrokeWidth.setMinimumValue( groupMin );
1012 : 0 : edgeStrokeWidth.setMaximumValue( groupMax );
1013 : 0 : QgsInterpolatedLineColor edgeStrokeColor( fcn );
1014 : 0 : QgsInterpolatedLineRenderer edgeStrokePen;
1015 : 0 : scalarSettings.setEdgeStrokeWidth( edgeStrokeWidth );
1016 : 0 : mRendererSettings.setScalarSettings( groupIndex, scalarSettings );
1017 : :
1018 : 0 : if ( metadata.isVector() )
1019 : : {
1020 : 0 : QgsMeshRendererVectorSettings vectorSettings;
1021 : 0 : vectorSettings.setColorRampShader( fcn );
1022 : 0 : mRendererSettings.setVectorSettings( groupIndex, vectorSettings );
1023 : 0 : }
1024 : 0 : }
1025 : :
1026 : 0 : QgsMapLayerRenderer *QgsMeshLayer::createMapRenderer( QgsRenderContext &rendererContext )
1027 : : {
1028 : : // Triangular mesh
1029 : 0 : updateTriangularMesh( rendererContext.coordinateTransform() );
1030 : :
1031 : : // Build overview triangular meshes if needed
1032 : 0 : createSimplifiedMeshes();
1033 : :
1034 : : // Cache
1035 : 0 : if ( !mRendererCache )
1036 : 0 : mRendererCache.reset( new QgsMeshLayerRendererCache() );
1037 : :
1038 : 0 : return new QgsMeshLayerRenderer( this, rendererContext );
1039 : 0 : }
1040 : :
1041 : 0 : bool QgsMeshLayer::readSymbology( const QDomNode &node, QString &errorMessage,
1042 : : QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
1043 : : {
1044 : 0 : Q_UNUSED( errorMessage )
1045 : : // TODO: implement categories for raster layer
1046 : :
1047 : 0 : QDomElement elem = node.toElement();
1048 : :
1049 : 0 : readCommonStyle( elem, context, categories );
1050 : :
1051 : 0 : QDomElement elemRendererSettings = elem.firstChildElement( "mesh-renderer-settings" );
1052 : 0 : if ( !elemRendererSettings.isNull() )
1053 : 0 : mRendererSettings.readXml( elemRendererSettings, context );
1054 : :
1055 : 0 : QDomElement elemSimplifySettings = elem.firstChildElement( "mesh-simplify-settings" );
1056 : 0 : if ( !elemSimplifySettings.isNull() )
1057 : 0 : mSimplificationSettings.readXml( elemSimplifySettings, context );
1058 : :
1059 : : // get and set the blend mode if it exists
1060 : 0 : QDomNode blendModeNode = node.namedItem( QStringLiteral( "blendMode" ) );
1061 : 0 : if ( !blendModeNode.isNull() )
1062 : : {
1063 : 0 : QDomElement e = blendModeNode.toElement();
1064 : 0 : setBlendMode( QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( e.text().toInt() ) ) );
1065 : 0 : }
1066 : :
1067 : : // get and set the layer transparency
1068 : 0 : if ( categories.testFlag( Rendering ) )
1069 : : {
1070 : 0 : QDomNode layerOpacityNode = node.namedItem( QStringLiteral( "layerOpacity" ) );
1071 : 0 : if ( !layerOpacityNode.isNull() )
1072 : : {
1073 : 0 : QDomElement e = layerOpacityNode.toElement();
1074 : 0 : setOpacity( e.text().toDouble() );
1075 : 0 : }
1076 : 0 : }
1077 : :
1078 : : return true;
1079 : 0 : }
1080 : :
1081 : 0 : bool QgsMeshLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage,
1082 : : const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
1083 : : {
1084 : 0 : Q_UNUSED( errorMessage )
1085 : : // TODO: implement categories for raster layer
1086 : :
1087 : 0 : QDomElement elem = node.toElement();
1088 : :
1089 : 0 : writeCommonStyle( elem, doc, context, categories );
1090 : :
1091 : 0 : QDomElement elemRendererSettings = mRendererSettings.writeXml( doc, context );
1092 : 0 : elem.appendChild( elemRendererSettings );
1093 : :
1094 : 0 : QDomElement elemSimplifySettings = mSimplificationSettings.writeXml( doc, context );
1095 : 0 : elem.appendChild( elemSimplifySettings );
1096 : :
1097 : : // add blend mode node
1098 : 0 : QDomElement blendModeElement = doc.createElement( QStringLiteral( "blendMode" ) );
1099 : 0 : QDomText blendModeText = doc.createTextNode( QString::number( QgsPainting::getBlendModeEnum( blendMode() ) ) );
1100 : 0 : blendModeElement.appendChild( blendModeText );
1101 : 0 : node.appendChild( blendModeElement );
1102 : :
1103 : : // add the layer opacity
1104 : 0 : if ( categories.testFlag( Rendering ) )
1105 : : {
1106 : 0 : QDomElement layerOpacityElem = doc.createElement( QStringLiteral( "layerOpacity" ) );
1107 : 0 : QDomText layerOpacityText = doc.createTextNode( QString::number( opacity() ) );
1108 : 0 : layerOpacityElem.appendChild( layerOpacityText );
1109 : 0 : node.appendChild( layerOpacityElem );
1110 : 0 : }
1111 : :
1112 : : return true;
1113 : 0 : }
1114 : :
1115 : 0 : bool QgsMeshLayer::writeStyle( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
1116 : : {
1117 : 0 : return writeSymbology( node, doc, errorMessage, context, categories );
1118 : : }
1119 : :
1120 : 0 : bool QgsMeshLayer::readStyle( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
1121 : : {
1122 : 0 : return readSymbology( node, errorMessage, context, categories );
1123 : : }
1124 : :
1125 : 0 : QString QgsMeshLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
1126 : : {
1127 : 0 : QString src( source );
1128 : 0 : if ( provider == QLatin1String( "mdal" ) )
1129 : : {
1130 : 0 : src = context.pathResolver().readPath( src );
1131 : 0 : }
1132 : 0 : return src;
1133 : 0 : }
1134 : :
1135 : 0 : QString QgsMeshLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
1136 : : {
1137 : 0 : QString src( source );
1138 : 0 : if ( providerType() == QLatin1String( "mdal" ) )
1139 : : {
1140 : 0 : src = context.pathResolver().writePath( src );
1141 : 0 : }
1142 : 0 : return src;
1143 : 0 : }
1144 : :
1145 : 0 : bool QgsMeshLayer::readXml( const QDomNode &layer_node, QgsReadWriteContext &context )
1146 : : {
1147 : 0 : QgsDebugMsgLevel( QStringLiteral( "Datasource in QgsMeshLayer::readXml: %1" ).arg( mDataSource.toLocal8Bit().data() ), 3 );
1148 : :
1149 : : //process provider key
1150 : 0 : QDomNode pkeyNode = layer_node.namedItem( QStringLiteral( "provider" ) );
1151 : :
1152 : 0 : if ( pkeyNode.isNull() )
1153 : : {
1154 : 0 : mProviderKey.clear();
1155 : 0 : }
1156 : : else
1157 : : {
1158 : 0 : QDomElement pkeyElt = pkeyNode.toElement();
1159 : 0 : mProviderKey = pkeyElt.text();
1160 : 0 : }
1161 : :
1162 : 0 : if ( mReadFlags & QgsMapLayer::FlagDontResolveLayers )
1163 : : {
1164 : 0 : return false;
1165 : : }
1166 : :
1167 : 0 : QgsDataProvider::ProviderOptions providerOptions;
1168 : 0 : QgsDataProvider::ReadFlags flags = QgsDataProvider::ReadFlags();
1169 : 0 : if ( mReadFlags & QgsMapLayer::FlagTrustLayerMetadata )
1170 : : {
1171 : 0 : flags |= QgsDataProvider::FlagTrustDataSource;
1172 : 0 : }
1173 : 0 : if ( !setDataProvider( mProviderKey, providerOptions, flags ) )
1174 : : {
1175 : 0 : return false;
1176 : : }
1177 : :
1178 : 0 : QDomElement elemExtraDatasets = layer_node.firstChildElement( QStringLiteral( "extra-datasets" ) );
1179 : 0 : if ( !elemExtraDatasets.isNull() )
1180 : : {
1181 : 0 : QDomElement elemUri = elemExtraDatasets.firstChildElement( QStringLiteral( "uri" ) );
1182 : 0 : while ( !elemUri.isNull() )
1183 : : {
1184 : 0 : QString uri = context.pathResolver().readPath( elemUri.text() );
1185 : :
1186 : 0 : bool res = mDataProvider->addDataset( uri );
1187 : : #ifdef QGISDEBUG
1188 : : QgsDebugMsg( QStringLiteral( "extra dataset (res %1): %2" ).arg( res ).arg( uri ) );
1189 : : #else
1190 : : ( void )res; // avoid unused warning in release builds
1191 : : #endif
1192 : :
1193 : 0 : elemUri = elemUri.nextSiblingElement( QStringLiteral( "uri" ) );
1194 : 0 : }
1195 : 0 : }
1196 : :
1197 : 0 : if ( mDataProvider && pkeyNode.toElement().hasAttribute( QStringLiteral( "time-unit" ) ) )
1198 : 0 : mDataProvider->setTemporalUnit(
1199 : 0 : static_cast<QgsUnitTypes::TemporalUnit>( pkeyNode.toElement().attribute( QStringLiteral( "time-unit" ) ).toInt() ) );
1200 : :
1201 : : // read dataset group store
1202 : 0 : QDomElement elemDatasetGroupsStore = layer_node.firstChildElement( QStringLiteral( "mesh-dataset-groups-store" ) );
1203 : 0 : if ( elemDatasetGroupsStore.isNull() )
1204 : 0 : resetDatasetGroupTreeItem();
1205 : : else
1206 : 0 : mDatasetGroupStore->readXml( elemDatasetGroupsStore, context );
1207 : :
1208 : 0 : QString errorMsg;
1209 : 0 : readSymbology( layer_node, errorMsg, context );
1210 : :
1211 : 0 : if ( !mTemporalProperties->timeExtent().begin().isValid() )
1212 : 0 : temporalProperties()->setDefaultsFromDataProviderTemporalCapabilities( dataProvider()->temporalCapabilities() );
1213 : :
1214 : : // read static dataset
1215 : 0 : QDomElement elemStaticDataset = layer_node.firstChildElement( QStringLiteral( "static-active-dataset" ) );
1216 : 0 : if ( elemStaticDataset.hasAttribute( QStringLiteral( "scalar" ) ) )
1217 : : {
1218 : 0 : mStaticScalarDatasetIndex = elemStaticDataset.attribute( QStringLiteral( "scalar" ) ).toInt();
1219 : 0 : }
1220 : 0 : if ( elemStaticDataset.hasAttribute( QStringLiteral( "vector" ) ) )
1221 : : {
1222 : 0 : mStaticVectorDatasetIndex = elemStaticDataset.attribute( QStringLiteral( "vector" ) ).toInt();
1223 : 0 : }
1224 : :
1225 : 0 : return isValid(); // should be true if read successfully
1226 : 0 : }
1227 : :
1228 : 0 : bool QgsMeshLayer::writeXml( QDomNode &layer_node, QDomDocument &document, const QgsReadWriteContext &context ) const
1229 : : {
1230 : : // first get the layer element so that we can append the type attribute
1231 : 0 : QDomElement mapLayerNode = layer_node.toElement();
1232 : :
1233 : 0 : if ( mapLayerNode.isNull() || ( QLatin1String( "maplayer" ) != mapLayerNode.nodeName() ) )
1234 : : {
1235 : 0 : QgsDebugMsgLevel( QStringLiteral( "can't find <maplayer>" ), 2 );
1236 : 0 : return false;
1237 : : }
1238 : :
1239 : 0 : mapLayerNode.setAttribute( QStringLiteral( "type" ), QgsMapLayerFactory::typeToString( QgsMapLayerType::MeshLayer ) );
1240 : :
1241 : : // add provider node
1242 : 0 : if ( mDataProvider )
1243 : : {
1244 : 0 : QDomElement provider = document.createElement( QStringLiteral( "provider" ) );
1245 : 0 : QDomText providerText = document.createTextNode( providerType() );
1246 : 0 : provider.appendChild( providerText );
1247 : 0 : layer_node.appendChild( provider );
1248 : 0 : provider.setAttribute( QStringLiteral( "time-unit" ), mDataProvider->temporalCapabilities()->temporalUnit() );
1249 : :
1250 : 0 : const QStringList extraDatasetUris = mDataProvider->extraDatasets();
1251 : 0 : QDomElement elemExtraDatasets = document.createElement( QStringLiteral( "extra-datasets" ) );
1252 : 0 : for ( const QString &uri : extraDatasetUris )
1253 : : {
1254 : 0 : QString path = context.pathResolver().writePath( uri );
1255 : 0 : QDomElement elemUri = document.createElement( QStringLiteral( "uri" ) );
1256 : 0 : elemUri.appendChild( document.createTextNode( path ) );
1257 : 0 : elemExtraDatasets.appendChild( elemUri );
1258 : 0 : }
1259 : 0 : layer_node.appendChild( elemExtraDatasets );
1260 : 0 : }
1261 : :
1262 : 0 : QDomElement elemStaticDataset = document.createElement( QStringLiteral( "static-active-dataset" ) );
1263 : 0 : elemStaticDataset.setAttribute( QStringLiteral( "scalar" ), mStaticScalarDatasetIndex );
1264 : 0 : elemStaticDataset.setAttribute( QStringLiteral( "vector" ), mStaticVectorDatasetIndex );
1265 : 0 : layer_node.appendChild( elemStaticDataset );
1266 : :
1267 : : // write dataset group store
1268 : 0 : layer_node.appendChild( mDatasetGroupStore->writeXml( document, context ) );
1269 : :
1270 : : // renderer specific settings
1271 : 0 : QString errorMsg;
1272 : 0 : return writeSymbology( layer_node, document, errorMsg, context );
1273 : 0 : }
1274 : :
1275 : 0 : void QgsMeshLayer::reload()
1276 : : {
1277 : 0 : if ( mDataProvider && mDataProvider->isValid() )
1278 : : {
1279 : 0 : mDataProvider->reloadData();
1280 : :
1281 : : //reload the mesh structure
1282 : 0 : if ( !mNativeMesh )
1283 : 0 : mNativeMesh.reset( new QgsMesh );
1284 : :
1285 : 0 : dataProvider()->populateMesh( mNativeMesh.get() );
1286 : :
1287 : : //clear the TriangularMeshes
1288 : 0 : mTriangularMeshes.clear();
1289 : :
1290 : : //clear the rendererCache
1291 : 0 : mRendererCache.reset( new QgsMeshLayerRendererCache() );
1292 : 0 : }
1293 : 0 : }
1294 : :
1295 : 0 : QStringList QgsMeshLayer::subLayers() const
1296 : : {
1297 : 0 : if ( mDataProvider )
1298 : 0 : return mDataProvider->subLayers();
1299 : : else
1300 : 0 : return QStringList();
1301 : 0 : }
1302 : :
1303 : 0 : QString QgsMeshLayer::htmlMetadata() const
1304 : : {
1305 : 0 : QgsLayerMetadataFormatter htmlFormatter( metadata() );
1306 : 0 : QString myMetadata = QStringLiteral( "<html>\n<body>\n" );
1307 : :
1308 : : // Begin Provider section
1309 : 0 : myMetadata += QStringLiteral( "<h1>" ) + tr( "Information from provider" ) + QStringLiteral( "</h1>\n<hr>\n" );
1310 : 0 : myMetadata += QLatin1String( "<table class=\"list-view\">\n" );
1311 : :
1312 : : // name
1313 : 0 : myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Name" ) + QStringLiteral( "</td><td>" ) + name() + QStringLiteral( "</td></tr>\n" );
1314 : :
1315 : : // local path
1316 : 0 : QVariantMap uriComponents = QgsProviderRegistry::instance()->decodeUri( mProviderKey, publicSource() );
1317 : 0 : QString path;
1318 : 0 : if ( uriComponents.contains( QStringLiteral( "path" ) ) )
1319 : : {
1320 : 0 : path = uriComponents[QStringLiteral( "path" )].toString();
1321 : 0 : if ( QFile::exists( path ) )
1322 : 0 : myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Path" ) + QStringLiteral( "</td><td>%1" ).arg( QStringLiteral( "<a href=\"%1\">%2</a>" ).arg( QUrl::fromLocalFile( path ).toString(), QDir::toNativeSeparators( path ) ) ) + QStringLiteral( "</td></tr>\n" );
1323 : 0 : }
1324 : 0 : if ( uriComponents.contains( QStringLiteral( "url" ) ) )
1325 : : {
1326 : 0 : const QString url = uriComponents[QStringLiteral( "url" )].toString();
1327 : 0 : myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "URL" ) + QStringLiteral( "</td><td>%1" ).arg( QStringLiteral( "<a href=\"%1\">%2</a>" ).arg( QUrl( url ).toString(), url ) ) + QStringLiteral( "</td></tr>\n" );
1328 : 0 : }
1329 : :
1330 : : // data source
1331 : 0 : if ( publicSource() != path )
1332 : 0 : myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Source" ) + QStringLiteral( "</td><td>%1" ).arg( publicSource() ) + QStringLiteral( "</td></tr>\n" );
1333 : :
1334 : : // EPSG
1335 : 0 : myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "CRS" ) + QStringLiteral( "</td><td>" );
1336 : 0 : if ( crs().isValid() )
1337 : : {
1338 : 0 : myMetadata += crs().userFriendlyIdentifier( QgsCoordinateReferenceSystem::FullString ) + QStringLiteral( " - " );
1339 : 0 : if ( crs().isGeographic() )
1340 : 0 : myMetadata += tr( "Geographic" );
1341 : : else
1342 : 0 : myMetadata += tr( "Projected" );
1343 : 0 : }
1344 : 0 : myMetadata += QLatin1String( "</td></tr>\n" );
1345 : :
1346 : : // Extent
1347 : 0 : myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Extent" ) + QStringLiteral( "</td><td>" ) + extent().toString() + QStringLiteral( "</td></tr>\n" );
1348 : :
1349 : : // unit
1350 : 0 : myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Unit" ) + QStringLiteral( "</td><td>" ) + QgsUnitTypes::toString( crs().mapUnits() ) + QStringLiteral( "</td></tr>\n" );
1351 : :
1352 : : // feature count
1353 : 0 : QLocale locale = QLocale();
1354 : 0 : locale.setNumberOptions( locale.numberOptions() &= ~QLocale::NumberOption::OmitGroupSeparator );
1355 : :
1356 : 0 : if ( dataProvider() )
1357 : : {
1358 : 0 : myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" )
1359 : 0 : + tr( "Vertex count" ) + QStringLiteral( "</td><td>" )
1360 : 0 : + ( locale.toString( static_cast<qlonglong>( dataProvider()->vertexCount() ) ) )
1361 : 0 : + QStringLiteral( "</td></tr>\n" );
1362 : 0 : myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" )
1363 : 0 : + tr( "Face count" ) + QStringLiteral( "</td><td>" )
1364 : 0 : + ( locale.toString( static_cast<qlonglong>( dataProvider()->faceCount() ) ) )
1365 : 0 : + QStringLiteral( "</td></tr>\n" );
1366 : 0 : myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" )
1367 : 0 : + tr( "Edge count" ) + QStringLiteral( "</td><td>" )
1368 : 0 : + ( locale.toString( static_cast<qlonglong>( dataProvider()->edgeCount() ) ) )
1369 : 0 : + QStringLiteral( "</td></tr>\n" );
1370 : 0 : myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" )
1371 : 0 : + tr( "Dataset groups count" ) + QStringLiteral( "</td><td>" )
1372 : 0 : + ( locale.toString( static_cast<qlonglong>( dataProvider()->datasetGroupCount() ) ) )
1373 : 0 : + QStringLiteral( "</td></tr>\n" );
1374 : 0 : }
1375 : :
1376 : : // End Provider section
1377 : 0 : myMetadata += QLatin1String( "</table>\n<br><br>" );
1378 : :
1379 : : // identification section
1380 : 0 : myMetadata += QStringLiteral( "<h1>" ) + tr( "Identification" ) + QStringLiteral( "</h1>\n<hr>\n" );
1381 : 0 : myMetadata += htmlFormatter.identificationSectionHtml( );
1382 : 0 : myMetadata += QLatin1String( "<br><br>\n" );
1383 : :
1384 : : // extent section
1385 : 0 : myMetadata += QStringLiteral( "<h1>" ) + tr( "Extent" ) + QStringLiteral( "</h1>\n<hr>\n" );
1386 : 0 : myMetadata += htmlFormatter.extentSectionHtml( isSpatial() );
1387 : 0 : myMetadata += QLatin1String( "<br><br>\n" );
1388 : :
1389 : : // Start the Access section
1390 : 0 : myMetadata += QStringLiteral( "<h1>" ) + tr( "Access" ) + QStringLiteral( "</h1>\n<hr>\n" );
1391 : 0 : myMetadata += htmlFormatter.accessSectionHtml( );
1392 : 0 : myMetadata += QLatin1String( "<br><br>\n" );
1393 : :
1394 : : // Start the contacts section
1395 : 0 : myMetadata += QStringLiteral( "<h1>" ) + tr( "Contacts" ) + QStringLiteral( "</h1>\n<hr>\n" );
1396 : 0 : myMetadata += htmlFormatter.contactsSectionHtml( );
1397 : 0 : myMetadata += QLatin1String( "<br><br>\n" );
1398 : :
1399 : : // Start the links section
1400 : 0 : myMetadata += QStringLiteral( "<h1>" ) + tr( "Links" ) + QStringLiteral( "</h1>\n<hr>\n" );
1401 : 0 : myMetadata += htmlFormatter.linksSectionHtml( );
1402 : 0 : myMetadata += QLatin1String( "<br><br>\n" );
1403 : :
1404 : : // Start the history section
1405 : 0 : myMetadata += QStringLiteral( "<h1>" ) + tr( "History" ) + QStringLiteral( "</h1>\n<hr>\n" );
1406 : 0 : myMetadata += htmlFormatter.historySectionHtml( );
1407 : 0 : myMetadata += QLatin1String( "<br><br>\n" );
1408 : :
1409 : 0 : myMetadata += QLatin1String( "\n</body>\n</html>\n" );
1410 : 0 : return myMetadata;
1411 : 0 : }
1412 : :
1413 : 0 : bool QgsMeshLayer::setDataProvider( QString const &provider, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags )
1414 : : {
1415 : 0 : delete mDataProvider;
1416 : :
1417 : 0 : mProviderKey = provider;
1418 : 0 : QString dataSource = mDataSource;
1419 : :
1420 : 0 : mDataProvider = qobject_cast<QgsMeshDataProvider *>( QgsProviderRegistry::instance()->createProvider( provider, dataSource, options, flags ) );
1421 : 0 : if ( !mDataProvider )
1422 : : {
1423 : 0 : QgsDebugMsgLevel( QStringLiteral( "Unable to get mesh data provider" ), 2 );
1424 : 0 : return false;
1425 : : }
1426 : :
1427 : 0 : mDataProvider->setParent( this );
1428 : 0 : QgsDebugMsgLevel( QStringLiteral( "Instantiated the mesh data provider plugin" ), 2 );
1429 : :
1430 : 0 : setValid( mDataProvider->isValid() );
1431 : 0 : if ( !isValid() )
1432 : : {
1433 : 0 : QgsDebugMsgLevel( QStringLiteral( "Invalid mesh provider plugin %1" ).arg( QString( mDataSource.toUtf8() ) ), 2 );
1434 : 0 : return false;
1435 : : }
1436 : :
1437 : 0 : setCrs( mDataProvider->crs() );
1438 : :
1439 : 0 : if ( provider == QLatin1String( "mesh_memory" ) )
1440 : : {
1441 : : // required so that source differs between memory layers
1442 : 0 : mDataSource = mDataSource + QStringLiteral( "&uid=%1" ).arg( QUuid::createUuid().toString() );
1443 : 0 : }
1444 : :
1445 : 0 : mDatasetGroupStore->setPersistentProvider( mDataProvider );
1446 : :
1447 : 0 : for ( int i = 0; i < mDataProvider->datasetGroupCount(); ++i )
1448 : 0 : assignDefaultStyleToDatasetGroup( i );
1449 : :
1450 : 0 : connect( mDataProvider, &QgsMeshDataProvider::dataChanged, this, &QgsMeshLayer::dataChanged );
1451 : :
1452 : 0 : return true;
1453 : 0 : }
1454 : :
1455 : 0 : QgsMapLayerTemporalProperties *QgsMeshLayer::temporalProperties()
1456 : : {
1457 : 0 : return mTemporalProperties;
1458 : : }
|