27   return mRegistery.keys();
 
   32   return mDatasetGroupTreeRootItem->enabledDatasetGroupIndexes();
 
   37   return mRegistery.count();
 
   42   return mExtraDatasets->datasetGroupCount();
 
   53   removePersistentProvider();
 
   54   mPersistentProvider = provider;
 
   55   if ( !mPersistentProvider )
 
   57   for ( 
const QString &uri : extraDatasetUri )
 
   62   checkDatasetConsistency( mPersistentProvider );
 
   63   removeUnregisteredItemFromTree();
 
   68     const int groupCount = mExtraDatasets->datasetGroupCount();
 
   69     for ( 
int i = 0; i < groupCount; ++i )
 
   70       if ( mExtraDatasets->datasetGroup( i ) )
 
   71         mExtraDatasets->datasetGroup( i )->initialize();
 
   74   mExtraDatasets->updateTemporalCapabilities();
 
   79 QgsMeshDatasetGroupStore::DatasetGroup QgsMeshDatasetGroupStore::datasetGroup( 
int index )
 const 
   81   if ( mRegistery.contains( index ) )
 
   82     return mRegistery[index];
 
   84     return DatasetGroup{
nullptr, -1};
 
   89   if ( !mPersistentProvider )
 
   91   return mPersistentProvider->
addDataset( path ) ;
 
   96   if ( !mExtraDatasets && !mLayer )
 
  118   int nativeIndex = mExtraDatasets->addDatasetGroup( group );
 
  119   int groupIndex = registerDatasetGroup( DatasetGroup{mExtraDatasets.get(), nativeIndex} );
 
  120   QList<int> groupIndexes;
 
  121   groupIndexes.append( groupIndex );
 
  122   createDatasetGroupTreeItems( groupIndexes );
 
  123   syncItemToDatasetGroup( groupIndex );
 
  135   for ( 
int groupIndex : groupIndexes )
 
  136     syncItemToDatasetGroup( groupIndex );
 
  141   return mDatasetGroupTreeRootItem.get();
 
  147     mDatasetGroupTreeRootItem.reset( rootItem->
clone() );
 
  149     mDatasetGroupTreeRootItem.reset();
 
  151   unregisterGroupNotPresentInTree();
 
  156   QgsMeshDatasetGroupStore::DatasetGroup  group = datasetGroup( index.
group() );
 
  158     return group.first->datasetGroupMetadata( group.second );
 
  165   QgsMeshDatasetGroupStore::DatasetGroup  group = datasetGroup( groupIndex );
 
  167     return group.first->datasetCount( group.second );
 
  174   QgsMeshDatasetGroupStore::DatasetGroup  group = datasetGroup( index.
group() );
 
  183   QgsMeshDatasetGroupStore::DatasetGroup  group = datasetGroup( index.
group() );
 
  192   QgsMeshDatasetGroupStore::DatasetGroup  group = datasetGroup( index.
group() );
 
  201   QgsMeshDatasetGroupStore::DatasetGroup  group = datasetGroup( index.
group() );
 
  210   QgsMeshDatasetGroupStore::DatasetGroup  group = datasetGroup( index.
group() );
 
  219   QgsMeshDatasetGroupStore::DatasetGroup  group = datasetGroup( index.
group() );
 
  230   QgsMeshDatasetGroupStore::DatasetGroup  group = datasetGroup( groupIndex );
 
  237                               group.first->datasetIndexAtTime( referenceTime, group.second, time, method ).dataset() );
 
  243   int groupIndex )
 const 
  245   const QgsMeshDatasetGroupStore::DatasetGroup  group = datasetGroup( groupIndex );
 
  247     return QList<QgsMeshDatasetIndex>();
 
  251   const QList<QgsMeshDatasetIndex> datasetIndexes = group.first->datasetIndexInTimeInterval( referenceTime, group.second, time1, time2 );
 
  253   QList<QgsMeshDatasetIndex> ret;
 
  254   ret.reserve( datasetIndexes.count() );
 
  264   QgsMeshDatasetGroupStore::DatasetGroup  group = datasetGroup( index.
group() );
 
  265   if ( !group.first || group.second < 0 )
 
  270   if ( group.first == mPersistentProvider )
 
  272   else if ( group.first == mExtraDatasets.get() )
 
  273     return mExtraDatasets->datasetRelativeTime( nativeIndex );
 
  282          ( mExtraDatasets && mExtraDatasets->hasTemporalCapabilities() );
 
  288   QDomElement storeElement = doc.createElement( QStringLiteral( 
"mesh-dataset-groups-store" ) );
 
  289   storeElement.appendChild( mDatasetGroupTreeRootItem->writeXml( doc, context ) );
 
  291   QMap < int, DatasetGroup>::const_iterator it = mRegistery.constBegin();
 
  292   while ( it != mRegistery.constEnd() )
 
  294     QDomElement elemDataset;
 
  295     if ( it.value().first == mPersistentProvider )
 
  297       elemDataset = doc.createElement( QStringLiteral( 
"mesh-dataset" ) );
 
  298       elemDataset.setAttribute( QStringLiteral( 
"global-index" ), it.key() );
 
  299       elemDataset.setAttribute( QStringLiteral( 
"source-type" ), QStringLiteral( 
"persitent-provider" ) );
 
  300       elemDataset.setAttribute( QStringLiteral( 
"source-index" ), it.value().second );
 
  302     else if ( it.value().first == mExtraDatasets.get() )
 
  307         elemDataset = mExtraDatasets->writeXml( it.value().second, doc, context );
 
  308         if ( !elemDataset.isNull() )
 
  309           elemDataset.setAttribute( QStringLiteral( 
"global-index" ), it.key() );
 
  313     if ( !elemDataset.isNull() )
 
  314       storeElement.appendChild( elemDataset );
 
  325   QDomElement datasetElem = storeElem.firstChildElement( 
"mesh-dataset" );
 
  326   QMap<int, QgsMeshDatasetGroup *> extraDatasetGroups;
 
  327   while ( !datasetElem.isNull() )
 
  329     int globalIndex = datasetElem.attribute( QStringLiteral( 
"global-index" ) ).toInt();
 
  330     int sourceIndex = -1;
 
  332     const QString sourceType = datasetElem.attribute( QStringLiteral( 
"source-type" ) );
 
  333     if ( sourceType == QLatin1String( 
"persitent-provider" ) )
 
  335       source = mPersistentProvider;
 
  336       sourceIndex = datasetElem.attribute( QStringLiteral( 
"source-index" ) ).toInt();
 
  337       mPersistentExtraDatasetGroupIndexes.append( globalIndex );
 
  339     else if ( sourceType == QLatin1String( 
"virtual" ) )
 
  341       source = mExtraDatasets.get();
 
  342       QString name = datasetElem.attribute( QStringLiteral( 
"name" ) );
 
  343       QString formula = datasetElem.attribute( QStringLiteral( 
"formula" ) );
 
  344       qint64 startTime = datasetElem.attribute( QStringLiteral( 
"start-time" ) ).toLongLong();
 
  345       qint64 endTime = datasetElem.attribute( QStringLiteral( 
"end-time" ) ).toLongLong();
 
  348       extraDatasetGroups[globalIndex] = dsg;
 
  349       sourceIndex = mExtraDatasets->addDatasetGroup( dsg );
 
  353       QgsDebugMsg( QStringLiteral( 
"Unhandled source-type: %1." ).arg( sourceType ) );
 
  357       mRegistery[globalIndex] = DatasetGroup{source, sourceIndex};
 
  360     datasetElem = datasetElem.nextSiblingElement( QStringLiteral( 
"mesh-dataset" ) );
 
  363   QDomElement rootTreeItemElem = storeElem.firstChildElement( QStringLiteral( 
"mesh-dataset-group-tree-item" ) );
 
  364   if ( !rootTreeItemElem.isNull() )
 
  370   for ( QMap<int, DatasetGroup>::const_iterator it = mRegistery.cbegin(); it != mRegistery.cend(); ++it )
 
  372     if ( it.value().first == source && it.value().second == nativeGroupIndex )
 
  381   DatasetGroup group = datasetGroup( groupIndex );
 
  384   if ( group.first && group.second >= 0 )
 
  385     fail = mPersistentProvider->
persistDatasetGroup( filePath, driver, group.first, group.second );
 
  389     eraseDatasetGroup( group );
 
  390     group.first = mPersistentProvider;
 
  392     mRegistery[groupIndex] = group;
 
  394     if ( mDatasetGroupTreeRootItem )
 
  405 void QgsMeshDatasetGroupStore::onPersistentDatasetAdded( 
int count )
 
  407   Q_ASSERT( mPersistentProvider );
 
  411   QList<int> newGroupIndexes;
 
  412   for ( 
int i = providerBeginIndex; i < providerTotalCount; ++i )
 
  414     if ( i < mPersistentExtraDatasetGroupIndexes.count() )
 
  415       mRegistery[mPersistentExtraDatasetGroupIndexes.at( i )] = DatasetGroup( mPersistentProvider, i );
 
  417       newGroupIndexes.append( registerDatasetGroup( DatasetGroup{mPersistentProvider, i} ) );
 
  420   if ( !newGroupIndexes.isEmpty() )
 
  422     createDatasetGroupTreeItems( newGroupIndexes );
 
  423     mPersistentExtraDatasetGroupIndexes.append( newGroupIndexes );
 
  425     for ( 
int groupIndex : std::as_const( newGroupIndexes ) )
 
  426       syncItemToDatasetGroup( groupIndex );
 
  432 void QgsMeshDatasetGroupStore::removePersistentProvider()
 
  434   if ( !mPersistentProvider )
 
  439   QMap < int, DatasetGroup>::iterator it = mRegistery.begin();
 
  440   while ( it != mRegistery.end() )
 
  442     if ( it.value().first == mPersistentProvider )
 
  443       it = mRegistery.erase( it );
 
  448   mPersistentProvider = 
nullptr;
 
  451 int QgsMeshDatasetGroupStore::newIndex()
 
  454   QMap < int, DatasetGroup>::iterator it = mRegistery.begin();
 
  455   while ( it != mRegistery.end() )
 
  457     if ( index <= it.key() )
 
  458       index = it.key() + 1;
 
  464 int QgsMeshDatasetGroupStore::registerDatasetGroup( 
const QgsMeshDatasetGroupStore::DatasetGroup &group )
 
  466   int groupIndex = newIndex();
 
  467   mRegistery[newIndex()] = group;
 
  471 void QgsMeshDatasetGroupStore::eraseDatasetGroup( 
const QgsMeshDatasetGroupStore::DatasetGroup &group )
 
  473   if ( group.first == mPersistentProvider )
 
  475   else if ( group.first == mExtraDatasets.get() )
 
  476     eraseExtraDataset( group.second );
 
  479 void QgsMeshDatasetGroupStore::eraseExtraDataset( 
int indexInExtraStore )
 
  481   mExtraDatasets->removeDatasetGroup( indexInExtraStore );
 
  484   QMap < int, DatasetGroup>::iterator it = mRegistery.begin();
 
  485   while ( it != mRegistery.end() )
 
  487     int localIndex = it.value().second;
 
  488     if ( it.value().first == mExtraDatasets.get() && localIndex > indexInExtraStore )
 
  489       it->second = localIndex - 1;
 
  496   QMap < int, DatasetGroup>::const_iterator it = mRegistery.constBegin();
 
  497   while ( it != mRegistery.constEnd() )
 
  499     if ( it.value() == DatasetGroup{source, nativeIndex} )
 
  511     if ( nativeIndexToGroupIndex( source, i ) == -1 )
 
  512       indexes.append( registerDatasetGroup( DatasetGroup{source, i} ) );
 
  514   if ( !indexes.isEmpty() )
 
  515     createDatasetGroupTreeItems( indexes );
 
  517   for ( 
int globalIndex : mRegistery.keys() )
 
  519     if ( mRegistery.value( globalIndex ).first == source )
 
  520       syncItemToDatasetGroup( globalIndex );
 
  524 void QgsMeshDatasetGroupStore::removeUnregisteredItemFromTree()
 
  526   QList<QgsMeshDatasetGroupTreeItem *> itemsToCheck;
 
  527   QList<int> indexItemToRemove;
 
  528   for ( 
int i = 0; i < mDatasetGroupTreeRootItem->childCount(); ++i )
 
  529     itemsToCheck.append( mDatasetGroupTreeRootItem->child( i ) );
 
  531   while ( !itemsToCheck.isEmpty() )
 
  535     if ( !mRegistery.contains( globalIndex ) )
 
  536       indexItemToRemove.append( globalIndex );
 
  537     for ( 
int i = 0; i < item->
childCount(); ++i )
 
  538       itemsToCheck.append( item->
child( i ) );
 
  541   for ( 
int i : indexItemToRemove )
 
  549 void QgsMeshDatasetGroupStore::unregisterGroupNotPresentInTree()
 
  551   if ( !mDatasetGroupTreeRootItem )
 
  557   QMap < int, DatasetGroup>::iterator it = mRegistery.begin();
 
  558   while ( it != mRegistery.end() )
 
  560     DatasetGroup datasetGroup = it.value();
 
  561     int globalIndex = it.key();
 
  562     if ( ! mDatasetGroupTreeRootItem->childFromDatasetGroupIndex( globalIndex ) 
 
  563          && datasetGroup.first != mPersistentProvider ) 
 
  565       it = mRegistery.erase( it ); 
 
  566       eraseDatasetGroup( datasetGroup ); 
 
  573 void QgsMeshDatasetGroupStore::syncItemToDatasetGroup( 
int groupIndex )
 
  575   if ( !mDatasetGroupTreeRootItem )
 
  577   DatasetGroup group = datasetGroup( groupIndex );
 
  579   if ( group.first == mPersistentProvider && mPersistentProvider )
 
  585   else if ( group.first == mExtraDatasets.get() )
 
  592 void QgsMeshDatasetGroupStore::createDatasetGroupTreeItems( 
const QList<int> &indexes )
 
  594   QMap<QString, QgsMeshDatasetGroupTreeItem *> mNameToItem;
 
  596   for ( 
int i = 0; i < indexes.count(); ++i )
 
  598     int groupIndex = indexes.at( i );
 
  600     const QString name = meta.
name();
 
  601     const QStringList subdatasets = name.split( 
'/' );
 
  603     QString displayName = name;
 
  606     if ( subdatasets.size() == 2 )
 
  608       auto it = mNameToItem.find( subdatasets[0] );
 
  609       if ( it == mNameToItem.end() )
 
  610         QgsDebugMsg( QStringLiteral( 
"Unable to find parent group for %1." ).arg( name ) );
 
  613         displayName = subdatasets[1];
 
  617     else if ( subdatasets.size() != 1 )
 
  618       QgsDebugMsg( QStringLiteral( 
"Ignoring too deep child group name %1." ).arg( name ) );
 
  622     if ( mNameToItem.contains( name ) )
 
  623       QgsDebugMsg( QStringLiteral( 
"Group %1 is not unique" ).arg( displayName ) );
 
  624     mNameToItem[name] = item;
 
  630   int groupIndex = mGroups.size();
 
  631   mGroups.push_back( std::unique_ptr<QgsMeshDatasetGroup>( 
datasetGroup ) );
 
  640   return mGroups.size() - 1;
 
  646     mGroups.erase( mGroups.begin() + index );
 
  664   if ( groupIndex >= 0 && groupIndex < 
int( mGroups.size() ) )
 
  665     return mGroups.at( groupIndex )->description();
 
  672   if ( groupIndex >= 0 && groupIndex < 
int( mGroups.size() ) )
 
  673     return mGroups[groupIndex].get();
 
  686   return QStringList();
 
  691   return mGroups.size();
 
  697     return mGroups.at( groupIndex )->datasetCount();
 
  705     return mGroups.at( groupIndex )->groupMetadata();
 
  712   int groupIndex = index.
group();
 
  715     int datasetIndex = index.
dataset();
 
  725   int groupIndex = index.
group();
 
  729     int datasetIndex = index.
dataset();
 
  739   int groupIndex = index.
group();
 
  743     int datasetIndex = index.
dataset();
 
  755   Q_UNUSED( faceIndex )
 
  762   int groupIndex = index.
group();
 
  766     int datasetIndex = index.
dataset();
 
  776   int groupIndex = index.
group();
 
  780     int datasetIndex = index.
dataset();
 
  788     const QString &outputDriver,
 
  790     const QVector<QgsMeshDataBlock> &datasetValues,
 
  791     const QVector<QgsMeshDataBlock> &datasetActive,
 
  792     const QVector<double> × )
 
  794   Q_UNUSED( outputFilePath )
 
  795   Q_UNUSED( outputDriver )
 
  798   Q_UNUSED( datasetActive )
 
  804     const QString &outputDriver,
 
  806     int datasetGroupIndex )
 
  808   Q_UNUSED( outputFilePath )
 
  809   Q_UNUSED( outputDriver )
 
  811   Q_UNUSED( datasetGroupIndex )
 
  817   if ( groupIndex >= 0 && groupIndex < 
int( mGroups.size() ) && mGroups[groupIndex] )
 
  818     return mGroups[groupIndex]->writeXml( doc, context );
 
  820     return QDomElement();
 
  827   bool hasTemporal = 
false;
 
  828   for ( 
size_t g = 0; g < mGroups.size(); ++g )
 
bool hasTemporalCapabilities() const
Returns true if the provider has temporal capabilities available.
QgsMesh3dDataBlock is a block of 3d stacked mesh data related N faces defined on base mesh frame.
QgsMeshDataBlock is a block of integers/doubles that can be used to retrieve: active flags (e....
QDateTime referenceTime() const
Returns the reference time.
MatchingTemporalDatasetMethod
Method for selection of temporal mesh dataset from a range time.
qint64 datasetTime(const QgsMeshDatasetIndex &index) const
Returns the relative time in milliseconds of the dataset.
Base class for providing data for QgsMeshLayer.
void datasetGroupsAdded(int count)
Emitted when some new dataset groups have been added.
QgsMeshDataProviderTemporalCapabilities * temporalCapabilities() override
Returns the provider's temporal capabilities.
QgsMeshDatasetGroupTreeItem * datasetGroupTreeItem() const
Returns a pointer to the root of the dataset groups tree item.
QgsMeshDatasetMetadata datasetMetadata(const QgsMeshDatasetIndex &index) const
Returns the metadata of the dataset with global index.
QList< int > enabledDatasetGroupIndexes() const
Returns a list of all group indexes that are enabled.
bool addPersistentDatasets(const QString &path)
Adds persistent datasets from a file with path.
QgsMeshDatasetGroupMetadata datasetGroupMetadata(const QgsMeshDatasetIndex &index) const
Returns the metadata of the dataset group with global index.
bool isFaceActive(const QgsMeshDatasetIndex &index, int faceIndex) const
Returns whether face is active for particular dataset.
QList< int > datasetGroupIndexes() const
Returns a list of all group indexes.
bool hasTemporalCapabilities() const
Returns whether at lea&st one of stored dataset group is temporal.
void resetDatasetGroupTreeItem()
Resets to default state the dataset groups tree item.
QgsMeshDataBlock datasetValues(const QgsMeshDatasetIndex &index, int valueIndex, int count) const
Returns count values of the dataset with global index and from valueIndex.
QgsMesh3dDataBlock dataset3dValues(const QgsMeshDatasetIndex &index, int faceIndex, int count) const
Returns count 3D values of the dataset with global index and from valueIndex.
QgsMeshDatasetIndex datasetIndexAtTime(qint64 time, int groupIndex, QgsMeshDataProviderTemporalCapabilities::MatchingTemporalDatasetMethod method) const
Returns the global dataset index of the dataset int the dataset group with groupIndex,...
bool saveDatasetGroup(QString filePath, int groupIndex, QString driver)
Saves on a file with filePath the dataset groups index with groupIndex with the specified driver.
QList< QgsMeshDatasetIndex > datasetIndexInTimeInterval(qint64 time1, qint64 time2, int groupIndex) const
Returns the global dataset index of the dataset int the dataset group with groupIndex,...
bool addDatasetGroup(QgsMeshDatasetGroup *group)
Adds a extra dataset group, take ownership.
QgsMeshDatasetValue datasetValue(const QgsMeshDatasetIndex &index, int valueIndex) const
Returns the value of the dataset with global index and valueIndex.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context)
Writes the store's information in a DOM document.
int extraDatasetGroupCount() const
Returns the count of extra dataset groups.
int datasetGroupCount() const
Returns the count of dataset groups.
QgsMeshDatasetGroupStore(QgsMeshLayer *layer)
Constructor.
QgsMeshDataBlock areFacesActive(const QgsMeshDatasetIndex &index, int faceIndex, int count) const
Returns whether faces are active for particular dataset.
void setDatasetGroupTreeItem(QgsMeshDatasetGroupTreeItem *rootItem)
Sets the root of the dataset groups tree item, doesn't take onwnershib but clone the root item.
void setPersistentProvider(QgsMeshDataProvider *provider, const QStringList &extraDatasetUri)
Sets the persistent mesh data provider with the path of its extra dataset.
qint64 datasetRelativeTime(const QgsMeshDatasetIndex &index) const
Returns the relative time of the dataset from the persistent provider reference time.
int datasetCount(int groupIndex) const
Returns the total count of dataset group in the store.
void readXml(const QDomElement &storeElem, const QgsReadWriteContext &context)
Reads the store's information from a DOM document.
int globalDatasetGroupIndexInSource(QgsMeshDatasetSourceInterface *source, int nativeGroupIndex) const
Returns the global dataset group index of the dataset group with native index globalGroupIndex in the...
void datasetGroupsAdded(QList< int > indexes)
Emitted after dataset groups are added.
Tree item for display of the mesh dataset groups.
QgsMeshDatasetGroupTreeItem * clone() const
Clones the item.
void setPersistentDatasetGroup(const QString &uri)
Set parameters of the item in accordance with the persistent dataset group with uri.
int childCount() const
Returns the count of children.
int datasetGroupIndex() const
QgsMeshDatasetGroupTreeItem * parentItem() const
Returns the parent item, nullptr if it is root item.
void removeChild(QgsMeshDatasetGroupTreeItem *item)
Removes a item child if exists.
void setDatasetGroup(QgsMeshDatasetGroup *datasetGroup)
Set parameters of the item in accordance with the dataset group.
void appendChild(QgsMeshDatasetGroupTreeItem *item)
Appends a child item.
QgsMeshDatasetGroupTreeItem * child(int row) const
Returns a child.
Abstract class that represents a dataset group.
bool isScalar() const
Returns whether the group contain scalar values.
bool checkValueCountPerDataset(int count) const
Returns whether all the datasets contain count values.
virtual QgsMeshDatasetMetadata datasetMetadata(int datasetIndex) const =0
Returns the metadata of the dataset with index datasetIndex.
virtual QgsMeshDataset * dataset(int index) const =0
Returns the dataset with index.
QgsMeshDatasetGroupMetadata::DataType dataType() const
Returns the data type of the dataset group.
virtual int datasetCount() const =0
Returns the count of datasets in the group.
QgsMeshDatasetIndex is index that identifies the dataset group (e.g.
bool isValid() const
Returns whether index is valid, ie at least groups is set.
int group() const
Returns a group index.
int dataset() const
Returns a dataset index within group()
Interface for mesh datasets and dataset groups.
virtual Q_DECL_DEPRECATED bool persistDatasetGroup(const QString &path, const QgsMeshDatasetGroupMetadata &meta, const QVector< QgsMeshDataBlock > &datasetValues, const QVector< QgsMeshDataBlock > &datasetActive, const QVector< double > ×)
Creates a new dataset group from a data and persists it into a destination path.
virtual QgsMeshDatasetGroupMetadata datasetGroupMetadata(int groupIndex) const =0
Returns dataset group metadata.
virtual int datasetGroupCount() const =0
Returns number of datasets groups loaded.
std::unique_ptr< QgsMeshDataProviderTemporalCapabilities > mTemporalCapabilities
virtual bool addDataset(const QString &uri)=0
Associate dataset with the mesh.
QgsMeshDatasetValue represents single dataset value.
virtual QgsMeshDataBlock datasetValues(bool isScalar, int valueIndex, int count) const =0
Returns count values from valueIndex.
virtual bool isActive(int faceIndex) const =0
Returns whether the face is active.
virtual QgsMeshDataBlock areFacesActive(int faceIndex, int count) const =0
Returns whether faces are active.
virtual QgsMeshDatasetValue datasetValue(int valueIndex) const =0
Returns the value with index valueIndex.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
int meshFaceCount() const
Returns the faces count of the mesh frame.
int meshEdgeCount() const
Returns the edges count of the mesh frame.
int meshVertexCount() const
Returns the vertices count of the mesh frame.
Represents a dataset group calculated from a formula string.
The class is used as a container of context for various read/write operations on other objects.
#define INVALID_MESHLAYER_TIME