26 #define M_DEG2RAD 0.0174532925
32 if ( mCacheFaceIndex != -1 && mCacheFaceIndex < mTriangularMesh.triangles().count() )
34 QgsVector res = interpolatedValuePrivate( mCacheFaceIndex, point );
35 if ( isVectorValid( res ) )
37 activeFaceFilter( res, mCacheFaceIndex );
43 QList<int> potentialFaceIndexes = mTriangularMesh.faceIndexesForRectangle(
QgsRectangle( point, point ) );
45 for (
const int faceIndex : potentialFaceIndexes )
47 QgsVector res = interpolatedValuePrivate( faceIndex, point );
48 if ( isVectorValid( res ) )
50 mCacheFaceIndex = faceIndex;
51 activeFaceFilter( res, mCacheFaceIndex );
57 return (
QgsVector( std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() ) );
61 QgsMeshVectorValueInterpolator &QgsMeshVectorValueInterpolator::operator=(
const QgsMeshVectorValueInterpolator &other )
63 mTriangularMesh = other.mTriangularMesh;
64 mDatasetValues = other.mDatasetValues;
65 mActiveFaceFlagValues = other.mActiveFaceFlagValues;
66 mFaceCache = other.mFaceCache;
67 mCacheFaceIndex = other.mCacheFaceIndex;
68 mUseScalarActiveFaceFlagValues = other.mUseScalarActiveFaceFlagValues;
73 QgsMeshVectorValueInterpolatorFromVertex::
75 QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues )
80 QgsMeshVectorValueInterpolatorFromVertex::
81 QgsMeshVectorValueInterpolatorFromVertex(
const QgsTriangularMesh &triangularMesh,
84 QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues, scalarActiveFaceFlagValues )
89 QgsMeshVectorValueInterpolatorFromVertex::QgsMeshVectorValueInterpolatorFromVertex(
const QgsMeshVectorValueInterpolatorFromVertex &other ):
90 QgsMeshVectorValueInterpolator( other )
93 QgsMeshVectorValueInterpolatorFromVertex *QgsMeshVectorValueInterpolatorFromVertex::clone()
95 return new QgsMeshVectorValueInterpolatorFromVertex( *
this );
98 QgsMeshVectorValueInterpolatorFromVertex &QgsMeshVectorValueInterpolatorFromVertex::
99 operator=(
const QgsMeshVectorValueInterpolatorFromVertex &other )
101 QgsMeshVectorValueInterpolator::operator=( other );
105 QgsVector QgsMeshVectorValueInterpolatorFromVertex::interpolatedValuePrivate(
int faceIndex,
const QgsPointXY point )
const
107 QgsMeshFace face = mTriangularMesh.triangles().at( faceIndex );
114 mDatasetValues.value( face.at( 0 ) ).y() );
117 mDatasetValues.value( face.at( 1 ) ).y() );
120 mDatasetValues.value( face.at( 2 ) ).y() );
122 return QgsMeshLayerUtils::interpolateVectorFromVerticesData(
132 QgsMeshVectorValueInterpolator::QgsMeshVectorValueInterpolator(
const QgsTriangularMesh &triangularMesh,
134 mTriangularMesh( triangularMesh ),
135 mDatasetValues( datasetVectorValues ),
136 mUseScalarActiveFaceFlagValues( false )
139 QgsMeshVectorValueInterpolator::QgsMeshVectorValueInterpolator(
const QgsTriangularMesh &triangularMesh,
142 mTriangularMesh( triangularMesh ),
143 mDatasetValues( datasetVectorValues ),
144 mActiveFaceFlagValues( scalarActiveFaceFlagValues ),
145 mUseScalarActiveFaceFlagValues( true )
148 QgsMeshVectorValueInterpolator::QgsMeshVectorValueInterpolator(
const QgsMeshVectorValueInterpolator &other ):
149 mTriangularMesh( other.mTriangularMesh ),
150 mDatasetValues( other.mDatasetValues ),
151 mActiveFaceFlagValues( other.mActiveFaceFlagValues ),
152 mFaceCache( other.mFaceCache ),
153 mCacheFaceIndex( other.mCacheFaceIndex ),
154 mUseScalarActiveFaceFlagValues( other.mUseScalarActiveFaceFlagValues )
157 void QgsMeshVectorValueInterpolator::updateCacheFaceIndex(
const QgsPointXY &point )
const
161 mCacheFaceIndex = mTriangularMesh.faceIndexForPoint_v2( point );
165 bool QgsMeshVectorValueInterpolator::isVectorValid(
const QgsVector &v )
const
167 return !( std::isnan( v.
x() ) || std::isnan( v.
y() ) );
171 void QgsMeshVectorValueInterpolator::activeFaceFilter(
QgsVector &vector,
int faceIndex )
const
173 if ( mUseScalarActiveFaceFlagValues && ! mActiveFaceFlagValues.active( mTriangularMesh.trianglesToNativeFaces()[faceIndex] ) )
174 vector =
QgsVector( std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() ) ;
177 QSize QgsMeshStreamField::size()
const
182 QPoint QgsMeshStreamField::topLeft()
const
184 return mFieldTopLeftInDeviceCoordinates;
187 int QgsMeshStreamField::resolution()
const
189 return mFieldResolution;
192 QgsPointXY QgsMeshStreamField::positionToMapCoordinates(
const QPoint &pixelPosition,
const QgsPointXY &positionInPixel )
194 QgsPointXY mapPoint = mMapToFieldPixel.toMapCoordinates( pixelPosition );
195 mapPoint = mapPoint +
QgsVector( positionInPixel.
x() * mMapToFieldPixel.mapUnitsPerPixel(),
196 positionInPixel.
y() * mMapToFieldPixel.mapUnitsPerPixel() );
200 QgsMeshStreamField::QgsMeshStreamField(
205 double magnitudeMaximum,
bool dataIsOnVertices,
209 mFieldResolution( resolution ),
210 mVectorColoring( vectorColoring ),
211 mLayerExtent( layerExtent ),
212 mMaximumMagnitude( magnitudeMaximum ),
213 mRenderContext( rendererContext )
215 if ( dataIsOnVertices )
217 if ( scalarActiveFaceFlagValues.
isValid() )
218 mVectorValueInterpolator.reset(
new QgsMeshVectorValueInterpolatorFromVertex( triangularMesh,
220 scalarActiveFaceFlagValues ) );
222 mVectorValueInterpolator.reset(
new QgsMeshVectorValueInterpolatorFromVertex( triangularMesh,
223 dataSetVectorValues ) );
227 if ( scalarActiveFaceFlagValues.
isValid() )
228 mVectorValueInterpolator.reset(
new QgsMeshVectorValueInterpolatorFromFace( triangularMesh,
230 scalarActiveFaceFlagValues ) );
232 mVectorValueInterpolator.reset(
new QgsMeshVectorValueInterpolatorFromFace( triangularMesh,
233 dataSetVectorValues ) );
237 QgsMeshStreamField::QgsMeshStreamField(
const QgsMeshStreamField &other ):
238 mFieldSize( other.mFieldSize ),
239 mFieldResolution( other.mFieldResolution ),
241 mTraceImage( other.mTraceImage ),
242 mMapToFieldPixel( other.mMapToFieldPixel ),
243 mVectorColoring( other.mVectorColoring ),
244 mPixelFillingCount( other.mPixelFillingCount ),
245 mMaxPixelFillingCount( other.mMaxPixelFillingCount ),
246 mLayerExtent( other.mLayerExtent ),
247 mMapExtent( other.mMapExtent ),
248 mFieldTopLeftInDeviceCoordinates( other.mFieldTopLeftInDeviceCoordinates ),
249 mValid( other.mValid ),
250 mMaximumMagnitude( other.mMaximumMagnitude ),
251 mPixelFillingDensity( other.mPixelFillingDensity ),
252 mMinMagFilter( other.mMinMagFilter ),
253 mMaxMagFilter( other.mMaxMagFilter ),
254 mRenderContext( other.mRenderContext ),
255 mMinimizeFieldSize( other.mMinimizeFieldSize )
257 mPainter.reset(
new QPainter( &mTraceImage ) );
258 mVectorValueInterpolator =
259 std::unique_ptr<QgsMeshVectorValueInterpolator>( other.mVectorValueInterpolator->clone() );
262 QgsMeshStreamField::~QgsMeshStreamField()
268 void QgsMeshStreamField::updateSize(
const QgsRenderContext &renderContext )
281 layerExtent = mMapExtent;
285 if ( mMinimizeFieldSize )
286 interestZoneExtent = layerExtent.
intersect( mMapExtent );
288 interestZoneExtent = mMapExtent;
293 mFieldSize = QSize();
294 mFieldTopLeftInDeviceCoordinates = QPoint();
300 QgsRectangle fieldInterestZoneInDeviceCoordinates = QgsMeshLayerUtils::boundingBoxToScreenRectangle( deviceMapToPixel, interestZoneExtent );
301 mFieldTopLeftInDeviceCoordinates = QPoint(
int( fieldInterestZoneInDeviceCoordinates.
xMinimum() ),
int( fieldInterestZoneInDeviceCoordinates.
yMinimum() ) );
302 int fieldWidthInDeviceCoordinate = int( fieldInterestZoneInDeviceCoordinates.
width() );
303 int fieldHeightInDeviceCoordinate = int ( fieldInterestZoneInDeviceCoordinates.
height() );
305 int fieldWidth = int( fieldWidthInDeviceCoordinate / mFieldResolution );
306 int fieldHeight = int( fieldHeightInDeviceCoordinate / mFieldResolution );
309 if ( fieldWidthInDeviceCoordinate % mFieldResolution > 0 )
311 if ( fieldHeightInDeviceCoordinate % mFieldResolution > 0 )
314 if ( fieldWidth == 0 || fieldHeight == 0 )
316 mFieldSize = QSize();
320 mFieldSize.setWidth( fieldWidth );
321 mFieldSize.setHeight( fieldHeight );
324 double mapUnitPerFieldPixel;
325 if ( interestZoneExtent.
width() > 0 )
326 mapUnitPerFieldPixel = deviceMapToPixel.
mapUnitsPerPixel() * mFieldResolution * mFieldSize.width() / ( fieldWidthInDeviceCoordinate / mFieldResolution ) ;
328 mapUnitPerFieldPixel = 1e-8;
330 int fieldRightDevice = mFieldTopLeftInDeviceCoordinates.x() + mFieldSize.width() * mFieldResolution;
331 int fieldBottomDevice = mFieldTopLeftInDeviceCoordinates.y() + mFieldSize.height() * mFieldResolution;
334 int fieldTopDevice = mFieldTopLeftInDeviceCoordinates.
x();
335 int fieldLeftDevice = mFieldTopLeftInDeviceCoordinates.y();
338 double xc = ( fieldRightBottomMap.
x() + fieldTopLeftMap.
x() ) / 2;
339 double yc = ( fieldTopLeftMap.
y() + fieldRightBottomMap.
y() ) / 2;
353 void QgsMeshStreamField::updateSize(
const QgsRenderContext &renderContext,
int resolution )
355 if ( renderContext.
mapExtent() == mMapExtent && resolution == mFieldResolution )
357 mFieldResolution = resolution;
359 updateSize( renderContext );
362 bool QgsMeshStreamField::isValid()
const
367 void QgsMeshStreamField::addTrace(
QgsPointXY startPoint )
369 addTrace( mMapToFieldPixel.transform( startPoint ).toQPointF().toPoint() );
373 void QgsMeshStreamField::addRandomTraces()
375 if ( mMaximumMagnitude > 0 )
376 while ( mPixelFillingCount < mMaxPixelFillingCount && !mRenderContext.renderingStopped() )
380 void QgsMeshStreamField::addRandomTrace()
385 int xRandom = 1 + std::rand() / int( ( RAND_MAX + 1u ) / uint( mFieldSize.width() ) ) ;
386 int yRandom = 1 + std::rand() / int ( ( RAND_MAX + 1u ) / uint( mFieldSize.height() ) ) ;
387 addTrace( QPoint( xRandom, yRandom ) );
390 void QgsMeshStreamField::addGriddedTraces(
int dx,
int dy )
393 while ( i < mFieldSize.width() && !mRenderContext.renderingStopped() )
396 while ( j < mFieldSize.height() && !mRenderContext.renderingStopped() )
398 addTrace( QPoint( i, j ) );
409 for (
auto f : std::as_const( facesInExtent ) )
412 for (
auto i : std::as_const( face ) )
413 vertices.insert( i );
416 for (
auto i : std::as_const( vertices ) )
418 addTrace( mesh.
vertices().at( i ) );
422 void QgsMeshStreamField::addTrace( QPoint startPixel )
428 if ( isTraceExists( startPixel ) || isTraceOutside( startPixel ) )
431 if ( !mVectorValueInterpolator )
434 if ( !( mMaximumMagnitude > 0 ) )
437 mPainter->setPen( mPen );
443 std::list<QPair<QPoint, FieldData>> chunkTrace;
445 QPoint currentPixel = startPixel;
450 while ( !mRenderContext.renderingStopped() )
453 vector = mVectorValueInterpolator->vectorValue( mapPosition ) ;
455 if ( std::isnan( vector.
x() ) || std::isnan( vector.
y() ) )
457 mPixelFillingCount++;
458 setChunkTrace( chunkTrace );
459 drawChunkTrace( chunkTrace );
467 QgsVector vu = vector / mMaximumMagnitude * 2;
468 data.magnitude = vector.
length();
472 double Vu = data.magnitude / mMaximumMagnitude * 2;
477 addPixelToChunkTrace( currentPixel, data, chunkTrace );
478 simplifyChunkTrace( chunkTrace );
479 setChunkTrace( chunkTrace );
480 drawChunkTrace( chunkTrace );
488 if ( nextPosition.
x() > 1 )
490 if ( nextPosition.
x() < -1 )
492 if ( nextPosition.
y() > 1 )
494 if ( nextPosition.
y() < -1 )
499 if ( incX != 0 || incY != 0 )
501 data.directionX = incX;
502 data.directionY = -incY;
504 if ( chunkTrace.empty() )
506 storeInField( QPair<QPoint, FieldData>( currentPixel, data ) );
508 if ( addPixelToChunkTrace( currentPixel, data, chunkTrace ) )
510 setChunkTrace( chunkTrace );
511 drawChunkTrace( chunkTrace );
512 clearChunkTrace( chunkTrace );
516 currentPixel += QPoint( incX, -incY );
517 x1 = nextPosition.
x() - 2 * incX;
518 y1 = nextPosition.
y() - 2 * incY;
548 x2 = x1 + ( 1 - y1 ) * Vx / fabs( Vy ) ;
550 x2 = x1 + ( 1 + y1 ) * Vx / fabs( Vy ) ;
552 y2 = y1 + ( 1 - x1 ) * Vy / fabs( Vx ) ;
554 y2 = y1 + ( 1 + x1 ) * Vy / fabs( Vx ) ;
581 double dl = sqrt( dx * dx + dy * dy );
583 data.time += dl / Vu ;
584 if ( data.time > 10000 )
586 addPixelToChunkTrace( currentPixel, data, chunkTrace );
587 setChunkTrace( chunkTrace );
588 drawChunkTrace( chunkTrace );
596 if ( isTraceExists( currentPixel ) )
599 setChunkTrace( chunkTrace );
600 addPixelToChunkTrace( currentPixel, data, chunkTrace );
601 drawChunkTrace( chunkTrace );
605 if ( isTraceOutside( currentPixel ) )
607 setChunkTrace( chunkTrace );
608 drawChunkTrace( chunkTrace );
614 void QgsMeshStreamField::setResolution(
int width )
616 mFieldResolution = width;
619 QSize QgsMeshStreamField::imageSize()
const
621 return mFieldSize * mFieldResolution;
624 QPointF QgsMeshStreamField::fieldToDevice(
const QPoint &pixel )
const
627 p = mFieldResolution * p + QPointF( mFieldResolution - 1, mFieldResolution - 1 ) / 2;
631 bool QgsMeshStreamField::addPixelToChunkTrace( QPoint &pixel,
632 QgsMeshStreamField::FieldData &data,
633 std::list<QPair<QPoint, QgsMeshStreamField::FieldData> > &chunkTrace )
635 chunkTrace.emplace_back( pixel, data );
636 if ( chunkTrace.size() == 3 )
638 simplifyChunkTrace( chunkTrace );
644 void QgsMeshStreamlinesField::initField()
646 mField = QVector<bool>( mFieldSize.width() * mFieldSize.height(),
false );
650 QgsMeshStreamlinesField::QgsMeshStreamlinesField(
const QgsTriangularMesh &triangularMesh,
655 bool dataIsOnVertices,
658 QgsMeshStreamField( triangularMesh,
660 scalarActiveFaceFlagValues,
668 QgsMeshStreamlinesField::QgsMeshStreamlinesField(
const QgsMeshStreamlinesField &other ):
669 QgsMeshStreamField( other ),
670 mField( other.mField )
673 QgsMeshStreamlinesField &QgsMeshStreamlinesField::operator=(
const QgsMeshStreamlinesField &other )
675 QgsMeshStreamField::operator=( other );
676 mField = other.mField;
680 void QgsMeshStreamlinesField::storeInField(
const QPair<QPoint, FieldData> pixelData )
682 int i = pixelData.first.x();
683 int j = pixelData.first.y();
684 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
686 mField[j * mFieldSize.width() + i] =
true;
690 void QgsMeshStreamField::setChunkTrace( std::list<QPair<QPoint, FieldData> > &chunkTrace )
692 auto p = chunkTrace.begin();
693 while ( p != chunkTrace.end() )
695 storeInField( ( *p ) );
696 mPixelFillingCount++;
701 void QgsMeshStreamlinesField::drawChunkTrace(
const std::list<QPair<QPoint, QgsMeshStreamField::FieldData> > &chunkTrace )
703 auto p1 = chunkTrace.begin();
706 while ( p2 != chunkTrace.end() )
708 double mag1 = ( *p1 ).second.magnitude;
709 double mag2 = ( *p2 ).second.magnitude;
710 if ( filterMag( mag1 ) && filterMag( mag2 ) )
712 QPen pen = mPainter->pen();
713 pen.setColor( mVectorColoring.color( ( mag1 + mag2 ) / 2 ) );
714 mPainter->setPen( pen );
715 mPainter->drawLine( fieldToDevice( ( *p1 ).first ), fieldToDevice( ( *p2 ).first ) );
723 void QgsMeshStreamField::clearChunkTrace( std::list<QPair<QPoint, QgsMeshStreamField::FieldData> > &chunkTrace )
725 auto one_before_end = std::prev( chunkTrace.end() );
726 chunkTrace.erase( chunkTrace.begin(), one_before_end );
729 void QgsMeshStreamField::simplifyChunkTrace( std::list<QPair<QPoint, FieldData> > &shunkTrace )
731 if ( shunkTrace.size() != 3 )
734 auto ip3 = shunkTrace.begin();
738 while ( ip3 != shunkTrace.end() && ip2 != shunkTrace.end() )
740 QPoint v1 = ( *ip1 ).first - ( *ip2 ).first;
741 QPoint v2 = ( *ip2 ).first - ( *ip3 ).first;
742 if ( v1.x()*v2.x() + v1.y()*v2.y() == 0 )
744 ( *ip1 ).second.time += ( ( *ip2 ).second.time ) / 2;
745 ( *ip3 ).second.time += ( ( *ip2 ).second.time ) / 2;
746 ( *ip1 ).second.directionX += ( *ip2 ).second.directionX;
747 ( *ip1 ).second.directionY += ( *ip2 ).second.directionY;
748 shunkTrace.erase( ip2 );
755 bool QgsMeshStreamlinesField::isTraceExists(
const QPoint &pixel )
const
759 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
761 return mField[j * mFieldSize.width() + i];
767 bool QgsMeshStreamField::isTraceOutside(
const QPoint &pixel )
const
771 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
778 void QgsMeshStreamField::setMinimizeFieldSize(
bool minimizeFieldSize )
780 mMinimizeFieldSize = minimizeFieldSize;
783 QgsMeshStreamField &QgsMeshStreamField::operator=(
const QgsMeshStreamField &other )
785 mFieldSize = other.mFieldSize ;
786 mFieldResolution = other.mFieldResolution;
788 mTraceImage = other.mTraceImage ;
789 mMapToFieldPixel = other.mMapToFieldPixel ;
790 mVectorColoring = other.mVectorColoring;
791 mPixelFillingCount = other.mPixelFillingCount ;
792 mMaxPixelFillingCount = other.mMaxPixelFillingCount ;
793 mLayerExtent = other.mLayerExtent ;
794 mMapExtent = other.mMapExtent;
795 mFieldTopLeftInDeviceCoordinates = other.mFieldTopLeftInDeviceCoordinates ;
796 mValid = other.mValid ;
797 mMaximumMagnitude = other.mMaximumMagnitude ;
798 mPixelFillingDensity = other.mPixelFillingDensity ;
799 mMinMagFilter = other.mMinMagFilter ;
800 mMaxMagFilter = other.mMaxMagFilter ;
801 mMinimizeFieldSize = other.mMinimizeFieldSize ;
802 mVectorValueInterpolator =
803 std::unique_ptr<QgsMeshVectorValueInterpolator>( other.mVectorValueInterpolator->clone() );
805 mPainter.reset(
new QPainter( &mTraceImage ) );
810 void QgsMeshStreamField::initImage()
813 mTraceImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
814 mTraceImage.fill( 0X00000000 );
816 mPainter.reset(
new QPainter( &mTraceImage ) );
817 mPainter->setRenderHint( QPainter::Antialiasing,
true );
818 mPainter->setPen( mPen );
821 bool QgsMeshStreamField::filterMag(
double value )
const
823 return ( mMinMagFilter < 0 || value > mMinMagFilter ) && ( mMaxMagFilter < 0 || value < mMaxMagFilter );
826 QImage QgsMeshStreamField::image()
828 return mTraceImage.scaled( mFieldSize * mFieldResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
831 void QgsMeshStreamField::setPixelFillingDensity(
double maxFilling )
833 mPixelFillingDensity = maxFilling;
834 mMaxPixelFillingCount = int( mPixelFillingDensity * mFieldSize.width() * mFieldSize.height() );
837 void QgsMeshStreamField::setColor( QColor color )
839 mPen.setColor( color );
842 void QgsMeshStreamField::setLineWidth(
double width )
844 mPen.setWidthF( width );
847 void QgsMeshStreamField::setFilter(
double min,
double max )
853 QgsMeshVectorValueInterpolatorFromFace::QgsMeshVectorValueInterpolatorFromFace(
const QgsTriangularMesh &triangularMesh,
const QgsMeshDataBlock &datasetVectorValues ):
854 QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues )
858 QgsMeshVectorValueInterpolator( triangularMesh, datasetVectorValues, scalarActiveFaceFlagValues )
861 QgsMeshVectorValueInterpolatorFromFace::QgsMeshVectorValueInterpolatorFromFace(
const QgsMeshVectorValueInterpolatorFromFace &other ):
862 QgsMeshVectorValueInterpolator( other )
865 QgsMeshVectorValueInterpolatorFromFace *QgsMeshVectorValueInterpolatorFromFace::clone()
867 return new QgsMeshVectorValueInterpolatorFromFace( *
this );
870 QgsMeshVectorValueInterpolatorFromFace &QgsMeshVectorValueInterpolatorFromFace::operator=(
const QgsMeshVectorValueInterpolatorFromFace &other )
872 QgsMeshVectorValueInterpolator::operator=( other );
876 QgsVector QgsMeshVectorValueInterpolatorFromFace::interpolatedValuePrivate(
int faceIndex,
const QgsPointXY point )
const
878 QgsMeshFace face = mTriangularMesh.triangles().at( faceIndex );
884 QgsVector vect =
QgsVector( mDatasetValues.value( mTriangularMesh.trianglesToNativeFaces().at( faceIndex ) ).x(),
885 mDatasetValues.value( mTriangularMesh.trianglesToNativeFaces().at( faceIndex ) ).y() );
887 return QgsMeshLayerUtils::interpolateVectorFromFacesData(
895 QgsMeshVectorStreamlineRenderer::QgsMeshVectorStreamlineRenderer(
899 bool dataIsOnVertices,
903 mRendererContext( rendererContext )
905 mStreamlineField.reset(
new QgsMeshStreamlinesField( triangularMesh,
907 scalarActiveFaceFlagValues,
914 mStreamlineField->updateSize( rendererContext );
917 QgsUnitTypes::RenderUnit::RenderMillimeters ) ) ;
918 mStreamlineField->setColor( settings.
color() );
927 mStreamlineField->addTracesOnMesh( triangularMesh, rendererContext.
mapExtent() );
930 mStreamlineField->addRandomTraces();
935 void QgsMeshVectorStreamlineRenderer::draw()
937 if ( mRendererContext.renderingStopped() )
939 mRendererContext.painter()->drawImage( mStreamlineField->topLeft(), mStreamlineField->image() );
942 QgsMeshParticleTracesField::QgsMeshParticleTracesField(
const QgsTriangularMesh &triangularMesh,
947 bool dataIsOnVertices,
950 QgsMeshStreamField( triangularMesh,
952 scalarActiveFaceFlagValues,
959 std::srand( uint( ::time(
nullptr ) ) );
960 mPen.setCapStyle( Qt::RoundCap );
963 QgsMeshParticleTracesField::QgsMeshParticleTracesField(
const QgsMeshParticleTracesField &other ):
964 QgsMeshStreamField( other ),
965 mTimeField( other.mTimeField ),
966 mMagnitudeField( other.mMagnitudeField ),
967 mDirectionField( other.mDirectionField ),
968 mParticles( other.mParticles ),
969 mStumpImage( other.mStumpImage ),
970 mTimeStep( other.mTimeStep ),
971 mParticlesLifeTime( other.mParticlesLifeTime ),
972 mParticlesCount( other.mParticlesCount ),
973 mTailFactor( other.mTailFactor ),
974 mParticleColor( other.mParticleColor ),
975 mParticleSize( other.mParticleSize ),
976 mStumpFactor( other.mStumpFactor )
979 void QgsMeshParticleTracesField::addParticle(
const QPoint &startPoint,
double lifeTime )
981 addTrace( startPoint );
982 if ( time( startPoint ) > 0 )
984 QgsMeshTraceParticle p;
985 p.lifeTime = lifeTime;
986 p.position = startPoint;
987 mParticles.append( p );
992 void QgsMeshParticleTracesField::addParticleXY(
const QgsPointXY &startPoint,
double lifeTime )
994 addParticle( mMapToFieldPixel.transform( startPoint ).toQPointF().toPoint(), lifeTime );
997 void QgsMeshParticleTracesField::moveParticles()
1000 for (
auto &p : mParticles )
1002 double spentTime = p.remainingTime;
1003 size_t countAdded = 0;
1004 while ( spentTime < mTimeStep && p.lifeTime > 0 )
1006 double timeToSpend = double( time( p.position ) );
1007 if ( timeToSpend > 0 )
1009 p.lifeTime -= timeToSpend;
1010 spentTime += timeToSpend;
1011 QPoint dir = direction( p.position );
1012 if ( p.lifeTime > 0 )
1015 p.tail.emplace_back( p.position );
1030 if ( p.lifeTime <= 0 )
1038 p.remainingTime = spentTime - mTimeStep;
1039 while (
int( p.tail.size() ) > mMinTailLength && p.tail.size() > countAdded * mTailFactor )
1040 p.tail.erase( p.tail.begin() );
1041 drawParticleTrace( p );
1047 while ( i < mParticles.count() )
1049 if ( mParticles.at( i ).tail.size() == 0 )
1050 mParticles.removeAt( i );
1056 if ( mParticles.count() < mParticlesCount )
1057 addRandomParticles();
1060 void QgsMeshParticleTracesField::addRandomParticles()
1065 if ( mParticlesCount < 0 )
1067 addParticleXY(
QgsPointXY( mMapToFieldPixel.xCenter(), mMapToFieldPixel.yCenter() ), mParticlesLifeTime );
1071 int count = mParticlesCount - mParticles.count();
1073 for (
int i = 0; i < count; ++i )
1075 int xRandom = 1 + std::rand() / int( ( RAND_MAX + 1u ) / uint( mFieldSize.width() ) ) ;
1076 int yRandom = 1 + std::rand() / int ( ( RAND_MAX + 1u ) / uint( mFieldSize.height() ) ) ;
1077 double lifeTime = ( std::rand() / ( ( RAND_MAX + 1u ) / mParticlesLifeTime ) );
1078 addParticle( QPoint( xRandom, yRandom ), lifeTime );
1082 void QgsMeshParticleTracesField::storeInField(
const QPair<QPoint, QgsMeshStreamField::FieldData> pixelData )
1084 int i = pixelData.first.x();
1085 int j = pixelData.first.y();
1086 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1088 mTimeField[j * mFieldSize.width() + i] = pixelData.second.time;
1089 int d = pixelData.second.directionX + 2 + ( pixelData.second.directionY + 1 ) * 3;
1090 mDirectionField[j * mFieldSize.width() + i] =
static_cast<char>( d );
1091 mMagnitudeField[j * mFieldSize.width() + i] = pixelData.second.magnitude;
1095 void QgsMeshParticleTracesField::initField()
1097 mTimeField = QVector<float>( mFieldSize.width() * mFieldSize.height(), -1 );
1098 mDirectionField = QVector<char>( mFieldSize.width() * mFieldSize.height(),
static_cast<char>(
int( 0 ) ) );
1099 mMagnitudeField = QVector<float>( mFieldSize.width() * mFieldSize.height(), 0 );
1101 mStumpImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
1102 mStumpImage.fill( QColor( 0, 0, 0, mStumpFactor ) );
1105 bool QgsMeshParticleTracesField::isTraceExists(
const QPoint &pixel )
const
1109 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1111 return mTimeField[j * mFieldSize.width() + i] >= 0;
1117 void QgsMeshParticleTracesField::setStumpParticleWithLifeTime(
bool stumpParticleWithLifeTime )
1119 mStumpParticleWithLifeTime = stumpParticleWithLifeTime;
1122 void QgsMeshParticleTracesField::setParticlesColor(
const QColor &
c )
1124 mVectorColoring.setColor(
c );
1127 void QgsMeshParticleTracesField::setMinTailLength(
int minTailLength )
1129 mMinTailLength = minTailLength;
1132 QgsMeshParticleTracesField &QgsMeshParticleTracesField::operator=(
const QgsMeshParticleTracesField &other )
1134 QgsMeshStreamField::operator=( other );
1135 mTimeField = other.mTimeField;
1136 mDirectionField = other.mDirectionField;
1137 mParticles = other.mParticles;
1138 mStumpImage = other.mStumpImage;
1139 mTimeStep = other.mTimeStep;
1140 mParticlesLifeTime = other.mParticlesLifeTime;
1141 mParticlesCount = other.mParticlesCount;
1142 mTailFactor = other.mTailFactor;
1143 mParticleColor = other.mParticleColor;
1144 mParticleSize = other.mParticleSize;
1145 mStumpFactor = other.mStumpFactor;
1150 void QgsMeshParticleTracesField::setTailFactor(
double tailFactor )
1152 mTailFactor = tailFactor;
1155 void QgsMeshParticleTracesField::setParticleSize(
double particleSize )
1157 mParticleSize = particleSize;
1160 void QgsMeshParticleTracesField::setTimeStep(
double timeStep )
1162 mTimeStep = timeStep;
1165 void QgsMeshParticleTracesField::setParticlesLifeTime(
double particlesLifeTime )
1167 mParticlesLifeTime = particlesLifeTime;
1170 QImage QgsMeshParticleTracesField::imageRendered()
const
1175 void QgsMeshParticleTracesField::stump()
1178 mPainter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
1179 mPainter->drawImage( QPoint( 0, 0 ), mStumpImage );
1182 void QgsMeshParticleTracesField::setStumpFactor(
int sf )
1185 mStumpImage = QImage( mFieldSize * mFieldResolution, QImage::Format_ARGB32 );
1186 mStumpImage.fill( QColor( 0, 0, 0, mStumpFactor ) );
1189 QPoint QgsMeshParticleTracesField::direction( QPoint position )
const
1191 int i = position.x();
1192 int j = position.y();
1193 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1195 int dir =
static_cast<int>( mDirectionField[j * mFieldSize.width() + i] );
1196 if ( dir != 0 && dir < 10 )
1197 return QPoint( ( dir - 1 ) % 3 - 1, ( dir - 1 ) / 3 - 1 );
1199 return QPoint( 0, 0 );
1202 float QgsMeshParticleTracesField::time( QPoint position )
const
1204 int i = position.x();
1205 int j = position.y();
1206 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1208 return mTimeField[j * mFieldSize.width() + i];
1213 float QgsMeshParticleTracesField::magnitude( QPoint position )
const
1215 int i = position.x();
1216 int j = position.y();
1217 if ( i >= 0 && i < mFieldSize.width() && j >= 0 && j < mFieldSize.height() )
1219 return mMagnitudeField[j * mFieldSize.width() + i];
1224 void QgsMeshParticleTracesField::drawParticleTrace(
const QgsMeshTraceParticle &particle )
1226 const std::list<QPoint> &tail = particle.tail;
1227 if ( tail.size() == 0 )
1229 double iniWidth = mParticleSize;
1230 double finWidth = 0;
1232 size_t pixelCount = tail.size();
1234 double transparency = 1;
1235 if ( mStumpParticleWithLifeTime )
1236 transparency = sin( M_PI * particle.lifeTime / mParticlesLifeTime );
1239 if ( pixelCount > 1 )
1240 dw = ( iniWidth - finWidth ) / ( pixelCount );
1244 auto ip1 = std::prev( tail.end() );
1245 auto ip2 = std::prev( ip1 );
1247 while ( ip1 != tail.begin() )
1249 QPointF p1 = fieldToDevice( ( *ip1 ) );
1250 QPointF p2 = fieldToDevice( ( *ip2 ) );
1251 QColor traceColor = mVectorColoring.color( magnitude( *ip1 ) );
1252 traceColor.setAlphaF( traceColor.alphaF()*transparency );
1253 mPen.setColor( traceColor );
1254 mPen.setWidthF( iniWidth - i * dw );
1255 mPainter->setPen( mPen );
1256 mPainter->drawLine( p1, p2 );
1263 void QgsMeshParticleTracesField::setParticlesCount(
int particlesCount )
1265 mParticlesCount = particlesCount;
1271 bool dataIsOnVertices,
1276 mRendererContext( rendererContext )
1278 mParticleField = std::unique_ptr<QgsMeshParticleTracesField>(
new QgsMeshParticleTracesField( triangularMesh,
1279 dataSetVectorValues,
1280 scalarActiveFaceFlagValues,
1286 mParticleField->updateSize( rendererContext ) ;
1290 mRendererContext( rendererContext )
1297 bool vectorDataOnVertices;
1307 if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
1308 ( cache->mActiveVectorDatasetIndex == datasetIndex ) )
1310 vectorDatasetValues = cache->mVectorDatasetValues;
1311 scalarActiveFaceFlagValues = cache->mScalarActiveFaceFlagValues;
1312 magMax = cache->mVectorDatasetMagMaximum;
1323 if ( vectorDataOnVertices )
1328 vectorDatasetValues = QgsMeshLayerUtils::datasetValues( layer, datasetIndex, 0, count );
1336 mParticleField = std::unique_ptr<QgsMeshParticleTracesField>(
new QgsMeshParticleTracesField( ( *layer->
triangularMesh() ),
1337 vectorDatasetValues,
1338 scalarActiveFaceFlagValues,
1341 vectorDataOnVertices,
1345 mParticleField->setMinimizeFieldSize(
false );
1346 mParticleField->updateSize( mRendererContext );
1350 mRendererContext( other.mRendererContext ),
1352 mVpixMax( other.mVpixMax ),
1353 mParticleLifeTime( other.mParticleLifeTime )
1355 mParticleField = std::unique_ptr<QgsMeshParticleTracesField>(
1356 new QgsMeshParticleTracesField( *other.mParticleField ) );
1362 mParticleField->setParticlesCount( count );
1363 mParticleField->addRandomParticles();
1368 mParticleField->moveParticles();
1369 return mParticleField->image();
1379 updateFieldParameter();
1385 updateFieldParameter();
1390 mParticleLifeTime = particleLifeTime;
1391 updateFieldParameter();
1396 mParticleField->setParticlesColor(
c );
1401 mParticleField->setParticleSize( width );
1406 mParticleField->setTailFactor( fct );
1411 mParticleField->setMinTailLength( l );
1420 mParticleField->setStumpFactor(
int( 255 * p ) );
1425 mParticleField.reset(
new QgsMeshParticleTracesField( *mParticleField ) );
1426 const_cast<QgsRenderContext &
>( mRendererContext ) = other.mRendererContext;
1428 mVpixMax = other.mVpixMax;
1429 mParticleLifeTime = other.mParticleLifeTime;
1434 void QgsMeshVectorTraceAnimationGenerator::updateFieldParameter()
1436 double fieldTimeStep = mVpixMax / mFPS;
1437 double fieldLifeTime = mParticleLifeTime * mFPS * fieldTimeStep;
1438 mParticleField->setTimeStep( fieldTimeStep );
1439 mParticleField->setParticlesLifeTime( fieldLifeTime );
1442 QgsMeshVectorTraceRenderer::QgsMeshVectorTraceRenderer(
1446 bool dataIsOnVertices,
1451 mRendererContext( rendererContext )
1453 mParticleField = std::unique_ptr<QgsMeshParticleTracesField>(
new QgsMeshParticleTracesField( triangularMesh,
1454 dataSetVectorValues,
1455 scalarActiveFaceFlagValues,
1461 mParticleField->updateSize( rendererContext ) ;
1464 settings.
lineWidth(), QgsUnitTypes::RenderUnit::RenderMillimeters ) );
1466 mParticleField->setTailFactor( 1 );
1467 mParticleField->setStumpParticleWithLifeTime(
false );
1470 mParticleField->addRandomParticles();
1471 mParticleField->moveParticles();
1474 void QgsMeshVectorTraceRenderer::draw()
1476 if ( mRendererContext.renderingStopped() )
1478 mRendererContext.painter()->drawImage( mParticleField->topLeft(), mParticleField->image() );
QgsVertexIterator vertices() const
Returns a read-only, Java-style iterator for traversal of vertices of all the geometry,...
Custom exception class for Coordinate Reference System related exceptions.
Class defining color to render mesh datasets.
Perform transforms between map coordinates and device coordinates.
double mapUnitsPerPixel() const
Returns current map units per pixel.
QgsPointXY toMapCoordinates(int x, int y) const
Transform device coordinates to map (world) coordinates.
double mapRotation() const
Returns current map rotation in degrees (clockwise)
QgsMeshDataBlock is a block of integers/doubles that can be used to retrieve: active flags (e....
bool isValid() const
Whether the block is valid.
QgsMeshDatasetIndex is index that identifies the dataset group (e.g.
int group() const
Returns a group index.
virtual QgsMeshDatasetGroupMetadata datasetGroupMetadata(int groupIndex) const =0
Returns dataset group metadata.
virtual QgsMeshDataBlock areFacesActive(QgsMeshDatasetIndex index, int faceIndex, int count) const =0
Returns whether the faces are active for particular dataset.
virtual int datasetGroupCount() const =0
Returns number of datasets groups loaded.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
QgsRectangle extent() const override
Returns the extent of the layer.
QgsMeshRendererSettings rendererSettings() const
Returns renderer settings.
void reload() override
Synchronises with changes in the datasource.
QgsMesh * nativeMesh()
Returns native mesh (nullptr before rendering or calling to updateMesh)
QgsMeshDatasetIndex activeVectorDatasetAtTime(const QgsDateTimeRange &timeRange) const
Returns dataset index from active vector group depending on the time range If the temporal properties...
QgsMeshDataProvider * dataProvider() override
Returns the layer's data provider, it may be nullptr.
QgsTriangularMesh * triangularMesh(double minimumTriangleSize=0) const
Returns triangular mesh (nullptr before rendering or calling to updateMesh).
QgsMeshLayerRendererCache * rendererCache()
Returns native mesh (nullptr before rendering)
QgsMeshRendererVectorSettings vectorSettings(int groupIndex) const
Returns renderer settings.
Represents a renderer settings for vector datasets.
int userGridCellWidth() const
Returns width in pixels of user grid cell.
QgsMeshRendererVectorTracesSettings tracesSettings() const
Returns settings for vector rendered with traces.
QColor color() const
Returns color used for drawing arrows.
int userGridCellHeight() const
Returns height in pixels of user grid cell.
double lineWidth() const
Returns line width of the arrow (in millimeters)
double filterMax() const
Returns filter value for vector magnitudes.
QgsInterpolatedLineColor vectorStrokeColoring() const
Returns the stroke coloring used to render vector datasets.
bool isOnUserDefinedGrid() const
Returns whether vectors are drawn on user-defined grid.
double filterMin() const
Returns filter value for vector magnitudes.
QgsMeshRendererVectorStreamlineSettings streamLinesSettings() const
Returns settings for vector rendered with streamlines.
SeedingStartPointsMethod seedingMethod() const
Returns the method used for seeding start points of strealines.
@ Random
Seeds start points randomly on the mesh.
@ MeshGridded
Seeds start points on the vertices mesh or user regular grid.
double seedingDensity() const
Returns the density used for seeding start points.
double maximumTailLength() const
Returns the maximum tail length.
QgsUnitTypes::RenderUnit maximumTailLengthUnit() const
Returns the maximum tail length unit.
int particlesCount() const
Returns particles count.
A wrapper for QgsMeshParticuleTracesField used to render the particles.
void setParticlesLifeTime(double particleLifeTime)
Sets maximum life time of particles in seconds.
void setMinimumTailLength(int l)
Sets the minimum tail length.
void setTailPersitence(double p)
Sets the visual persistence of the tail.
void setParticlesColor(const QColor &c)
Sets colors of particle.
QImage imageRendered()
Moves all the particles using frame per second (fps) to calculate the displacement and return the ren...
void setTailFactor(double fct)
Sets the tail factor, used to adjust the length of the tail. 0 : minimum length, >1 increase the tail...
void setFPS(int FPS)
Sets the number of frames per seconds that will be rendered.
QgsMeshVectorTraceAnimationGenerator(const QgsTriangularMesh &triangularMesh, const QgsMeshDataBlock &dataSetVectorValues, const QgsMeshDataBlock &scalarActiveFaceFlagValues, bool dataIsOnVertices, const QgsRenderContext &rendererContext, const QgsRectangle &layerExtent, double magMax, const QgsMeshRendererVectorSettings &vectorSettings)
Constructor to use from QgsMeshVectorRenderer.
void setParticlesSize(double width)
Sets particle size in px.
void setMaxSpeedPixel(int max)
Sets the max number of pixels that can be go through by the particles in 1 second.
QgsMeshVectorTraceAnimationGenerator & operator=(const QgsMeshVectorTraceAnimationGenerator &other)
Assignment operator.
void seedRandomParticles(int count)
seeds particles in the vector fields
A class to represent a 2D point.
Point geometry type, with support for z-dimension and m-values.
A rectangle specified with double values.
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
Contains information about the context of a rendering operation.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
QgsRectangle mapExtent() const
Returns the original extent of the map being rendered.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
Scoped object for saving and restoring a QPainter object's state.
const QgsDateTimeRange & temporalRange() const
Returns the datetime range for the object.
Triangular/Derived Mesh is mesh with vertices in map coordinates.
const QVector< QgsMeshFace > & triangles() const
Returns triangles.
const QVector< QgsMeshVertex > & vertices() const
Returns vertices in map coordinate system.
QList< int > faceIndexesForRectangle(const QgsRectangle &rectangle) const
Finds indexes of triangles intersecting given bounding box It uses spatial indexing.
A class to represent a vector.
QgsVector rotateBy(double rot) const SIP_HOLDGIL
Rotates the vector by a specified angle.
double y() const SIP_HOLDGIL
Returns the vector's y-component.
double x() const SIP_HOLDGIL
Returns the vector's x-component.
double length() const SIP_HOLDGIL
Returns the length of the vector.
bool isInTriangleFace(const QgsPointXY point, const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Tests if point p is on the face defined with vertices.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
QVector< int > QgsMeshFace
List of vertex indexes.
QVector< QgsMeshVertex > vertices
QVector< QgsMeshFace > faces