mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-07 00:03:52 -05:00
Merge pull request #7777 from PeterPetrik/mesh_vector_on_grid
[mesh] [feature] allow render vectors/arrows on the user-defined grid
This commit is contained in:
commit
1839daaa18
@ -282,6 +282,31 @@ Returns ratio of the head length of the arrow (range 0-1)
|
||||
void setArrowHeadLengthRatio( double arrowHeadLengthRatio );
|
||||
%Docstring
|
||||
Sets ratio of the head length of the arrow (range 0-1)
|
||||
%End
|
||||
|
||||
bool isOnUserDefinedGrid() const;
|
||||
%Docstring
|
||||
Returns whether vectors are drawn on user-defined grid
|
||||
%End
|
||||
void setOnUserDefinedGrid( bool enabled );
|
||||
%Docstring
|
||||
Toggles drawing of vectors on user defined grid
|
||||
%End
|
||||
int userGridCellWidth() const;
|
||||
%Docstring
|
||||
Returns width in pixels of user grid cell
|
||||
%End
|
||||
void setUserGridCellWidth( int width );
|
||||
%Docstring
|
||||
Sets width of user grid cell (in pixels)
|
||||
%End
|
||||
int userGridCellHeight() const;
|
||||
%Docstring
|
||||
Returns height in pixels of user grid cell
|
||||
%End
|
||||
void setUserGridCellHeight( int height );
|
||||
%Docstring
|
||||
Sets height of user grid cell (in pixels)
|
||||
%End
|
||||
|
||||
QDomElement writeXml( QDomDocument &doc ) const;
|
||||
|
||||
@ -19,7 +19,6 @@
|
||||
#include "qgsmeshlayer.h"
|
||||
#include "qgsmessagelog.h"
|
||||
|
||||
|
||||
QgsMeshRendererVectorSettingsWidget::QgsMeshRendererVectorSettingsWidget( QWidget *parent )
|
||||
: QWidget( parent )
|
||||
|
||||
@ -37,6 +36,9 @@ QgsMeshRendererVectorSettingsWidget::QgsMeshRendererVectorSettingsWidget( QWidge
|
||||
|
||||
connect( mShaftLengthComboBox, qgis::overload<int>::of( &QComboBox::currentIndexChanged ),
|
||||
mShaftOptionsStackedWidget, &QStackedWidget::setCurrentIndex );
|
||||
|
||||
connect( mDisplayVectorsOnGridGroupBox, &QGroupBox::toggled, this, &QgsMeshRendererVectorSettingsWidget::widgetChanged );
|
||||
|
||||
QVector<QLineEdit *> widgets;
|
||||
widgets << mMinMagLineEdit << mMaxMagLineEdit
|
||||
<< mHeadWidthLineEdit << mHeadLengthLineEdit
|
||||
@ -47,6 +49,9 @@ QgsMeshRendererVectorSettingsWidget::QgsMeshRendererVectorSettingsWidget( QWidge
|
||||
{
|
||||
connect( widget, &QLineEdit::textChanged, this, &QgsMeshRendererVectorSettingsWidget::widgetChanged );
|
||||
}
|
||||
|
||||
connect( mXSpacingSpinBox, qgis::overload<int>::of( &QgsSpinBox::valueChanged ), this, &QgsMeshRendererVectorSettingsWidget::widgetChanged );
|
||||
connect( mYSpacingSpinBox, qgis::overload<int>::of( &QgsSpinBox::valueChanged ), this, &QgsMeshRendererVectorSettingsWidget::widgetChanged );
|
||||
}
|
||||
|
||||
void QgsMeshRendererVectorSettingsWidget::setLayer( QgsMeshLayer *layer )
|
||||
@ -76,6 +81,12 @@ QgsMeshRendererVectorSettings QgsMeshRendererVectorSettingsWidget::settings() co
|
||||
val = filterValue( mHeadLengthLineEdit->text(), settings.arrowHeadLengthRatio() * 100.0 );
|
||||
settings.setArrowHeadLengthRatio( val / 100.0 );
|
||||
|
||||
// user grid
|
||||
bool enabled = mDisplayVectorsOnGridGroupBox->isChecked();
|
||||
settings.setOnUserDefinedGrid( enabled );
|
||||
settings.setUserGridCellWidth( mXSpacingSpinBox->value() );
|
||||
settings.setUserGridCellHeight( mYSpacingSpinBox->value() );
|
||||
|
||||
// shaft length
|
||||
auto method = static_cast<QgsMeshRendererVectorSettings::ArrowScalingMethod>( mShaftLengthComboBox->currentIndex() );
|
||||
settings.setShaftLengthMethod( method );
|
||||
@ -124,6 +135,11 @@ void QgsMeshRendererVectorSettingsWidget::syncToLayer( )
|
||||
mHeadWidthLineEdit->setText( QString::number( settings.arrowHeadWidthRatio() * 100.0 ) );
|
||||
mHeadLengthLineEdit->setText( QString::number( settings.arrowHeadLengthRatio() * 100.0 ) );
|
||||
|
||||
// user grid
|
||||
mDisplayVectorsOnGridGroupBox->setChecked( settings.isOnUserDefinedGrid() );
|
||||
mXSpacingSpinBox->setValue( settings.userGridCellWidth() );
|
||||
mYSpacingSpinBox->setValue( settings.userGridCellHeight() );
|
||||
|
||||
// shaft length
|
||||
mShaftLengthComboBox->setCurrentIndex( settings.shaftLengthMethod() );
|
||||
|
||||
|
||||
@ -25,7 +25,6 @@
|
||||
#include "qgsmaplayerlegend.h"
|
||||
#include "qgsmeshdataprovider.h"
|
||||
#include "qgsmeshlayer.h"
|
||||
#include "qgsmeshlayerinterpolator.h"
|
||||
#include "qgsmeshlayerrenderer.h"
|
||||
#include "qgsmeshlayerutils.h"
|
||||
#include "qgsproviderregistry.h"
|
||||
@ -144,11 +143,11 @@ QgsMeshDatasetValue QgsMeshLayer::datasetValue( const QgsMeshDatasetIndex &index
|
||||
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 );
|
||||
const double x = QgsMeshLayerUtils::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 );
|
||||
y = QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.y(), val2.y(), val3.y(), point );
|
||||
|
||||
value = QgsMeshDatasetValue( x, y );
|
||||
}
|
||||
|
||||
@ -26,86 +26,7 @@
|
||||
#include "qgsvector.h"
|
||||
#include "qgspoint.h"
|
||||
#include "qgspointxy.h"
|
||||
|
||||
static void bbox2rect(
|
||||
const QgsMapToPixel &mtp,
|
||||
const QSize &outputSize,
|
||||
const QgsRectangle &bbox,
|
||||
int &leftLim, int &rightLim, int &topLim, int &bottomLim )
|
||||
{
|
||||
QgsPointXY ll = mtp.transform( bbox.xMinimum(), bbox.yMinimum() );
|
||||
QgsPointXY ur = mtp.transform( bbox.xMaximum(), bbox.yMaximum() );
|
||||
topLim = std::max( int( ur.y() ), 0 );
|
||||
bottomLim = std::min( int( ll.y() ), outputSize.height() - 1 );
|
||||
leftLim = std::max( int( ll.x() ), 0 );
|
||||
rightLim = std::min( int( ur.x() ), outputSize.width() - 1 );
|
||||
}
|
||||
|
||||
static void lamTol( double &lam )
|
||||
{
|
||||
const static double eps = 1e-6;
|
||||
if ( ( lam < 0.0 ) && ( lam > -eps ) )
|
||||
{
|
||||
lam = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool E3T_physicalToBarycentric( const QgsPointXY &pA, const QgsPointXY &pB, const QgsPointXY &pC, const QgsPointXY &pP,
|
||||
double &lam1, double &lam2, double &lam3 )
|
||||
{
|
||||
if ( pA == pB || pA == pC || pB == pC )
|
||||
return false; // this is not a valid triangle!
|
||||
|
||||
// Compute vectors
|
||||
QgsVector v0( pC - pA );
|
||||
QgsVector v1( pB - pA );
|
||||
QgsVector v2( pP - pA );
|
||||
|
||||
// Compute dot products
|
||||
double dot00 = v0 * v0;
|
||||
double dot01 = v0 * v1;
|
||||
double dot02 = v0 * v2;
|
||||
double dot11 = v1 * v1;
|
||||
double dot12 = v1 * v2;
|
||||
|
||||
// Compute barycentric coordinates
|
||||
double invDenom = 1.0 / ( dot00 * dot11 - dot01 * dot01 );
|
||||
lam1 = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
|
||||
lam2 = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;
|
||||
lam3 = 1.0 - lam1 - lam2;
|
||||
|
||||
// Apply some tolerance to lam so we can detect correctly border points
|
||||
lamTol( lam1 );
|
||||
lamTol( lam2 );
|
||||
lamTol( lam3 );
|
||||
|
||||
// Return if POI is outside triangle
|
||||
if ( ( lam1 < 0 ) || ( lam2 < 0 ) || ( lam3 < 0 ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
double QgsMeshLayerInterpolator::interpolateFromVerticesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
|
||||
double val1, double val2, double val3, const QgsPointXY &pt )
|
||||
{
|
||||
double lam1, lam2, lam3;
|
||||
if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
|
||||
return lam1 * val3 + lam2 * val2 + lam3 * val1;
|
||||
}
|
||||
|
||||
double interpolateFromFacesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3, double val, const QgsPointXY &pt )
|
||||
{
|
||||
double lam1, lam2, lam3;
|
||||
if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
|
||||
return val;
|
||||
}
|
||||
#include "qgsmeshlayerutils.h"
|
||||
|
||||
QgsMeshLayerInterpolator::QgsMeshLayerInterpolator( const QgsTriangularMesh &m,
|
||||
const QVector<double> &datasetValues, const QVector<bool> &activeFaceFlagValues,
|
||||
@ -167,16 +88,13 @@ QgsRasterBlock *QgsMeshLayerInterpolator::block( int, const QgsRectangle &extent
|
||||
if ( !isActive )
|
||||
continue;
|
||||
|
||||
QgsRectangle bbox;
|
||||
bbox.combineExtentWith( p1.x(), p1.y() );
|
||||
bbox.combineExtentWith( p2.x(), p2.y() );
|
||||
bbox.combineExtentWith( p3.x(), p3.y() );
|
||||
QgsRectangle bbox = QgsMeshLayerUtils::triangleBoundingBox( p1, p2, p3 );
|
||||
if ( !extent.intersects( bbox ) )
|
||||
continue;
|
||||
|
||||
// Get the BBox of the element in pixels
|
||||
int topLim, bottomLim, leftLim, rightLim;
|
||||
bbox2rect( mContext.mapToPixel(), mOutputSize, bbox, leftLim, rightLim, topLim, bottomLim );
|
||||
QgsMeshLayerUtils::boundingBoxToScreenRectangle( mContext.mapToPixel(), mOutputSize, bbox, leftLim, rightLim, topLim, bottomLim );
|
||||
|
||||
// interpolate in the bounding box of the face
|
||||
for ( int j = topLim; j <= bottomLim; j++ )
|
||||
@ -187,7 +105,7 @@ QgsRasterBlock *QgsMeshLayerInterpolator::block( int, const QgsRectangle &extent
|
||||
double val;
|
||||
const QgsPointXY p = mContext.mapToPixel().toMapCoordinates( k, j );
|
||||
if ( mDataOnVertices )
|
||||
val = interpolateFromVerticesData(
|
||||
val = QgsMeshLayerUtils::interpolateFromVerticesData(
|
||||
p1,
|
||||
p2,
|
||||
p3,
|
||||
@ -198,7 +116,7 @@ QgsRasterBlock *QgsMeshLayerInterpolator::block( int, const QgsRectangle &extent
|
||||
else
|
||||
{
|
||||
int face = mTriangularMesh.trianglesToNativeFaces()[i];
|
||||
val = interpolateFromFacesData(
|
||||
val = QgsMeshLayerUtils::interpolateFromFacesData(
|
||||
p1,
|
||||
p2,
|
||||
p3,
|
||||
|
||||
@ -60,22 +60,6 @@ class QgsMeshLayerInterpolator : public QgsRasterInterface
|
||||
int bandCount() const override;
|
||||
QgsRasterBlock *block( int, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback = nullptr ) override;
|
||||
|
||||
/*
|
||||
* Interpolates value based on known values on the vertices of a triangle
|
||||
* \param p1 first vertex of the triangle
|
||||
* \param p2 second vertex of the triangle
|
||||
* \param p3 third vertex of the triangle
|
||||
* \param val1 value on p1 of the triangle
|
||||
* \param val2 value on p2 of the triangle
|
||||
* \param val3 value on p3 of the triangle
|
||||
* \param pt point where to calculate value
|
||||
* \returns value on the point pt or NaN in case the point is outside the triangle
|
||||
*/
|
||||
static double interpolateFromVerticesData(
|
||||
const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
|
||||
double val1, double val2, double val3, const QgsPointXY &pt
|
||||
);
|
||||
|
||||
private:
|
||||
const QgsTriangularMesh &mTriangularMesh;
|
||||
const QVector<double> &mDatasetValues;
|
||||
|
||||
@ -116,4 +116,96 @@ void QgsMeshLayerUtils::calculateMinMaxForDataset( double &min, double &max, Qgs
|
||||
|
||||
}
|
||||
|
||||
void QgsMeshLayerUtils::boundingBoxToScreenRectangle( const QgsMapToPixel &mtp,
|
||||
const QSize &outputSize,
|
||||
const QgsRectangle &bbox,
|
||||
int &leftLim,
|
||||
int &rightLim,
|
||||
int &topLim,
|
||||
int &bottomLim )
|
||||
{
|
||||
QgsPointXY ll = mtp.transform( bbox.xMinimum(), bbox.yMinimum() );
|
||||
QgsPointXY ur = mtp.transform( bbox.xMaximum(), bbox.yMaximum() );
|
||||
topLim = std::max( int( ur.y() ), 0 );
|
||||
bottomLim = std::min( int( ll.y() ), outputSize.height() - 1 );
|
||||
leftLim = std::max( int( ll.x() ), 0 );
|
||||
rightLim = std::min( int( ur.x() ), outputSize.width() - 1 );
|
||||
}
|
||||
|
||||
static void lamTol( double &lam )
|
||||
{
|
||||
const static double eps = 1e-6;
|
||||
if ( ( lam < 0.0 ) && ( lam > -eps ) )
|
||||
{
|
||||
lam = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool E3T_physicalToBarycentric( const QgsPointXY &pA, const QgsPointXY &pB, const QgsPointXY &pC, const QgsPointXY &pP,
|
||||
double &lam1, double &lam2, double &lam3 )
|
||||
{
|
||||
if ( pA == pB || pA == pC || pB == pC )
|
||||
return false; // this is not a valid triangle!
|
||||
|
||||
// Compute vectors
|
||||
QgsVector v0( pC - pA );
|
||||
QgsVector v1( pB - pA );
|
||||
QgsVector v2( pP - pA );
|
||||
|
||||
// Compute dot products
|
||||
double dot00 = v0 * v0;
|
||||
double dot01 = v0 * v1;
|
||||
double dot02 = v0 * v2;
|
||||
double dot11 = v1 * v1;
|
||||
double dot12 = v1 * v2;
|
||||
|
||||
// Compute barycentric coordinates
|
||||
double invDenom = 1.0 / ( dot00 * dot11 - dot01 * dot01 );
|
||||
lam1 = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
|
||||
lam2 = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;
|
||||
lam3 = 1.0 - lam1 - lam2;
|
||||
|
||||
// Apply some tolerance to lam so we can detect correctly border points
|
||||
lamTol( lam1 );
|
||||
lamTol( lam2 );
|
||||
lamTol( lam3 );
|
||||
|
||||
// Return if POI is outside triangle
|
||||
if ( ( lam1 < 0 ) || ( lam2 < 0 ) || ( lam3 < 0 ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
double QgsMeshLayerUtils::interpolateFromVerticesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
|
||||
double val1, double val2, double val3, const QgsPointXY &pt )
|
||||
{
|
||||
double lam1, lam2, lam3;
|
||||
if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
|
||||
return lam1 * val3 + lam2 * val2 + lam3 * val1;
|
||||
}
|
||||
|
||||
double QgsMeshLayerUtils::interpolateFromFacesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
|
||||
double val, const QgsPointXY &pt )
|
||||
{
|
||||
double lam1, lam2, lam3;
|
||||
if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
QgsRectangle QgsMeshLayerUtils::triangleBoundingBox( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3 )
|
||||
{
|
||||
QgsRectangle bbox;
|
||||
bbox.combineExtentWith( p1.x(), p1.y() );
|
||||
bbox.combineExtentWith( p2.x(), p2.y() );
|
||||
bbox.combineExtentWith( p3.x(), p3.y() );
|
||||
return bbox;
|
||||
}
|
||||
|
||||
///@endcond
|
||||
|
||||
@ -21,11 +21,14 @@
|
||||
#define SIP_NO_FILE
|
||||
|
||||
#include "qgis_core.h"
|
||||
#include "qgsrectangle.h"
|
||||
#include "qgsmaptopixel.h"
|
||||
|
||||
class QgsMeshDataProvider;
|
||||
class QgsMeshDatasetIndex;
|
||||
|
||||
#include <QVector>
|
||||
#include <QSize>
|
||||
|
||||
///@cond PRIVATE
|
||||
|
||||
@ -57,6 +60,60 @@ class CORE_EXPORT QgsMeshLayerUtils
|
||||
* Ignores any NaN values in the input. Returns NaN for min/max on error.
|
||||
*/
|
||||
static void calculateMinMaxForDataset( double &min, double &max, QgsMeshDataProvider *provider, QgsMeshDatasetIndex index );
|
||||
|
||||
/**
|
||||
* Transformes the bounding box to rectangle in screen coordinates (in pixels)
|
||||
* \param mtp actual renderer map to pixel
|
||||
* \param outputSize actual renderer output size
|
||||
* \param bbox bounding box in map coordinates
|
||||
* \param leftLim minimum x coordinate in pixel
|
||||
* \param rightLim maximum x coordinate in pixel
|
||||
* \param topLim minimum y coordinate in pixel
|
||||
* \param bottomLim maximum y coordinate in pixel
|
||||
*/
|
||||
static void boundingBoxToScreenRectangle(
|
||||
const QgsMapToPixel &mtp,
|
||||
const QSize &outputSize,
|
||||
const QgsRectangle &bbox,
|
||||
int &leftLim, int &rightLim, int &topLim, int &bottomLim );
|
||||
|
||||
/**
|
||||
* Interpolates value based on known values on the vertices of a triangle
|
||||
* \param p1 first vertex of the triangle
|
||||
* \param p2 second vertex of the triangle
|
||||
* \param p3 third vertex of the triangle
|
||||
* \param val1 value on p1 of the triangle
|
||||
* \param val2 value on p2 of the triangle
|
||||
* \param val3 value on p3 of the triangle
|
||||
* \param pt point where to calculate value
|
||||
* \returns value on the point pt or NaN in case the point is outside the triangle
|
||||
*/
|
||||
static double interpolateFromVerticesData(
|
||||
const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
|
||||
double val1, double val2, double val3, const QgsPointXY &pt
|
||||
);
|
||||
|
||||
/**
|
||||
* Interpolate value based on known value on the face of a triangle
|
||||
* \param p1 first vertex of the triangle
|
||||
* \param p2 second vertex of the triangle
|
||||
* \param p3 third vertex of the triangle
|
||||
* \param val face value
|
||||
* \param pt point where to calculate value
|
||||
* \returns value on the point pt or NaN in case the point is outside the triangle
|
||||
*/
|
||||
static double interpolateFromFacesData(
|
||||
const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
|
||||
double val, const QgsPointXY &pt );
|
||||
|
||||
/**
|
||||
* Calculates the bounding box of the triangle
|
||||
* \param p1 first vertex of the triangle
|
||||
* \param p2 second vertex of the triangle
|
||||
* \param p3 third vertex of the triangle
|
||||
* \returns bounding box of the triangle
|
||||
*/
|
||||
static QgsRectangle triangleBoundingBox( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3 );
|
||||
};
|
||||
|
||||
///@endcond
|
||||
|
||||
@ -16,7 +16,6 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgsmeshrenderersettings.h"
|
||||
|
||||
#include "qgssymbollayerutils.h"
|
||||
|
||||
|
||||
@ -52,18 +51,18 @@ void QgsMeshRendererMeshSettings::setColor( const QColor &color )
|
||||
|
||||
QDomElement QgsMeshRendererMeshSettings::writeXml( QDomDocument &doc ) const
|
||||
{
|
||||
QDomElement elem = doc.createElement( "mesh-settings" );
|
||||
elem.setAttribute( "enabled", mEnabled ? "1" : "0" );
|
||||
elem.setAttribute( "line-width", mLineWidth );
|
||||
elem.setAttribute( "color", QgsSymbolLayerUtils::encodeColor( mColor ) );
|
||||
QDomElement elem = doc.createElement( QStringLiteral( "mesh-settings" ) );
|
||||
elem.setAttribute( QStringLiteral( "enabled" ), mEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
|
||||
elem.setAttribute( QStringLiteral( "line-width" ), mLineWidth );
|
||||
elem.setAttribute( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( mColor ) );
|
||||
return elem;
|
||||
}
|
||||
|
||||
void QgsMeshRendererMeshSettings::readXml( const QDomElement &elem )
|
||||
{
|
||||
mEnabled = elem.attribute( "enabled" ).toInt();
|
||||
mLineWidth = elem.attribute( "line-width" ).toDouble();
|
||||
mColor = QgsSymbolLayerUtils::decodeColor( elem.attribute( "color" ) );
|
||||
mEnabled = elem.attribute( QStringLiteral( "enabled" ) ).toInt();
|
||||
mLineWidth = elem.attribute( QStringLiteral( "line-width" ) ).toDouble();
|
||||
mColor = QgsSymbolLayerUtils::decodeColor( elem.attribute( QStringLiteral( "color" ) ) );
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
@ -94,10 +93,10 @@ void QgsMeshRendererScalarSettings::setOpacity( double opacity ) { mOpacity = op
|
||||
|
||||
QDomElement QgsMeshRendererScalarSettings::writeXml( QDomDocument &doc ) const
|
||||
{
|
||||
QDomElement elem = doc.createElement( "scalar-settings" );
|
||||
elem.setAttribute( "min-val", mClassificationMinimum );
|
||||
elem.setAttribute( "max-val", mClassificationMaximum );
|
||||
elem.setAttribute( "opacity", mOpacity );
|
||||
QDomElement elem = doc.createElement( QStringLiteral( "scalar-settings" ) );
|
||||
elem.setAttribute( QStringLiteral( "min-val" ), mClassificationMinimum );
|
||||
elem.setAttribute( QStringLiteral( "max-val" ), mClassificationMaximum );
|
||||
elem.setAttribute( QStringLiteral( "opacity" ), mOpacity );
|
||||
QDomElement elemShader = mColorRampShader.writeXml( doc );
|
||||
elem.appendChild( elemShader );
|
||||
return elem;
|
||||
@ -105,9 +104,9 @@ QDomElement QgsMeshRendererScalarSettings::writeXml( QDomDocument &doc ) const
|
||||
|
||||
void QgsMeshRendererScalarSettings::readXml( const QDomElement &elem )
|
||||
{
|
||||
mClassificationMinimum = elem.attribute( "min-val" ).toDouble();
|
||||
mClassificationMaximum = elem.attribute( "max-val" ).toDouble();
|
||||
mOpacity = elem.attribute( "opacity" ).toDouble();
|
||||
mClassificationMinimum = elem.attribute( QStringLiteral( "min-val" ) ).toDouble();
|
||||
mClassificationMaximum = elem.attribute( QStringLiteral( "max-val" ) ).toDouble();
|
||||
mOpacity = elem.attribute( QStringLiteral( "opacity" ) ).toDouble();
|
||||
QDomElement elemShader = elem.firstChildElement( QStringLiteral( "colorrampshader" ) );
|
||||
mColorRampShader.readXml( elemShader );
|
||||
}
|
||||
@ -224,65 +223,101 @@ void QgsMeshRendererVectorSettings::setArrowHeadLengthRatio( double vectorHeadLe
|
||||
mArrowHeadLengthRatio = vectorHeadLengthRatio;
|
||||
}
|
||||
|
||||
bool QgsMeshRendererVectorSettings::isOnUserDefinedGrid() const
|
||||
{
|
||||
return mOnUserDefinedGrid;
|
||||
}
|
||||
|
||||
void QgsMeshRendererVectorSettings::setOnUserDefinedGrid( bool enabled )
|
||||
{
|
||||
mOnUserDefinedGrid = enabled;
|
||||
}
|
||||
|
||||
int QgsMeshRendererVectorSettings::userGridCellWidth() const
|
||||
{
|
||||
return mUserGridCellWidth;
|
||||
}
|
||||
|
||||
void QgsMeshRendererVectorSettings::setUserGridCellWidth( int width )
|
||||
{
|
||||
mUserGridCellWidth = width;
|
||||
}
|
||||
|
||||
int QgsMeshRendererVectorSettings::userGridCellHeight() const
|
||||
{
|
||||
return mUserGridCellHeight;
|
||||
}
|
||||
|
||||
void QgsMeshRendererVectorSettings::setUserGridCellHeight( int height )
|
||||
{
|
||||
mUserGridCellHeight = height;
|
||||
}
|
||||
|
||||
QDomElement QgsMeshRendererVectorSettings::writeXml( QDomDocument &doc ) const
|
||||
{
|
||||
QDomElement elem = doc.createElement( "vector-settings" );
|
||||
elem.setAttribute( "line-width", mLineWidth );
|
||||
elem.setAttribute( "color", QgsSymbolLayerUtils::encodeColor( mColor ) );
|
||||
elem.setAttribute( "filter-min", mFilterMin );
|
||||
elem.setAttribute( "filter-max", mFilterMax );
|
||||
elem.setAttribute( "arrow-head-width-ratio", mArrowHeadWidthRatio );
|
||||
elem.setAttribute( "arrow-head-length-ratio", mArrowHeadLengthRatio );
|
||||
QDomElement elem = doc.createElement( QStringLiteral( "vector-settings" ) );
|
||||
elem.setAttribute( QStringLiteral( "line-width" ), mLineWidth );
|
||||
elem.setAttribute( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( mColor ) );
|
||||
elem.setAttribute( QStringLiteral( "filter-min" ), mFilterMin );
|
||||
elem.setAttribute( QStringLiteral( "filter-max" ), mFilterMax );
|
||||
elem.setAttribute( QStringLiteral( "arrow-head-width-ratio" ), mArrowHeadWidthRatio );
|
||||
elem.setAttribute( QStringLiteral( "arrow-head-length-ratio" ), mArrowHeadLengthRatio );
|
||||
elem.setAttribute( QStringLiteral( "user-grid-enabled" ), mOnUserDefinedGrid ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
|
||||
elem.setAttribute( QStringLiteral( "user-grid-width" ), mUserGridCellWidth );
|
||||
elem.setAttribute( QStringLiteral( "user-grid-height" ), mUserGridCellHeight );
|
||||
|
||||
QDomElement elemShaft = doc.createElement( "shaft-length" );
|
||||
QDomElement elemShaft = doc.createElement( QStringLiteral( "shaft-length" ) );
|
||||
QString methodTxt;
|
||||
switch ( mShaftLengthMethod )
|
||||
{
|
||||
case MinMax:
|
||||
methodTxt = "minmax";
|
||||
elemShaft.setAttribute( "min", mMinShaftLength );
|
||||
elemShaft.setAttribute( "max", mMaxShaftLength );
|
||||
methodTxt = QStringLiteral( "minmax" );
|
||||
elemShaft.setAttribute( QStringLiteral( "min" ), mMinShaftLength );
|
||||
elemShaft.setAttribute( QStringLiteral( "max" ), mMaxShaftLength );
|
||||
break;
|
||||
case Scaled:
|
||||
methodTxt = "scaled";
|
||||
elemShaft.setAttribute( "scale-factor", mScaleFactor );
|
||||
methodTxt = QStringLiteral( "scaled" );
|
||||
elemShaft.setAttribute( QStringLiteral( "scale-factor" ), mScaleFactor );
|
||||
break;
|
||||
case Fixed:
|
||||
methodTxt = "fixed";
|
||||
elemShaft.setAttribute( "fixed-length", mFixedShaftLength );
|
||||
methodTxt = QStringLiteral( "fixed" ) ;
|
||||
elemShaft.setAttribute( QStringLiteral( "fixed-length" ), mFixedShaftLength );
|
||||
break;
|
||||
}
|
||||
elemShaft.setAttribute( "method", methodTxt );
|
||||
elemShaft.setAttribute( QStringLiteral( "method" ), methodTxt );
|
||||
elem.appendChild( elemShaft );
|
||||
return elem;
|
||||
}
|
||||
|
||||
void QgsMeshRendererVectorSettings::readXml( const QDomElement &elem )
|
||||
{
|
||||
mLineWidth = elem.attribute( "line-width" ).toDouble();
|
||||
mColor = QgsSymbolLayerUtils::decodeColor( elem.attribute( "color" ) );
|
||||
mFilterMin = elem.attribute( "filter-min" ).toDouble();
|
||||
mFilterMax = elem.attribute( "filter-max" ).toDouble();
|
||||
mArrowHeadWidthRatio = elem.attribute( "arrow-head-width-ratio" ).toDouble();
|
||||
mArrowHeadLengthRatio = elem.attribute( "arrow-head-length-ratio" ).toDouble();
|
||||
mLineWidth = elem.attribute( QStringLiteral( "line-width" ) ).toDouble();
|
||||
mColor = QgsSymbolLayerUtils::decodeColor( elem.attribute( QStringLiteral( "color" ) ) );
|
||||
mFilterMin = elem.attribute( QStringLiteral( "filter-min" ) ).toDouble();
|
||||
mFilterMax = elem.attribute( QStringLiteral( "filter-max" ) ).toDouble();
|
||||
mArrowHeadWidthRatio = elem.attribute( QStringLiteral( "arrow-head-width-ratio" ) ).toDouble();
|
||||
mArrowHeadLengthRatio = elem.attribute( QStringLiteral( "arrow-head-length-ratio" ) ).toDouble();
|
||||
mOnUserDefinedGrid = elem.attribute( QStringLiteral( "user-grid-enabled" ) ).toInt(); //bool
|
||||
mUserGridCellWidth = elem.attribute( QStringLiteral( "user-grid-width" ) ).toInt();
|
||||
mUserGridCellHeight = elem.attribute( QStringLiteral( "user-grid-height" ) ).toInt();
|
||||
|
||||
QDomElement elemShaft = elem.firstChildElement( "shaft-length" );
|
||||
QString methodTxt = elemShaft.attribute( "method" );
|
||||
if ( methodTxt == "minmax" )
|
||||
QDomElement elemShaft = elem.firstChildElement( QStringLiteral( "shaft-length" ) );
|
||||
QString methodTxt = elemShaft.attribute( QStringLiteral( "method" ) );
|
||||
if ( QStringLiteral( "minmax" ) == methodTxt )
|
||||
{
|
||||
mShaftLengthMethod = MinMax;
|
||||
mMinShaftLength = elemShaft.attribute( "min" ).toDouble();
|
||||
mMaxShaftLength = elemShaft.attribute( "max" ).toDouble();
|
||||
mMinShaftLength = elemShaft.attribute( QStringLiteral( "min" ) ).toDouble();
|
||||
mMaxShaftLength = elemShaft.attribute( QStringLiteral( "max" ) ).toDouble();
|
||||
}
|
||||
else if ( methodTxt == "scaled" )
|
||||
else if ( QStringLiteral( "scaled" ) == methodTxt )
|
||||
{
|
||||
mShaftLengthMethod = Scaled;
|
||||
mScaleFactor = elemShaft.attribute( "scale-factor" ).toDouble();
|
||||
mScaleFactor = elemShaft.attribute( QStringLiteral( "scale-factor" ) ).toDouble();
|
||||
}
|
||||
else // fixed
|
||||
{
|
||||
mShaftLengthMethod = Fixed;
|
||||
mFixedShaftLength = elemShaft.attribute( "fixed-length" ).toDouble();
|
||||
mFixedShaftLength = elemShaft.attribute( QStringLiteral( "fixed-length" ) ).toDouble();
|
||||
}
|
||||
}
|
||||
|
||||
@ -290,20 +325,20 @@ void QgsMeshRendererVectorSettings::readXml( const QDomElement &elem )
|
||||
|
||||
QDomElement QgsMeshRendererSettings::writeXml( QDomDocument &doc ) const
|
||||
{
|
||||
QDomElement elem = doc.createElement( "mesh-renderer-settings" );
|
||||
QDomElement elem = doc.createElement( QStringLiteral( "mesh-renderer-settings" ) );
|
||||
|
||||
QDomElement elemActiveDataset = doc.createElement( "active-dataset" );
|
||||
QDomElement elemActiveDataset = doc.createElement( QStringLiteral( "active-dataset" ) );
|
||||
if ( mActiveScalarDataset.isValid() )
|
||||
elemActiveDataset.setAttribute( "scalar", QString( "%1,%2" ).arg( mActiveScalarDataset.group() ).arg( mActiveScalarDataset.dataset() ) );
|
||||
elemActiveDataset.setAttribute( QStringLiteral( "scalar" ), QStringLiteral( "%1,%2" ).arg( mActiveScalarDataset.group() ).arg( mActiveScalarDataset.dataset() ) );
|
||||
if ( mActiveVectorDataset.isValid() )
|
||||
elemActiveDataset.setAttribute( "vector", QString( "%1,%2" ).arg( mActiveVectorDataset.group() ).arg( mActiveVectorDataset.dataset() ) );
|
||||
elemActiveDataset.setAttribute( QStringLiteral( "vector" ), QStringLiteral( "%1,%2" ).arg( mActiveVectorDataset.group() ).arg( mActiveVectorDataset.dataset() ) );
|
||||
elem.appendChild( elemActiveDataset );
|
||||
|
||||
for ( int groupIndex : mRendererScalarSettings.keys() )
|
||||
{
|
||||
const QgsMeshRendererScalarSettings &scalarSettings = mRendererScalarSettings[groupIndex];
|
||||
QDomElement elemScalar = scalarSettings.writeXml( doc );
|
||||
elemScalar.setAttribute( "group", groupIndex );
|
||||
elemScalar.setAttribute( QStringLiteral( "group" ), groupIndex );
|
||||
elem.appendChild( elemScalar );
|
||||
}
|
||||
|
||||
@ -311,16 +346,16 @@ QDomElement QgsMeshRendererSettings::writeXml( QDomDocument &doc ) const
|
||||
{
|
||||
const QgsMeshRendererVectorSettings &vectorSettings = mRendererVectorSettings[groupIndex];
|
||||
QDomElement elemVector = vectorSettings.writeXml( doc );
|
||||
elemVector.setAttribute( "group", groupIndex );
|
||||
elemVector.setAttribute( QStringLiteral( "group" ), groupIndex );
|
||||
elem.appendChild( elemVector );
|
||||
}
|
||||
|
||||
QDomElement elemNativeMesh = mRendererNativeMeshSettings.writeXml( doc );
|
||||
elemNativeMesh.setTagName( "mesh-settings-native" );
|
||||
elemNativeMesh.setTagName( QStringLiteral( "mesh-settings-native" ) );
|
||||
elem.appendChild( elemNativeMesh );
|
||||
|
||||
QDomElement elemTriangularMesh = mRendererTriangularMeshSettings.writeXml( doc );
|
||||
elemTriangularMesh.setTagName( "mesh-settings-triangular" );
|
||||
elemTriangularMesh.setTagName( QStringLiteral( "mesh-settings-triangular" ) );
|
||||
elem.appendChild( elemTriangularMesh );
|
||||
|
||||
return elem;
|
||||
@ -331,45 +366,45 @@ void QgsMeshRendererSettings::readXml( const QDomElement &elem )
|
||||
mRendererScalarSettings.clear();
|
||||
mRendererVectorSettings.clear();
|
||||
|
||||
QDomElement elemActiveDataset = elem.firstChildElement( "active-dataset" );
|
||||
if ( elemActiveDataset.hasAttribute( "scalar" ) )
|
||||
QDomElement elemActiveDataset = elem.firstChildElement( QStringLiteral( "active-dataset" ) );
|
||||
if ( elemActiveDataset.hasAttribute( QStringLiteral( "scalar" ) ) )
|
||||
{
|
||||
QStringList lst = elemActiveDataset.attribute( "scalar" ).split( QChar( ',' ) );
|
||||
QStringList lst = elemActiveDataset.attribute( QStringLiteral( "scalar" ) ).split( QChar( ',' ) );
|
||||
if ( lst.count() == 2 )
|
||||
mActiveScalarDataset = QgsMeshDatasetIndex( lst[0].toInt(), lst[1].toInt() );
|
||||
}
|
||||
if ( elemActiveDataset.hasAttribute( "vector" ) )
|
||||
if ( elemActiveDataset.hasAttribute( QStringLiteral( "vector" ) ) )
|
||||
{
|
||||
QStringList lst = elemActiveDataset.attribute( "vector" ).split( QChar( ',' ) );
|
||||
QStringList lst = elemActiveDataset.attribute( QStringLiteral( "vector" ) ).split( QChar( ',' ) );
|
||||
if ( lst.count() == 2 )
|
||||
mActiveVectorDataset = QgsMeshDatasetIndex( lst[0].toInt(), lst[1].toInt() );
|
||||
}
|
||||
|
||||
QDomElement elemScalar = elem.firstChildElement( "scalar-settings" );
|
||||
QDomElement elemScalar = elem.firstChildElement( QStringLiteral( "scalar-settings" ) );
|
||||
while ( !elemScalar.isNull() )
|
||||
{
|
||||
int groupIndex = elemScalar.attribute( "group" ).toInt();
|
||||
int groupIndex = elemScalar.attribute( QStringLiteral( "group" ) ).toInt();
|
||||
QgsMeshRendererScalarSettings scalarSettings;
|
||||
scalarSettings.readXml( elemScalar );
|
||||
mRendererScalarSettings.insert( groupIndex, scalarSettings );
|
||||
|
||||
elemScalar = elemScalar.nextSiblingElement( "scalar-settings" );
|
||||
elemScalar = elemScalar.nextSiblingElement( QStringLiteral( "scalar-settings" ) );
|
||||
}
|
||||
|
||||
QDomElement elemVector = elem.firstChildElement( "vector-settings" );
|
||||
QDomElement elemVector = elem.firstChildElement( QStringLiteral( "vector-settings" ) );
|
||||
while ( !elemVector.isNull() )
|
||||
{
|
||||
int groupIndex = elemVector.attribute( "group" ).toInt();
|
||||
int groupIndex = elemVector.attribute( QStringLiteral( "group" ) ).toInt();
|
||||
QgsMeshRendererVectorSettings vectorSettings;
|
||||
vectorSettings.readXml( elemVector );
|
||||
mRendererVectorSettings.insert( groupIndex, vectorSettings );
|
||||
|
||||
elemVector = elemVector.nextSiblingElement( "vector-settings" );
|
||||
elemVector = elemVector.nextSiblingElement( QStringLiteral( "vector-settings" ) );
|
||||
}
|
||||
|
||||
QDomElement elemNativeMesh = elem.firstChildElement( "mesh-settings-native" );
|
||||
QDomElement elemNativeMesh = elem.firstChildElement( QStringLiteral( "mesh-settings-native" ) );
|
||||
mRendererNativeMeshSettings.readXml( elemNativeMesh );
|
||||
|
||||
QDomElement elemTriangularMesh = elem.firstChildElement( "mesh-settings-triangular" );
|
||||
QDomElement elemTriangularMesh = elem.firstChildElement( QStringLiteral( "mesh-settings-triangular" ) );
|
||||
mRendererTriangularMeshSettings.readXml( elemTriangularMesh );
|
||||
}
|
||||
|
||||
@ -248,6 +248,19 @@ class CORE_EXPORT QgsMeshRendererVectorSettings
|
||||
//! Sets ratio of the head length of the arrow (range 0-1)
|
||||
void setArrowHeadLengthRatio( double arrowHeadLengthRatio );
|
||||
|
||||
//! Returns whether vectors are drawn on user-defined grid
|
||||
bool isOnUserDefinedGrid() const;
|
||||
//! Toggles drawing of vectors on user defined grid
|
||||
void setOnUserDefinedGrid( bool enabled );
|
||||
//! Returns width in pixels of user grid cell
|
||||
int userGridCellWidth() const;
|
||||
//! Sets width of user grid cell (in pixels)
|
||||
void setUserGridCellWidth( int width );
|
||||
//! Returns height in pixels of user grid cell
|
||||
int userGridCellHeight() const;
|
||||
//! Sets height of user grid cell (in pixels)
|
||||
void setUserGridCellHeight( int height );
|
||||
|
||||
//! Writes configuration to a new DOM element
|
||||
QDomElement writeXml( QDomDocument &doc ) const;
|
||||
//! Reads configuration from the given DOM element
|
||||
@ -265,6 +278,9 @@ class CORE_EXPORT QgsMeshRendererVectorSettings
|
||||
double mFixedShaftLength = 20; //in milimeters
|
||||
double mArrowHeadWidthRatio = 0.15;
|
||||
double mArrowHeadLengthRatio = 0.40;
|
||||
bool mOnUserDefinedGrid = false;
|
||||
int mUserGridCellWidth = 10; // in pixels
|
||||
int mUserGridCellHeight = 10; // in pixels
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
#include "qgscoordinatetransform.h"
|
||||
#include "qgsmaptopixel.h"
|
||||
#include "qgsunittypes.h"
|
||||
#include "qgsmeshlayerutils.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
@ -91,7 +92,9 @@ void QgsMeshVectorRenderer::draw()
|
||||
pen.setColor( mCfg.color() );
|
||||
painter->setPen( pen );
|
||||
|
||||
if ( mDataOnVertices )
|
||||
if ( mCfg.isOnUserDefinedGrid() )
|
||||
drawVectorDataOnGrid();
|
||||
else if ( mDataOnVertices )
|
||||
drawVectorDataOnVertices();
|
||||
else
|
||||
drawVectorDataOnFaces();
|
||||
@ -234,6 +237,94 @@ void QgsMeshVectorRenderer::drawVectorDataOnFaces()
|
||||
}
|
||||
}
|
||||
|
||||
void QgsMeshVectorRenderer::drawVectorDataOnGrid()
|
||||
{
|
||||
int cellx = mCfg.userGridCellWidth();
|
||||
int celly = mCfg.userGridCellHeight();
|
||||
|
||||
const QVector<QgsMeshFace> &triangles = mTriangularMesh.triangles();
|
||||
const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices();
|
||||
|
||||
for ( int i = 0; i < triangles.size(); i++ )
|
||||
{
|
||||
const QgsMeshFace &face = triangles[i];
|
||||
|
||||
const int v1 = face[0], v2 = face[1], v3 = face[2];
|
||||
const QgsPoint p1 = vertices[v1], p2 = vertices[v2], p3 = vertices[v3];
|
||||
|
||||
const int nativeFaceIndex = mTriangularMesh.trianglesToNativeFaces()[i];
|
||||
|
||||
QgsRectangle bbox = QgsMeshLayerUtils::triangleBoundingBox( p1, p2, p3 );
|
||||
if ( !mContext.extent().intersects( bbox ) )
|
||||
continue;
|
||||
|
||||
// Get the BBox of the element in pixels
|
||||
int left, right, top, bottom;
|
||||
QgsMeshLayerUtils::boundingBoxToScreenRectangle( mContext.mapToPixel(), mOutputSize, bbox, left, right, top, bottom );
|
||||
|
||||
// Align rect to the grid (e.g. interval <13, 36> with grid cell 10 will be trimmed to <20,30>
|
||||
if ( left % cellx != 0 )
|
||||
left += cellx - ( left % cellx );
|
||||
if ( right % cellx != 0 )
|
||||
right -= ( right % cellx );
|
||||
if ( top % celly != 0 )
|
||||
top += celly - ( top % celly );
|
||||
if ( bottom % celly != 0 )
|
||||
bottom -= ( bottom % celly );
|
||||
|
||||
for ( int y = top; y <= bottom; y += celly )
|
||||
{
|
||||
for ( int x = left; x <= right; x += cellx )
|
||||
{
|
||||
QgsMeshDatasetValue val;
|
||||
const QgsPointXY p = mContext.mapToPixel().toMapCoordinates( x, y );
|
||||
|
||||
if ( mDataOnVertices )
|
||||
{
|
||||
val.setX(
|
||||
QgsMeshLayerUtils::interpolateFromVerticesData(
|
||||
p1, p2, p3,
|
||||
mDatasetValuesX[v1],
|
||||
mDatasetValuesX[v2],
|
||||
mDatasetValuesX[v3],
|
||||
p )
|
||||
);
|
||||
val.setY(
|
||||
QgsMeshLayerUtils::interpolateFromVerticesData(
|
||||
p1, p2, p3,
|
||||
mDatasetValuesY[v1],
|
||||
mDatasetValuesY[v2],
|
||||
mDatasetValuesY[v3],
|
||||
p )
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
val.setX(
|
||||
QgsMeshLayerUtils::interpolateFromFacesData(
|
||||
p1, p2, p3,
|
||||
mDatasetValuesX[nativeFaceIndex],
|
||||
p
|
||||
)
|
||||
);
|
||||
val.setY(
|
||||
QgsMeshLayerUtils::interpolateFromFacesData(
|
||||
p1, p2, p3,
|
||||
mDatasetValuesY[nativeFaceIndex],
|
||||
p
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( nodataValue( val.x(), val.y() ) )
|
||||
continue;
|
||||
|
||||
QgsPointXY lineStart( x, y );
|
||||
drawVectorArrow( lineStart, val.x(), val.y(), val.scalar() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QgsMeshVectorRenderer::drawVectorArrow( const QgsPointXY &lineStart, double xVal, double yVal, double magnitude )
|
||||
{
|
||||
|
||||
@ -68,6 +68,8 @@ class QgsMeshVectorRenderer
|
||||
void drawVectorDataOnVertices();
|
||||
//! Draws for data defined on face centers
|
||||
void drawVectorDataOnFaces();
|
||||
//! Draws data on user-defined grid
|
||||
void drawVectorDataOnGrid();
|
||||
//! Draws arrow from start point and vector data
|
||||
void drawVectorArrow( const QgsPointXY &lineStart, double xVal, double yVal, double magnitude );
|
||||
//! Calculates the end point of the arrow based on start point and vector data
|
||||
|
||||
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>310</width>
|
||||
<height>419</height>
|
||||
<width>287</width>
|
||||
<height>520</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -61,6 +61,90 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="mDisplayVectorsOnGridGroupBox">
|
||||
<property name="title">
|
||||
<string>Display Vectors on User Grid</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="xSpacingLabel">
|
||||
<property name="text">
|
||||
<string>X Spacing</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="ySpacingLabel">
|
||||
<property name="text">
|
||||
<string>Y Spacing</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QgsSpinBox" name="mXSpacingSpinBox">
|
||||
<property name="suffix">
|
||||
<string> px</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>8000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>10</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="mYSpacingSpinBox">
|
||||
<property name="suffix">
|
||||
<string>px</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>5000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>10</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="headOptionsGroupBox">
|
||||
<property name="title">
|
||||
@ -216,16 +300,21 @@
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QgsColorButton</class>
|
||||
<extends>QPushButton</extends>
|
||||
<header>qgscolorbutton.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsDoubleSpinBox</class>
|
||||
<extends>QDoubleSpinBox</extends>
|
||||
<header>qgsdoublespinbox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsColorButton</class>
|
||||
<extends>QFrame</extends>
|
||||
<header>qgscolorbutton.h</header>
|
||||
<container>1</container>
|
||||
<class>QgsSpinBox</class>
|
||||
<extends>QSpinBox</extends>
|
||||
<header>qgsspinbox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
|
||||
@ -73,6 +73,8 @@ class TestQgsMeshRenderer : public QObject
|
||||
void test_face_scalar_dataset_rendering();
|
||||
void test_face_vector_dataset_rendering();
|
||||
void test_vertex_scalar_dataset_with_inactive_face_rendering();
|
||||
void test_face_vector_on_user_grid();
|
||||
void test_vertex_vector_on_user_grid();
|
||||
|
||||
void test_signals();
|
||||
};
|
||||
@ -266,6 +268,44 @@ void TestQgsMeshRenderer::test_vertex_scalar_dataset_with_inactive_face_renderin
|
||||
QVERIFY( imageCheck( "quad_and_triangle_vertex_scalar_dataset_with_inactive_face", mMdalLayer ) );
|
||||
}
|
||||
|
||||
void TestQgsMeshRenderer::test_face_vector_on_user_grid()
|
||||
{
|
||||
QgsMeshDatasetIndex ds( 3, 0 );
|
||||
const QgsMeshDatasetGroupMetadata metadata = mMemoryLayer->dataProvider()->datasetGroupMetadata( ds );
|
||||
QVERIFY( metadata.name() == "FaceVectorDataset" );
|
||||
|
||||
QgsMeshRendererSettings rendererSettings = mMemoryLayer->rendererSettings();
|
||||
QgsMeshRendererVectorSettings settings = rendererSettings.vectorSettings( ds.group() );
|
||||
settings.setOnUserDefinedGrid( true );
|
||||
settings.setUserGridCellWidth( 30 );
|
||||
settings.setUserGridCellHeight( 20 );
|
||||
settings.setLineWidth( 0.8 );
|
||||
rendererSettings.setVectorSettings( ds.group(), settings );
|
||||
rendererSettings.setActiveVectorDataset( ds );
|
||||
mMemoryLayer->setRendererSettings( rendererSettings );
|
||||
|
||||
QVERIFY( imageCheck( "quad_and_triangle_face_vector_user_grid_dataset", mMemoryLayer ) );
|
||||
}
|
||||
|
||||
void TestQgsMeshRenderer::test_vertex_vector_on_user_grid()
|
||||
{
|
||||
QgsMeshDatasetIndex ds( 1, 0 );
|
||||
const QgsMeshDatasetGroupMetadata metadata = mMemoryLayer->dataProvider()->datasetGroupMetadata( ds );
|
||||
QVERIFY( metadata.name() == "VertexVectorDataset" );
|
||||
|
||||
QgsMeshRendererSettings rendererSettings = mMemoryLayer->rendererSettings();
|
||||
QgsMeshRendererVectorSettings settings = rendererSettings.vectorSettings( ds.group() );
|
||||
settings.setOnUserDefinedGrid( true );
|
||||
settings.setUserGridCellWidth( 60 );
|
||||
settings.setUserGridCellHeight( 40 );
|
||||
settings.setLineWidth( 0.9 );
|
||||
rendererSettings.setVectorSettings( ds.group(), settings );
|
||||
rendererSettings.setActiveVectorDataset( ds );
|
||||
mMemoryLayer->setRendererSettings( rendererSettings );
|
||||
|
||||
QVERIFY( imageCheck( "quad_and_triangle_vertex_vector_user_grid_dataset", mMemoryLayer ) );
|
||||
}
|
||||
|
||||
void TestQgsMeshRenderer::test_signals()
|
||||
{
|
||||
QSignalSpy spy1( mMemoryLayer, &QgsMapLayer::rendererChanged );
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
Loading…
x
Reference in New Issue
Block a user