1 /***************************************************************************
2                          qgsmeshvirtualdatasetgroup.cpp
3                          ---------------------
4     begin                : June 2020
5     copyright            : (C) 2020 by Vincent Cloarec
6     email                : vcloarec at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "qgsmeshvirtualdatasetgroup.h"
19 #include "qgsmeshlayertemporalproperties.h"
20 
QgsMeshVirtualDatasetGroup(const QString & name,const QString & formulaString,QgsMeshLayer * layer,qint64 relativeStartTime,qint64 relativeEndTime)21 QgsMeshVirtualDatasetGroup::QgsMeshVirtualDatasetGroup(
22   const QString &name,
23   const QString &formulaString,
24   QgsMeshLayer *layer,
25   qint64 relativeStartTime,
26   qint64 relativeEndTime ):
27   QgsMeshDatasetGroup( name )
28   , mFormula( formulaString )
29   , mLayer( layer )
30   , mStartTime( relativeStartTime )
31   , mEndTime( relativeEndTime )
32 {
33 }
34 
initialize()35 void QgsMeshVirtualDatasetGroup::initialize()
36 {
37   QString errMessage;
38   mCalcNode.reset( QgsMeshCalcNode::parseMeshCalcString( mFormula, errMessage ) );
39 
40   if ( !mCalcNode || !mLayer )
41     return;
42 
43   mDatasetGroupNameUsed = mCalcNode->notAggregatedUsedDatasetGroupNames();
44   mDatasetGroupNameUsedForAggregate = mCalcNode->aggregatedUsedDatasetGroupNames();
45   setDataType( QgsMeshCalcUtils::determineResultDataType( mLayer,
46                mDatasetGroupNameUsed + mDatasetGroupNameUsedForAggregate ) );
47 
48   //populate used group indexes
49   QMap<QString, int> usedDatasetGroupindexes;
50   const QList<int> &indexes = mLayer->datasetGroupsIndexes();
51   for ( const int i : indexes )
52   {
53     const QString usedName = mLayer->datasetGroupMetadata( i ).name();
54     if ( mDatasetGroupNameUsed.contains( usedName ) )
55       usedDatasetGroupindexes[usedName] = i;
56   }
57 
58   QSet<qint64> times;
59   if ( !mCalcNode->isNonTemporal() )
60   {
61     //populate dataset index with time;
62     const QList<int> &usedIndexes = usedDatasetGroupindexes.values();
63     for ( const int groupIndex : usedIndexes )
64     {
65       const int dsCount = mLayer->datasetCount( groupIndex );
66       if ( dsCount == 0 )
67         return;
68 
69       if ( dsCount == 1 ) //non temporal dataset group
70         continue;
71       for ( int i = 0; i < dsCount; i++ )
72       {
73         const qint64 time = mLayer->datasetRelativeTimeInMilliseconds( QgsMeshDatasetIndex( groupIndex, i ) );
74         if ( time != INVALID_MESHLAYER_TIME )
75           times.insert( time );
76       }
77     }
78   }
79 
80   if ( times.isEmpty() )
81     times.insert( 0 );
82 
83   mDatasetTimes = qgis::setToList( times );
84   std::sort( mDatasetTimes.begin(), mDatasetTimes.end() );
85 
86   mDatasetMetaData = QVector<QgsMeshDatasetMetadata>( mDatasetTimes.count() );
87 
88   //to fill metadata, calculate all the datasets one time
89   int i = 0;
90   while ( i < mDatasetTimes.count() )
91   {
92     mCurrentDatasetIndex = i;
93     if ( calculateDataset() )
94       ++i; //calculation succeeds
95     else
96       mDatasetTimes.removeAt( i ); //calculation fails, remove this time step
97   }
98 
99   calculateStatistic();
100 }
101 
datasetCount() const102 int QgsMeshVirtualDatasetGroup::datasetCount() const
103 {
104   return mDatasetTimes.count();
105 }
106 
dataset(int index) const107 QgsMeshDataset *QgsMeshVirtualDatasetGroup::dataset( int index ) const
108 {
109   if ( index < 0 || index >= mDatasetTimes.count() )
110     return nullptr;
111 
112   if ( index != mCurrentDatasetIndex )
113   {
114     mCurrentDatasetIndex = index;
115     calculateDataset();
116   }
117 
118   return mCacheDataset.get();
119 }
120 
datasetMetadata(int datasetIndex) const121 QgsMeshDatasetMetadata QgsMeshVirtualDatasetGroup::datasetMetadata( int datasetIndex ) const
122 {
123   if ( datasetIndex < 0 && datasetIndex >= mDatasetMetaData.count() )
124     return QgsMeshDatasetMetadata();
125 
126   return mDatasetMetaData.at( datasetIndex );
127 }
128 
datasetGroupNamesDependentOn() const129 QStringList QgsMeshVirtualDatasetGroup::datasetGroupNamesDependentOn() const
130 {
131   return mDatasetGroupNameUsed;
132 }
133 
writeXml(QDomDocument & doc,const QgsReadWriteContext & context) const134 QDomElement QgsMeshVirtualDatasetGroup::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
135 {
136   Q_UNUSED( context )
137   QDomElement elemDataset = doc.createElement( QStringLiteral( "mesh-dataset" ) );
138   elemDataset.setAttribute( QStringLiteral( "source-type" ), QStringLiteral( "virtual" ) );
139   elemDataset.setAttribute( QStringLiteral( "name" ), name() );
140   elemDataset.setAttribute( QStringLiteral( "formula" ), mFormula );
141   elemDataset.setAttribute( QStringLiteral( "start-time" ), mStartTime );
142   elemDataset.setAttribute( QStringLiteral( "end-time" ), mEndTime );
143 
144   return elemDataset;
145 }
146 
description() const147 QString QgsMeshVirtualDatasetGroup::description() const
148 {
149   return mFormula;
150 }
151 
calculateDataset() const152 bool QgsMeshVirtualDatasetGroup::calculateDataset() const
153 {
154   if ( !mLayer )
155     return false;
156 
157   const QgsMeshCalcUtils dsu( mLayer,
158                               mDatasetGroupNameUsed,
159                               mDatasetGroupNameUsedForAggregate,
160                               QgsInterval( mDatasetTimes[mCurrentDatasetIndex] / 1000.0 ),
161                               QgsInterval( mStartTime / 1000.0 ),
162                               QgsInterval( mEndTime / 1000.0 ) );
163 
164   if ( !dsu.isValid() )
165     return false;
166 
167   //open output dataset
168   std::unique_ptr<QgsMeshMemoryDatasetGroup> outputGroup = std::make_unique<QgsMeshMemoryDatasetGroup> ( QString(), dsu.outputType() );
169   mCalcNode->calculate( dsu, *outputGroup );
170 
171   if ( outputGroup->memoryDatasets.isEmpty() )
172     return false;
173 
174   mCacheDataset = outputGroup->memoryDatasets[0];
175   if ( !mDatasetMetaData[mCurrentDatasetIndex].isValid() )
176   {
177     mCacheDataset->calculateMinMax();
178     mCacheDataset->time = mDatasetTimes[mCurrentDatasetIndex] / 3600.0 / 1000.0;
179     mDatasetMetaData[mCurrentDatasetIndex] = mCacheDataset->metadata();
180   }
181 
182   return true;
183 }
184