mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-16 00:03:12 -04:00
415 lines
14 KiB
C++
415 lines
14 KiB
C++
/***************************************************************************
|
|
qgsmeshlayer.cpp
|
|
----------------
|
|
begin : April 2018
|
|
copyright : (C) 2018 by Peter Petrik
|
|
email : zilolv at gmail dot com
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include <cstddef>
|
|
#include <limits>
|
|
|
|
#include <QUuid>
|
|
|
|
#include "qgscolorramp.h"
|
|
#include "qgslogger.h"
|
|
#include "qgsmaplayerlegend.h"
|
|
#include "qgsmeshdataprovider.h"
|
|
#include "qgsmeshlayer.h"
|
|
#include "qgsmeshlayerinterpolator.h"
|
|
#include "qgsmeshlayerrenderer.h"
|
|
#include "qgsmeshlayerutils.h"
|
|
#include "qgsproviderregistry.h"
|
|
#include "qgsreadwritecontext.h"
|
|
#include "qgsstyle.h"
|
|
#include "qgstriangularmesh.h"
|
|
|
|
|
|
QgsMeshLayer::QgsMeshLayer( const QString &meshLayerPath,
|
|
const QString &baseName,
|
|
const QString &providerKey,
|
|
const LayerOptions & )
|
|
: QgsMapLayer( MeshLayer, baseName, meshLayerPath )
|
|
, mProviderKey( providerKey )
|
|
{
|
|
// load data
|
|
QgsDataProvider::ProviderOptions providerOptions;
|
|
setDataProvider( providerKey, providerOptions );
|
|
|
|
setLegend( QgsMapLayerLegend::defaultMeshLegend( this ) );
|
|
|
|
// show at least the mesh by default so we render something
|
|
QgsMeshRendererMeshSettings meshSettings;
|
|
meshSettings.setEnabled( true );
|
|
mRendererSettings.setNativeMeshSettings( meshSettings );
|
|
|
|
} // QgsMeshLayer ctor
|
|
|
|
|
|
|
|
QgsMeshLayer::~QgsMeshLayer()
|
|
{
|
|
delete mDataProvider;
|
|
}
|
|
|
|
QgsMeshDataProvider *QgsMeshLayer::dataProvider()
|
|
{
|
|
return mDataProvider;
|
|
}
|
|
|
|
const QgsMeshDataProvider *QgsMeshLayer::dataProvider() const
|
|
{
|
|
return mDataProvider;
|
|
}
|
|
|
|
QgsMeshLayer *QgsMeshLayer::clone() const
|
|
{
|
|
QgsMeshLayer *layer = new QgsMeshLayer( source(), name(), mProviderKey );
|
|
QgsMapLayer::clone( layer );
|
|
return layer;
|
|
}
|
|
|
|
QgsRectangle QgsMeshLayer::extent() const
|
|
{
|
|
if ( mDataProvider )
|
|
return mDataProvider->extent();
|
|
else
|
|
{
|
|
QgsRectangle rec;
|
|
rec.setMinimal();
|
|
return rec;
|
|
}
|
|
}
|
|
|
|
QString QgsMeshLayer::providerType() const
|
|
{
|
|
return mProviderKey;
|
|
}
|
|
|
|
QgsMesh *QgsMeshLayer::nativeMesh() SIP_SKIP
|
|
{
|
|
return mNativeMesh.get();
|
|
}
|
|
|
|
|
|
QgsTriangularMesh *QgsMeshLayer::triangularMesh() SIP_SKIP
|
|
{
|
|
return mTriangularMesh.get();
|
|
}
|
|
|
|
QgsMeshRendererSettings QgsMeshLayer::rendererSettings() const
|
|
{
|
|
return mRendererSettings;
|
|
}
|
|
|
|
void QgsMeshLayer::setRendererSettings( const QgsMeshRendererSettings &settings )
|
|
{
|
|
mRendererSettings = settings;
|
|
emit rendererChanged();
|
|
triggerRepaint();
|
|
}
|
|
|
|
QgsMeshDatasetValue QgsMeshLayer::datasetValue( const QgsMeshDatasetIndex &index, const QgsPointXY &point ) const
|
|
{
|
|
QgsMeshDatasetValue value;
|
|
|
|
if ( mTriangularMesh && dataProvider() && dataProvider()->isValid() && index.isValid() )
|
|
{
|
|
int faceIndex = mTriangularMesh->faceIndexForPoint( point ) ;
|
|
if ( faceIndex >= 0 )
|
|
{
|
|
int nativeFaceIndex = mTriangularMesh->trianglesToNativeFaces().at( faceIndex );
|
|
if ( dataProvider()->faceIsActive( index, nativeFaceIndex ) )
|
|
{
|
|
|
|
if ( dataProvider()->datasetGroupMetadata( index ).dataType() == QgsMeshDatasetGroupMetadata::DataOnFaces )
|
|
{
|
|
int nativeFaceIndex = mTriangularMesh->trianglesToNativeFaces().at( faceIndex );
|
|
value = dataProvider()->datasetValue( index, nativeFaceIndex );
|
|
}
|
|
else
|
|
{
|
|
const QgsMeshFace &face = mTriangularMesh->triangles()[faceIndex];
|
|
const int v1 = face[0], v2 = face[1], v3 = face[2];
|
|
const QgsPoint p1 = mTriangularMesh->vertices()[v1], p2 = mTriangularMesh->vertices()[v2], p3 = mTriangularMesh->vertices()[v3];
|
|
const QgsMeshDatasetValue val1 = dataProvider()->datasetValue( index, v1 );
|
|
const QgsMeshDatasetValue val2 = dataProvider()->datasetValue( index, v2 );
|
|
const QgsMeshDatasetValue val3 = dataProvider()->datasetValue( index, v3 );
|
|
const double x = QgsMeshLayerInterpolator::interpolateFromVerticesData( p1, p2, p3, val1.x(), val2.x(), val3.x(), point );
|
|
double y = std::numeric_limits<double>::quiet_NaN();
|
|
bool isVector = dataProvider()->datasetGroupMetadata( index ).isVector();
|
|
if ( isVector )
|
|
y = QgsMeshLayerInterpolator::interpolateFromVerticesData( p1, p2, p3, val1.y(), val2.y(), val3.y(), point );
|
|
|
|
value = QgsMeshDatasetValue( x, y );
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
void QgsMeshLayer::fillNativeMesh()
|
|
{
|
|
Q_ASSERT( !mNativeMesh );
|
|
|
|
mNativeMesh.reset( new QgsMesh() );
|
|
|
|
if ( !( dataProvider() && dataProvider()->isValid() ) )
|
|
return;
|
|
|
|
mNativeMesh->vertices.resize( dataProvider()->vertexCount() );
|
|
for ( int i = 0; i < dataProvider()->vertexCount(); ++i )
|
|
{
|
|
mNativeMesh->vertices[i] = dataProvider()->vertex( i );
|
|
}
|
|
|
|
mNativeMesh->faces.resize( dataProvider()->faceCount() );
|
|
for ( int i = 0; i < dataProvider()->faceCount(); ++i )
|
|
{
|
|
mNativeMesh->faces[i] = dataProvider()->face( i );
|
|
}
|
|
}
|
|
|
|
void QgsMeshLayer::onDatasetGroupsAdded( int count )
|
|
{
|
|
// assign default style to new dataset groups
|
|
int newDatasetGroupCount = mDataProvider->datasetGroupCount();
|
|
for ( int i = newDatasetGroupCount - count; i < newDatasetGroupCount; ++i )
|
|
assignDefaultStyleToDatasetGroup( i );
|
|
}
|
|
|
|
static QgsColorRamp *_createDefaultColorRamp()
|
|
{
|
|
QgsColorRamp *ramp = QgsStyle::defaultStyle()->colorRamp( QStringLiteral( "Plasma" ) );
|
|
if ( ramp )
|
|
return ramp;
|
|
|
|
// definition of "Plasma" color ramp (in case it is not available in the style for some reason)
|
|
QgsStringMap props;
|
|
props["color1"] = "13,8,135,255";
|
|
props["color2"] = "240,249,33,255";
|
|
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:"
|
|
"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:"
|
|
"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:"
|
|
"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:"
|
|
"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:"
|
|
"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:"
|
|
"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:"
|
|
"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:"
|
|
"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:"
|
|
"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";
|
|
return QgsGradientColorRamp::create( props );
|
|
}
|
|
|
|
void QgsMeshLayer::assignDefaultStyleToDatasetGroup( int groupIndex )
|
|
{
|
|
double groupMin, groupMax;
|
|
QgsMeshLayerUtils::calculateMinMaxForDatasetGroup( groupMin, groupMax, mDataProvider, groupIndex );
|
|
|
|
QgsColorRampShader fcn( groupMin, groupMax, _createDefaultColorRamp() );
|
|
fcn.classifyColorRamp( 5, -1, QgsRectangle(), nullptr );
|
|
|
|
QgsMeshRendererScalarSettings scalarSettings;
|
|
scalarSettings.setClassificationMinimumMaximum( groupMin, groupMax );
|
|
scalarSettings.setColorRampShader( fcn );
|
|
mRendererSettings.setScalarSettings( groupIndex, scalarSettings );
|
|
}
|
|
|
|
QgsMapLayerRenderer *QgsMeshLayer::createMapRenderer( QgsRenderContext &rendererContext )
|
|
{
|
|
if ( !mNativeMesh )
|
|
{
|
|
// lazy loading of mesh data
|
|
fillNativeMesh();
|
|
}
|
|
|
|
if ( !mTriangularMesh )
|
|
mTriangularMesh.reset( new QgsTriangularMesh() );
|
|
|
|
mTriangularMesh->update( mNativeMesh.get(), &rendererContext );
|
|
return new QgsMeshLayerRenderer( this, rendererContext );
|
|
}
|
|
|
|
bool QgsMeshLayer::readSymbology( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context )
|
|
{
|
|
Q_UNUSED( errorMessage );
|
|
Q_UNUSED( context );
|
|
|
|
QDomElement elem = node.toElement();
|
|
QDomElement elemRendererSettings = elem.firstChildElement( "mesh-renderer-settings" );
|
|
if ( !elemRendererSettings.isNull() )
|
|
mRendererSettings.readXml( elemRendererSettings );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool QgsMeshLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context ) const
|
|
{
|
|
Q_UNUSED( errorMessage );
|
|
Q_UNUSED( context );
|
|
|
|
QDomElement elem = node.toElement();
|
|
QDomElement elemRendererSettings = mRendererSettings.writeXml( doc );
|
|
elem.appendChild( elemRendererSettings );
|
|
|
|
return true;
|
|
}
|
|
|
|
QString QgsMeshLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
|
|
{
|
|
QString src( source );
|
|
if ( provider == QLatin1String( "mdal" ) )
|
|
{
|
|
src = context.pathResolver().readPath( src );
|
|
}
|
|
return src;
|
|
}
|
|
|
|
QString QgsMeshLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
|
|
{
|
|
QString src( source );
|
|
if ( providerType() == QLatin1String( "mdal" ) )
|
|
{
|
|
src = context.pathResolver().writePath( src );
|
|
}
|
|
return src;
|
|
}
|
|
|
|
bool QgsMeshLayer::readXml( const QDomNode &layer_node, QgsReadWriteContext &context )
|
|
{
|
|
QgsDebugMsgLevel( QStringLiteral( "Datasource in QgsMeshLayer::readXml: %1" ).arg( mDataSource.toLocal8Bit().data() ), 3 );
|
|
|
|
//process provider key
|
|
QDomNode pkeyNode = layer_node.namedItem( QStringLiteral( "provider" ) );
|
|
|
|
if ( pkeyNode.isNull() )
|
|
{
|
|
mProviderKey.clear();
|
|
}
|
|
else
|
|
{
|
|
QDomElement pkeyElt = pkeyNode.toElement();
|
|
mProviderKey = pkeyElt.text();
|
|
}
|
|
|
|
QgsDataProvider::ProviderOptions providerOptions;
|
|
if ( !setDataProvider( mProviderKey, providerOptions ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
QDomElement elemExtraDatasets = layer_node.firstChildElement( QStringLiteral( "extra-datasets" ) );
|
|
if ( !elemExtraDatasets.isNull() )
|
|
{
|
|
QDomElement elemUri = elemExtraDatasets.firstChildElement( QStringLiteral( "uri" ) );
|
|
while ( !elemUri.isNull() )
|
|
{
|
|
QString uri = context.pathResolver().readPath( elemUri.text() );
|
|
|
|
bool res = mDataProvider->addDataset( uri );
|
|
#ifdef QGISDEBUG
|
|
QgsDebugMsg( QStringLiteral( "extra dataset (res %1): %2" ).arg( res ).arg( uri ) );
|
|
#else
|
|
( void )res; // avoid unused warning in release builds
|
|
#endif
|
|
|
|
elemUri = elemUri.nextSiblingElement( QStringLiteral( "uri" ) );
|
|
}
|
|
}
|
|
|
|
QString errorMsg;
|
|
readSymbology( layer_node, errorMsg, context );
|
|
|
|
return mValid; // should be true if read successfully
|
|
}
|
|
|
|
bool QgsMeshLayer::writeXml( QDomNode &layer_node, QDomDocument &document, const QgsReadWriteContext &context ) const
|
|
{
|
|
// first get the layer element so that we can append the type attribute
|
|
QDomElement mapLayerNode = layer_node.toElement();
|
|
|
|
if ( mapLayerNode.isNull() || ( QLatin1String( "maplayer" ) != mapLayerNode.nodeName() ) )
|
|
{
|
|
QgsDebugMsgLevel( QStringLiteral( "can't find <maplayer>" ), 2 );
|
|
return false;
|
|
}
|
|
|
|
mapLayerNode.setAttribute( QStringLiteral( "type" ), QStringLiteral( "mesh" ) );
|
|
|
|
// add provider node
|
|
if ( mDataProvider )
|
|
{
|
|
QDomElement provider = document.createElement( QStringLiteral( "provider" ) );
|
|
QDomText providerText = document.createTextNode( providerType() );
|
|
provider.appendChild( providerText );
|
|
layer_node.appendChild( provider );
|
|
|
|
const QStringList extraDatasetUris = mDataProvider->extraDatasets();
|
|
QDomElement elemExtraDatasets = document.createElement( QStringLiteral( "extra-datasets" ) );
|
|
for ( const QString &uri : extraDatasetUris )
|
|
{
|
|
QString path = context.pathResolver().writePath( uri );
|
|
QDomElement elemUri = document.createElement( QStringLiteral( "uri" ) );
|
|
elemUri.appendChild( document.createTextNode( path ) );
|
|
elemExtraDatasets.appendChild( elemUri );
|
|
}
|
|
layer_node.appendChild( elemExtraDatasets );
|
|
}
|
|
|
|
// renderer specific settings
|
|
QString errorMsg;
|
|
return writeSymbology( layer_node, document, errorMsg, context );
|
|
}
|
|
|
|
bool QgsMeshLayer::setDataProvider( QString const &provider, const QgsDataProvider::ProviderOptions &options )
|
|
{
|
|
delete mDataProvider;
|
|
|
|
mProviderKey = provider;
|
|
QString dataSource = mDataSource;
|
|
|
|
mDataProvider = qobject_cast<QgsMeshDataProvider *>( QgsProviderRegistry::instance()->createProvider( provider, dataSource, options ) );
|
|
if ( !mDataProvider )
|
|
{
|
|
QgsDebugMsgLevel( QStringLiteral( "Unable to get mesh data provider" ), 2 );
|
|
return false;
|
|
}
|
|
|
|
mDataProvider->setParent( this );
|
|
QgsDebugMsgLevel( QStringLiteral( "Instantiated the mesh data provider plugin" ), 2 );
|
|
|
|
mValid = mDataProvider->isValid();
|
|
if ( !mValid )
|
|
{
|
|
QgsDebugMsgLevel( QStringLiteral( "Invalid mesh provider plugin %1" ).arg( QString( mDataSource.toUtf8() ) ), 2 );
|
|
return false;
|
|
}
|
|
|
|
if ( provider == QStringLiteral( "mesh_memory" ) )
|
|
{
|
|
// required so that source differs between memory layers
|
|
mDataSource = mDataSource + QStringLiteral( "&uid=%1" ).arg( QUuid::createUuid().toString() );
|
|
}
|
|
|
|
for ( int i = 0; i < mDataProvider->datasetGroupCount(); ++i )
|
|
assignDefaultStyleToDatasetGroup( i );
|
|
|
|
connect( mDataProvider, &QgsMeshDataProvider::dataChanged, this, &QgsMeshLayer::dataChanged );
|
|
connect( mDataProvider, &QgsMeshDataProvider::datasetGroupsAdded, this, &QgsMeshLayer::onDatasetGroupsAdded );
|
|
|
|
return true;
|
|
}
|