Skip to content
Permalink
Browse files

fix #35147 QgsMeshLayer map canvas rotation rendering (#38795)

fix #35147 QgsMeshLayer map canvas rotation rendering
  • Loading branch information
PeterPetrik committed Sep 17, 2020
1 parent 5ca4ef8 commit fce4b276441747b977e3adcc6fd97fa2d204ba77
Showing with 106 additions and 48 deletions.
  1. +1 −1 python/core/auto_generated/qgsmaptopixel.sip.in
  2. +5 −6 src/core/mesh/qgsmeshlayerrenderer.cpp
  3. +33 −13 src/core/mesh/qgsmeshlayerutils.cpp
  4. +18 −5 src/core/mesh/qgsmeshlayerutils.h
  5. +16 −16 src/core/mesh/qgsmeshtracerenderer.cpp
  6. +4 −1 src/core/mesh/qgsmeshtracerenderer.h
  7. +8 −2 src/core/mesh/qgsmeshvectorrenderer.cpp
  8. +1 −1 src/core/qgsmaptopixel.h
  9. +20 −3 tests/src/core/testqgsmeshlayerrenderer.cpp
  10. BIN ...h/expected_lines_edge_scalar_dataset_rotated_45/expected_lines_edge_scalar_dataset_rotated_45.png
  11. BIN ...h/expected_lines_edge_vector_dataset_rotated_45/expected_lines_edge_vector_dataset_rotated_45.png
  12. BIN ...pected_lines_vertex_scalar_dataset_rotated_45/expected_lines_vertex_scalar_dataset_rotated_45.png
  13. BIN ...pected_lines_vertex_vector_dataset_rotated_45/expected_lines_vertex_vector_dataset_rotated_45.png
  14. BIN ...ngle_face_scalar_dataset_rotated_45/expected_quad_and_triangle_face_scalar_dataset_rotated_45.png
  15. BIN ...ngle_face_vector_dataset_rotated_45/expected_quad_and_triangle_face_vector_dataset_rotated_45.png
  16. BIN ...r_grid_dataset_rotated_45/expected_quad_and_triangle_face_vector_user_grid_dataset_rotated_45.png
  17. BIN ...es_rotated_45/expected_quad_and_triangle_face_vector_user_grid_dataset_streamlines_rotated_45.png
  18. BIN ...ed_quad_and_triangle_native_mesh_rotated_45/expected_quad_and_triangle_native_mesh_rotated_45.png
  19. BIN ...and_triangle_triangular_mesh_rotated_45/expected_quad_and_triangle_triangular_mesh_rotated_45.png
  20. BIN ..._vertex_scalar_dataset_rotated_45/expected_quad_and_triangle_vertex_scalar_dataset_rotated_45.png
  21. BIN ..._vertex_vector_dataset_rotated_45/expected_quad_and_triangle_vertex_vector_dataset_rotated_45.png
  22. BIN ...le_vertex_vector_traces_rotated_45/expected_quad_and_triangle_vertex_vector_traces_rotated_45.png
  23. BIN ...grid_dataset_rotated_45/expected_quad_and_triangle_vertex_vector_user_grid_dataset_rotated_45.png
  24. BIN ..._rotated_45/expected_quad_and_triangle_vertex_vector_user_grid_dataset_streamlines_rotated_45.png
@@ -161,7 +161,7 @@ Set map rotation in degrees (clockwise)

double mapRotation() const;
%Docstring
Returns current map rotation in degrees
Returns current map rotation in degrees (clockwise)

.. versionadded:: 2.8
%End
@@ -99,12 +99,11 @@ void QgsMeshLayerRenderer::calculateOutputSize()
{
// figure out image size
QgsRenderContext &context = *renderContext();
QgsRectangle extent = context.mapExtent();
QgsMapToPixel mapToPixel = context.mapToPixel();
QgsPointXY topleft = mapToPixel.transform( extent.xMinimum(), extent.yMaximum() );
QgsPointXY bottomright = mapToPixel.transform( extent.xMaximum(), extent.yMinimum() );
int width = int( bottomright.x() - topleft.x() );
int height = int( bottomright.y() - topleft.y() );
const QgsRectangle extent = context.mapExtent();
const QgsMapToPixel mapToPixel = context.mapToPixel();
const QgsRectangle screenBBox = QgsMeshLayerUtils::boundingBoxToScreenRectangle( mapToPixel, extent );
int width = int( screenBBox.width() );
int height = int( screenBBox.height() );
mOutputSize = QSize( width, height );
}

@@ -197,20 +197,40 @@ QVector<double> QgsMeshLayerUtils::calculateMagnitudes( const QgsMeshDataBlock &
return ret;
}

void QgsMeshLayerUtils::boundingBoxToScreenRectangle( const QgsMapToPixel &mtp,
const QSize &outputSize,
const QgsRectangle &bbox,
int &leftLim,
int &rightLim,
int &topLim,
int &bottomLim )
QgsRectangle QgsMeshLayerUtils::boundingBoxToScreenRectangle(
const QgsMapToPixel &mtp,
const QgsRectangle &bbox
)
{
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 );
const QgsPointXY topLeft = mtp.transform( bbox.xMinimum(), bbox.yMaximum() );
const QgsPointXY topRight = mtp.transform( bbox.xMaximum(), bbox.yMaximum() );
const QgsPointXY bottomLeft = mtp.transform( bbox.xMinimum(), bbox.yMinimum() );
const QgsPointXY bottomRight = mtp.transform( bbox.xMaximum(), bbox.yMinimum() );

double xMin = std::min( {topLeft.x(), topRight.x(), bottomLeft.x(), bottomRight.x()} );
double xMax = std::max( {topLeft.x(), topRight.x(), bottomLeft.x(), bottomRight.x()} );
double yMin = std::min( {topLeft.y(), topRight.y(), bottomLeft.y(), bottomRight.y()} );
double yMax = std::max( {topLeft.y(), topRight.y(), bottomLeft.y(), bottomRight.y()} );

QgsRectangle ret( xMin, yMin, xMax, yMax );
return ret;
}

void QgsMeshLayerUtils::boundingBoxToScreenRectangle(
const QgsMapToPixel &mtp,
const QSize &outputSize,
const QgsRectangle &bbox,
int &leftLim,
int &rightLim,
int &bottomLim,
int &topLim )
{
const QgsRectangle screenBBox = boundingBoxToScreenRectangle( mtp, bbox );

bottomLim = std::max( int( screenBBox.yMinimum() ), 0 );
topLim = std::min( int( screenBBox.yMaximum() ), outputSize.height() - 1 );
leftLim = std::max( int( screenBBox.xMinimum() ), 0 );
rightLim = std::min( int( screenBBox.xMaximum() ), outputSize.width() - 1 );
}

static void lamTol( double &lam )
@@ -115,16 +115,29 @@ class CORE_EXPORT QgsMeshLayerUtils
* \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
* \param leftLim minimum x coordinate in pixel, clipped by 0
* \param rightLim maximum x coordinate in pixel, clipped by outputSize width
* \param bottomLim minimum y coordinate in pixel, clipped by 0
* \param topLim maximum y coordinate in pixel, clipped by outputSize height
*/
static void boundingBoxToScreenRectangle(
const QgsMapToPixel &mtp,
const QSize &outputSize,
const QgsRectangle &bbox,
int &leftLim, int &rightLim, int &topLim, int &bottomLim );
int &leftLim,
int &rightLim,
int &bottomLim,
int &topLim );

/**
* Transformes the bounding box to rectangle in screen coordinates (in pixels)
* \param mtp actual renderer map to pixel
* \param bbox bounding box in map coordinates
*/
static QgsRectangle boundingBoxToScreenRectangle(
const QgsMapToPixel &mtp,
const QgsRectangle &bbox
);

/**
* Interpolates value based on known values on the vertices of a edge
@@ -19,6 +19,11 @@
#include "qgsmeshlayerrenderer.h"
///@cond PRIVATE

#ifndef M_DEG2RAD
#define M_DEG2RAD 0.0174532925
#endif


QgsVector QgsMeshVectorValueInterpolator::vectorValue( const QgsPointXY &point ) const
{
if ( mCacheFaceIndex != -1 && mCacheFaceIndex < mTriangularMesh.triangles().count() )
@@ -268,7 +273,7 @@ void QgsMeshStreamField::updateSize( const QgsRenderContext &renderContext )
}
catch ( QgsCsException &cse )
{
Q_UNUSED( cse );
Q_UNUSED( cse )
//if the transform fails, consider the whole map
layerExtent = mMapExtent;
}
@@ -288,17 +293,11 @@ void QgsMeshStreamField::updateSize( const QgsRenderContext &renderContext )
return;
}

QgsPointXY interestZoneTopLeft;
QgsPointXY interestZoneBottomRight;

interestZoneTopLeft = deviceMapToPixel.transform( QgsPointXY( interestZoneExtent.xMinimum(), interestZoneExtent.yMaximum() ) );
interestZoneBottomRight = deviceMapToPixel.transform( QgsPointXY( interestZoneExtent.xMaximum(), interestZoneExtent.yMinimum() ) );


mFieldTopLeftInDeviceCoordinates = interestZoneTopLeft.toQPointF().toPoint();
QPoint mFieldBottomRightInDeviceCoordinates = interestZoneBottomRight.toQPointF().toPoint();
int fieldWidthInDeviceCoordinate = mFieldBottomRightInDeviceCoordinates.x() - mFieldTopLeftInDeviceCoordinates.x();
int fieldHeightInDeviceCoordinate = mFieldBottomRightInDeviceCoordinates.y() - mFieldTopLeftInDeviceCoordinates.y();
QgsRectangle fieldInterestZoneInDeviceCoordinates = QgsMeshLayerUtils::boundingBoxToScreenRectangle( deviceMapToPixel, interestZoneExtent );
mFieldTopLeftInDeviceCoordinates = QPoint( int( fieldInterestZoneInDeviceCoordinates.xMinimum() ), int( fieldInterestZoneInDeviceCoordinates.yMinimum() ) );
int fieldWidthInDeviceCoordinate = int( fieldInterestZoneInDeviceCoordinates.width() );
int fieldHeightInDeviceCoordinate = int ( fieldInterestZoneInDeviceCoordinates.height() );

int fieldWidth = int( fieldWidthInDeviceCoordinate / mFieldResolution );
int fieldHeight = int( fieldHeightInDeviceCoordinate / mFieldResolution );
@@ -319,10 +318,9 @@ void QgsMeshStreamField::updateSize( const QgsRenderContext &renderContext )
mFieldSize.setHeight( fieldHeight );
}


double mapUnitPerFieldPixel;
if ( interestZoneExtent.width() > 0 )
mapUnitPerFieldPixel = interestZoneExtent.width() / fieldWidthInDeviceCoordinate * mFieldResolution;
mapUnitPerFieldPixel = deviceMapToPixel.mapUnitsPerPixel() * mFieldResolution * mFieldSize.width() / ( fieldWidthInDeviceCoordinate / mFieldResolution ) ;
else
mapUnitPerFieldPixel = 1e-8;

@@ -341,7 +339,9 @@ void QgsMeshStreamField::updateSize( const QgsRenderContext &renderContext )
xc,
yc,
fieldWidth,
fieldHeight, 0 );
fieldHeight,
deviceMapToPixel.mapRotation()
);

initField();
mValid = true;
@@ -363,8 +363,6 @@ bool QgsMeshStreamField::isValid() const

void QgsMeshStreamField::addTrace( QgsPointXY startPoint )
{
QPoint sp;
sp = mMapToFieldPixel.transform( startPoint ).toQPointF().toPoint();
addTrace( mMapToFieldPixel.transform( startPoint ).toQPointF().toPoint() );
}

@@ -462,8 +460,10 @@ void QgsMeshStreamField::addTrace( QPoint startPixel )
/* nondimensional value : Vu=2 when the particle need dt=1 to go through a pixel with the mMagMax magnitude
* The nondimensional size of the side of a pixel is 2
*/
vector = vector.rotateBy( -mMapToFieldPixel.mapRotation() * M_DEG2RAD );
QgsVector vu = vector / mMaximumMagnitude * 2;
data.magnitude = vector.length();

double Vx = vu.x();
double Vy = vu.y();
double Vu = data.magnitude / mMaximumMagnitude * 2; //nondimensional vector magnitude
@@ -62,7 +62,10 @@ class QgsMeshVectorValueInterpolator
//! Destructor
virtual ~QgsMeshVectorValueInterpolator() = default;

//! Returns the interpolated vector
/**
* Returns the interpolated vector
* \param point point in map coordinates
*/
virtual QgsVector vectorValue( const QgsPointXY &point ) const;

//! Assignment operator
@@ -32,6 +32,10 @@

///@cond PRIVATE

#ifndef M_DEG2RAD
#define M_DEG2RAD 0.0174532925
#endif

inline double mag( double input )
{
if ( input < 0.0 )
@@ -146,7 +150,8 @@ bool QgsMeshVectorArrowRenderer::calcVectorLineEnd(

// Determine the angle of the vector, counter-clockwise, from east
// (and associated trigs)
double vectorAngle = -1.0 * atan( ( -1.0 * yVal ) / xVal );
double vectorAngle = -1.0 * atan( ( -1.0 * yVal ) / xVal ) - mContext.mapToPixel().mapRotation() * M_DEG2RAD;

cosAlpha = cos( vectorAngle ) * mag( xVal );
sinAlpha = sin( vectorAngle ) * mag( xVal );

@@ -487,7 +492,8 @@ QgsMeshVectorRenderer *QgsMeshVectorRenderer::makeVectorRenderer(
datasetMagMinimumValue,
dataType,
settings,
context, size );
context,
size );
break;
case QgsMeshRendererVectorSettings::Streamlines:
renderer = new QgsMeshVectorStreamlineRenderer(
@@ -169,7 +169,7 @@ class CORE_EXPORT QgsMapToPixel
void setMapRotation( double degrees, double cx, double cy );

/**
* Returns current map rotation in degrees
* Returns current map rotation in degrees (clockwise)
* \since QGIS 2.8
*/
double mapRotation() const;
@@ -65,7 +65,7 @@ class TestQgsMeshRenderer : public QObject
void cleanupTestCase();// will be called after the last testfunction was executed.
void init(); // will be called before each testfunction is executed.
void cleanup() {} // will be called after every testfunction.
bool imageCheck( const QString &testType, QgsMeshLayer *layer );
bool imageCheck( const QString &testType, QgsMeshLayer *layer, double rotation = 0.0 );
QString readFile( const QString &fname ) const;


@@ -228,12 +228,14 @@ QString TestQgsMeshRenderer::readFile( const QString &fname ) const
return uri;
}

bool TestQgsMeshRenderer::imageCheck( const QString &testType, QgsMeshLayer *layer )
bool TestQgsMeshRenderer::imageCheck( const QString &testType, QgsMeshLayer *layer, double rotation )
{
mReport += "<h2>" + testType + "</h2>\n";
mMapSettings->setExtent( layer->extent() );
mMapSettings->setDestinationCrs( layer->crs() );
mMapSettings->setExtent( layer->extent() );
mMapSettings->setRotation( rotation );
mMapSettings->setOutputDpi( 96 );

QgsRenderChecker myChecker;
myChecker.setControlPathPrefix( QStringLiteral( "mesh" ) );
myChecker.setControlName( "expected_" + testType );
@@ -253,6 +255,7 @@ void TestQgsMeshRenderer::test_native_mesh_rendering()
rendererSettings.setNativeMeshSettings( settings );
mMemoryLayer->setRendererSettings( rendererSettings );
QVERIFY( imageCheck( "quad_and_triangle_native_mesh", mMemoryLayer ) );
QVERIFY( imageCheck( "quad_and_triangle_native_mesh_rotated_45", mMemoryLayer, 45.0 ) );
}

void TestQgsMeshRenderer::test_native_mesh_renderingWithClipping()
@@ -287,6 +290,7 @@ void TestQgsMeshRenderer::test_triangular_mesh_rendering()
rendererSettings.setTriangularMeshSettings( settings );
mMemoryLayer->setRendererSettings( rendererSettings );
QVERIFY( imageCheck( "quad_and_triangle_triangular_mesh", mMemoryLayer ) );
QVERIFY( imageCheck( "quad_and_triangle_triangular_mesh_rotated_45", mMemoryLayer, 45.0 ) );
}

void TestQgsMeshRenderer::test_edge_mesh_rendering()
@@ -319,6 +323,7 @@ void TestQgsMeshRenderer::test_1d_vertex_scalar_dataset_rendering()
mMemory1DLayer->setStaticScalarDatasetIndex( ds );

QVERIFY( imageCheck( "lines_vertex_scalar_dataset", mMemory1DLayer ) );
QVERIFY( imageCheck( "lines_vertex_scalar_dataset_rotated_45", mMemory1DLayer, 45 ) );
}

void TestQgsMeshRenderer::test_1d_vertex_vector_dataset_rendering()
@@ -337,6 +342,7 @@ void TestQgsMeshRenderer::test_1d_vertex_vector_dataset_rendering()
mMemory1DLayer->setStaticVectorDatasetIndex( ds );

QVERIFY( imageCheck( "lines_vertex_vector_dataset", mMemory1DLayer ) );
QVERIFY( imageCheck( "lines_vertex_vector_dataset_rotated_45", mMemory1DLayer, 45 ) );
}

void TestQgsMeshRenderer::test_1d_edge_scalar_dataset_rendering()
@@ -357,6 +363,7 @@ void TestQgsMeshRenderer::test_1d_edge_scalar_dataset_rendering()
mMemory1DLayer->setStaticScalarDatasetIndex( ds );

QVERIFY( imageCheck( "lines_edge_scalar_dataset", mMemory1DLayer ) );
QVERIFY( imageCheck( "lines_edge_scalar_dataset_rotated_45", mMemory1DLayer, 45 ) );
}

void TestQgsMeshRenderer::test_1d_edge_vector_dataset_rendering()
@@ -370,6 +377,7 @@ void TestQgsMeshRenderer::test_1d_edge_vector_dataset_rendering()
mMemory1DLayer->setStaticVectorDatasetIndex( ds );

QVERIFY( imageCheck( "lines_edge_vector_dataset", mMemory1DLayer ) );
QVERIFY( imageCheck( "lines_edge_vector_dataset_rotated_45", mMemory1DLayer, 45 ) );
}

void TestQgsMeshRenderer::test_vertex_scalar_dataset_rendering()
@@ -383,6 +391,7 @@ void TestQgsMeshRenderer::test_vertex_scalar_dataset_rendering()
mMemoryLayer->setStaticScalarDatasetIndex( ds );

QVERIFY( imageCheck( "quad_and_triangle_vertex_scalar_dataset", mMemoryLayer ) );
QVERIFY( imageCheck( "quad_and_triangle_vertex_scalar_dataset_rotated_45", mMemoryLayer, 45.0 ) );
}

void TestQgsMeshRenderer::test_vertex_vector_dataset_rendering()
@@ -401,6 +410,7 @@ void TestQgsMeshRenderer::test_vertex_vector_dataset_rendering()
mMemoryLayer->setStaticVectorDatasetIndex( ds );

QVERIFY( imageCheck( "quad_and_triangle_vertex_vector_dataset", mMemoryLayer ) );
QVERIFY( imageCheck( "quad_and_triangle_vertex_vector_dataset_rotated_45", mMemoryLayer, 45.0 ) );
}

void TestQgsMeshRenderer::test_vertex_vector_dataset_colorRamp_rendering()
@@ -433,6 +443,7 @@ void TestQgsMeshRenderer::test_face_scalar_dataset_rendering()
mMemoryLayer->setStaticScalarDatasetIndex( ds );

QVERIFY( imageCheck( "quad_and_triangle_face_scalar_dataset", mMemoryLayer ) );
QVERIFY( imageCheck( "quad_and_triangle_face_scalar_dataset_rotated_45", mMemoryLayer, 45.0 ) );
}

void TestQgsMeshRenderer::test_face_scalar_dataset_interpolated_neighbour_average_rendering()
@@ -463,6 +474,7 @@ void TestQgsMeshRenderer::test_face_vector_dataset_rendering()
mMemoryLayer->setStaticVectorDatasetIndex( ds );

QVERIFY( imageCheck( "quad_and_triangle_face_vector_dataset", mMemoryLayer ) );
QVERIFY( imageCheck( "quad_and_triangle_face_vector_dataset_rotated_45", mMemoryLayer, 45.0 ) );
}

void TestQgsMeshRenderer::test_vertex_scalar_dataset_with_inactive_face_rendering()
@@ -496,6 +508,7 @@ void TestQgsMeshRenderer::test_face_vector_on_user_grid()
mMemoryLayer->setStaticVectorDatasetIndex( ds );

QVERIFY( imageCheck( "quad_and_triangle_face_vector_user_grid_dataset", mMemoryLayer ) );
QVERIFY( imageCheck( "quad_and_triangle_face_vector_user_grid_dataset_rotated_45", mMemoryLayer, 45.0 ) );
}

void TestQgsMeshRenderer::test_face_vector_on_user_grid_streamlines()
@@ -516,6 +529,7 @@ void TestQgsMeshRenderer::test_face_vector_on_user_grid_streamlines()
mMemoryLayer->setStaticVectorDatasetIndex( ds );

QVERIFY( imageCheck( "quad_and_triangle_face_vector_user_grid_dataset_streamlines", mMemoryLayer ) );
QVERIFY( imageCheck( "quad_and_triangle_face_vector_user_grid_dataset_streamlines_rotated_45", mMemoryLayer, 45.0 ) );
}

void TestQgsMeshRenderer::test_vertex_vector_on_user_grid()
@@ -537,6 +551,7 @@ void TestQgsMeshRenderer::test_vertex_vector_on_user_grid()
mMemoryLayer->setStaticVectorDatasetIndex( ds );

QVERIFY( imageCheck( "quad_and_triangle_vertex_vector_user_grid_dataset", mMemoryLayer ) );
QVERIFY( imageCheck( "quad_and_triangle_vertex_vector_user_grid_dataset_rotated_45", mMemoryLayer, 45.0 ) );
}

void TestQgsMeshRenderer::test_vertex_vector_on_user_grid_streamlines()
@@ -558,6 +573,7 @@ void TestQgsMeshRenderer::test_vertex_vector_on_user_grid_streamlines()
mMemoryLayer->setStaticVectorDatasetIndex( ds );

QVERIFY( imageCheck( "quad_and_triangle_vertex_vector_user_grid_dataset_streamlines", mMemoryLayer ) );
QVERIFY( imageCheck( "quad_and_triangle_vertex_vector_user_grid_dataset_streamlines_rotated_45", mMemoryLayer, 45.0 ) );
}

void TestQgsMeshRenderer::test_vertex_vector_on_user_grid_streamlines_colorRamp()
@@ -606,6 +622,7 @@ void TestQgsMeshRenderer::test_vertex_vector_traces()
mMemoryLayer->setStaticVectorDatasetIndex( ds );

QVERIFY( imageCheck( "quad_and_triangle_vertex_vector_traces", mMemoryLayer ) );
QVERIFY( imageCheck( "quad_and_triangle_vertex_vector_traces_rotated_45", mMemoryLayer, 45.0 ) );
}

void TestQgsMeshRenderer::test_vertex_vector_traces_colorRamp()
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit fce4b27

Please sign in to comment.
You can’t perform that action at this time.