395 lines
13 KiB
C++
Executable File
395 lines
13 KiB
C++
Executable File
/**
|
|
* @RDE_HEADER@
|
|
* Interface-Implementation of EsriDictionaryProcessor
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
*/
|
|
|
|
// Own
|
|
#include "esri_dictionary_processor.h"
|
|
|
|
#include "symbol_dictionary.h" // Interface (enums)
|
|
#include <Common/situation_map_logging.h>
|
|
|
|
|
|
// ESRI
|
|
#include <arcgis/TaskWatcher.h>
|
|
#include <arcgis/Symbol.h>
|
|
#include <arcgis/SymbolStyleSearchParameters.h>
|
|
#include <arcgis/SymbolStyleSearchResultListModel.h>
|
|
#include <arcgis/SymbolStyleSearchResult.h>
|
|
|
|
|
|
// Qt
|
|
#include <QtConcurrent>
|
|
#include <QFuture>
|
|
|
|
#define CREATE_ESRI_SYMBOL_ON_DEMAND // save time on startup
|
|
// REGG-3483 - EsriDictionaryProcessor::syncTableWithinMainThread verzögert Applikations-Start um mehrere Sekunden
|
|
|
|
QImage AdvancedSymbology::scaleImage(const QImage &image, int sizeL)
|
|
{
|
|
if (sizeL == 100)
|
|
return image;
|
|
double factor = sizeL / 100.0;
|
|
const QSize &newSize = image.size() * factor;
|
|
return image.scaled(newSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
|
}
|
|
|
|
|
|
namespace AdvancedSymbology
|
|
{
|
|
|
|
EsriDictionaryProcessor::EsriDictionaryProcessor(SymbologyType symbologyType)
|
|
: m_logger(getLogger("EsriDictionaryProcessor")),
|
|
m_baseSymbolCodeToSymbolPropertyTable(symbologyType, SidcCodec::SidcType::Base)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " ThreadId=" << QThread::currentThreadId();
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
}
|
|
|
|
EsriDictionaryProcessor::~EsriDictionaryProcessor()
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
this->cancel();
|
|
this->waitForFinished();
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
}
|
|
|
|
void EsriDictionaryProcessor::cancel()
|
|
{
|
|
NOTICE_LOG(m_logger) << LOGFUNCTION << " canceling ...";
|
|
m_baseSymbolCodeToSymbolPropertyTableWatcher.cancel(); // Job 1
|
|
m_modifierTableWatcher.cancel(); // Job 2
|
|
// Job n
|
|
NOTICE_LOG(m_logger) << LOGFUNCTION << " canceling ... done";
|
|
}
|
|
|
|
void EsriDictionaryProcessor::waitForFinished()
|
|
{
|
|
NOTICE_LOG(m_logger) << LOGFUNCTION << " waitForFinished ...";
|
|
m_baseSymbolCodeToSymbolPropertyTableWatcher.waitForFinished(); // Job 1
|
|
m_modifierTableWatcher.waitForFinished(); // Job 2
|
|
// Job n
|
|
NOTICE_LOG(m_logger) << LOGFUNCTION << " waitForFinished ... done";
|
|
}
|
|
|
|
void EsriDictionaryProcessor::init(Esri::ArcGISRuntime::DictionarySymbolStyle *loaded_esri_dictionary)
|
|
{
|
|
m_esri_dictionary = loaded_esri_dictionary;
|
|
}
|
|
|
|
void EsriDictionaryProcessor::prepare()
|
|
{
|
|
searchSymbols();
|
|
}
|
|
|
|
void EsriDictionaryProcessor::searchSymbols()
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
|
|
//
|
|
// search for all results (and store them in member):
|
|
//
|
|
|
|
connect(m_esri_dictionary, &Esri::ArcGISRuntime::DictionarySymbolStyle::searchSymbolsCompleted,
|
|
this, &EsriDictionaryProcessor::onSearchSymbolsCompletedPropertyTable, Qt::UniqueConnection);
|
|
|
|
Esri::ArcGISRuntime::SymbolStyleSearchParameters sssp;
|
|
|
|
Esri::ArcGISRuntime::TaskWatcher tw = m_esri_dictionary->searchSymbols(sssp);
|
|
if (tw.isValid())
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " TaskWatcher valid: ssTaskId=" << tw.taskId().toString();
|
|
else
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " TaskWatcher invalid" << " ThreadId=" << QThread::currentThreadId();
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
|
|
}
|
|
|
|
void EsriDictionaryProcessor::startBaseSymbolCodeToPropertyTable()
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
|
|
// A-sync:
|
|
// (slot is a pure virtual method)
|
|
m_baseSymbolCodeToSymbolPropertyTableWatcher = QtConcurrent::run(this, &EsriDictionaryProcessor::processBaseSymbolCodeToPropertyTable);
|
|
|
|
|
|
// Sync: // processBaseSymbolCodeToPropertyTable();
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
}
|
|
|
|
void EsriDictionaryProcessor::startModifierTable()
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
|
|
// A-sync:
|
|
// (slot is a pure virtual method)
|
|
m_modifierTableWatcher = QtConcurrent::run(this, &EsriDictionaryProcessor::processModifierTable);
|
|
|
|
// Sync: // processModifierTable();
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
}
|
|
|
|
QStringList EsriDictionaryProcessor::symbolModifierKeys() const
|
|
{
|
|
return m_modifierTable.keys();
|
|
} // QStringList EsriDictionaryProcessor::symbolModifierKeys() const
|
|
|
|
|
|
bool EsriDictionaryProcessor::symbolModifierData(const QString &key, ModifierData &data) const
|
|
{
|
|
if (!m_modifierTable.contains(key)) return false;
|
|
data = m_modifierTable[key];
|
|
return true;
|
|
} // bool EsriDictionaryProcessor::symbolModifierData(const QString & key, SymbolDictionary::ModifierData & data) const
|
|
|
|
|
|
QVector<ModifierData> EsriDictionaryProcessor::symbolModifierList(SidcDefinitions::SymbolSet symbolSet, unsigned int modifierSector) const
|
|
{
|
|
// check modifierSector, must be 1 or 2
|
|
|
|
if ((modifierSector < 1) || (modifierSector > 2))
|
|
{
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " Illegal modifierSector \"" << modifierSector << "\". Must be 1 or 2.";
|
|
return QVector<ModifierData>();
|
|
}
|
|
|
|
|
|
QVector<ModifierData> resultList;
|
|
resultList.reserve(m_modifierTable.size());
|
|
|
|
QMapIterator<QString, ModifierData> hIt(m_modifierTable);
|
|
while (hIt.hasNext())
|
|
{
|
|
hIt.next();
|
|
if ((hIt.value().m_symbolSet == symbolSet) && (hIt.value().m_modifierSector == modifierSector))
|
|
resultList << hIt.value();
|
|
}
|
|
return resultList;
|
|
} // QVector<SymbolDictionary::ModifierData> EsriDictionaryProcessor::symbolModifierList(const QString & baseSymbolCode, unsigned int modifierSector) const
|
|
|
|
|
|
|
|
bool EsriDictionaryProcessor::allJobsDone() const
|
|
{
|
|
return
|
|
m_baseSymbolCodeToSymbolPropertyTableFilled && // Job 1
|
|
m_modifierTableFilled; // Job 2
|
|
// Job n
|
|
}
|
|
|
|
void EsriDictionaryProcessor::onSearchSymbolsCompletedPropertyTable(QUuid, Esri::ArcGISRuntime::SymbolStyleSearchResultListModel *searchResults)
|
|
{
|
|
// ssTaskId can be ignored during initializing phase:
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
|
|
// save:
|
|
m_searchResultList = searchResults->searchResults();
|
|
// m_searchResultList << searchResults->searchResults().first(); // speed up for valgrind
|
|
|
|
// cleanup:
|
|
searchResults->deleteLater();
|
|
|
|
// disconnect:
|
|
disconnect(m_esri_dictionary, &Esri::ArcGISRuntime::DictionarySymbolStyle::searchSymbolsCompleted,
|
|
this, &EsriDictionaryProcessor::onSearchSymbolsCompletedPropertyTable);
|
|
|
|
// method 'symbol' has to be called (created) within mainThread:
|
|
syncTableWithinMainThread();
|
|
|
|
|
|
//
|
|
// trigger single jobs:
|
|
//
|
|
startBaseSymbolCodeToPropertyTable(); // Job 1
|
|
startModifierTable(); // Job 2
|
|
// Job n
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END, m_searchResultList.size()=" << m_searchResultList.size();
|
|
}
|
|
|
|
void EsriDictionaryProcessor::onEsriSymbolSwatchCompleted(const QUuid &csTaskId, const QImage &image)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
|
|
<< " csTaskId= '" << csTaskId << "'"
|
|
<< " image.isNull= '" << image.isNull() << "'"
|
|
<< " image.colorTable().size()= '" << image.colorTable().size() << "'"
|
|
<< " ThreadId=" << QThread::currentThreadId();
|
|
|
|
auto it = m_symbolPropertyImage2symbolResult.find(csTaskId);
|
|
if (it == m_symbolPropertyImage2symbolResult.end())
|
|
{
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " no taskIdMapping"
|
|
<< " csTaskId= '" << csTaskId << "'";
|
|
return;
|
|
}
|
|
|
|
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " SymbolResult found..." << " csTaskId= '" << csTaskId << "'";
|
|
QSharedPointer<SymbolResult> symResult = it.value().toStrongRef();
|
|
if (symResult)
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " SymbolResult found and still valid..."
|
|
<< " csTaskId= '" << csTaskId << "'";
|
|
|
|
const SymbolDataToRender &symDataToRender = symResult->symbolDataToRender();
|
|
|
|
// save in 'cache':
|
|
const QString &originalKey = symDataToRender.spiOriginalKey;
|
|
const int sizeL = symDataToRender.spiSizeL;
|
|
auto itCache = m_originalKeyToEsriSymbol.find(originalKey);
|
|
if (itCache == m_originalKeyToEsriSymbol.end())
|
|
{
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " save to cache, not found, " << " originalKey= '" << originalKey << "'"
|
|
<< " csTaskId= '" << csTaskId << "'";
|
|
}
|
|
else
|
|
itCache.value().swatchImage = image;
|
|
|
|
// return value
|
|
SymbolImage symImage;
|
|
|
|
const QImage &scaledImg = scaleImage(image, sizeL);
|
|
|
|
if (scaledImg.isNull())
|
|
{
|
|
ERROR_LOG(m_logger) << LOGFUNCTION
|
|
<< " image.isNull() = " << image.isNull()
|
|
<< " scaledImg.isNull() = " << scaledImg.isNull()
|
|
<< " sizeL=" << sizeL;
|
|
}
|
|
|
|
symImage.setImage(scaledImg);
|
|
symImage.setValid(true);
|
|
symResult->setSymbolImage(symImage);
|
|
symResult->fireResultReady(); // ToDo: maybe async!?!?
|
|
|
|
}
|
|
else
|
|
{
|
|
INFO_LOG(m_logger) << LOGFUNCTION << " SymbolResult has already been deleted"
|
|
<< " csTaskId= '" << csTaskId << "'";
|
|
}
|
|
|
|
// cleanup
|
|
m_symbolPropertyImage2symbolResult.erase(it);
|
|
}
|
|
|
|
void EsriDictionaryProcessor::checkIfAllAreDone()
|
|
{
|
|
if (allJobsDone())
|
|
emit allIsDone();
|
|
}
|
|
|
|
void EsriDictionaryProcessor::syncTableWithinMainThread()
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
|
|
// for ALL searchResuls (for valid baseSymbolCodes, for modifierTable...)
|
|
for (const Esri::ArcGISRuntime::SymbolStyleSearchResult &resultItem : qAsConst(m_searchResultList))
|
|
{
|
|
const QString &originalKey = resultItem.key();
|
|
|
|
// store esriSymbol for rendering issues:
|
|
EsriSymbol eSym;
|
|
#ifdef CREATE_ESRI_SYMBOL_ON_DEMAND
|
|
eSym.m_sssResult =resultItem;
|
|
#else
|
|
QObject *parent = this;
|
|
eSym.esriSymbol = resultItem.symbol(parent); // resultItem.symbol() has to be called from mainThread???
|
|
#endif // CREATE_ESRI_SYMBOL_ON_DEMAND
|
|
m_originalKeyToEsriSymbol[originalKey] = eSym;
|
|
}
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END, m_originalKeyToEsriSymbol.size=" << m_originalKeyToEsriSymbol.size();
|
|
}
|
|
|
|
bool EsriDictionaryProcessor::symbolPropertyImageContains(const QString &originalKey) const
|
|
{
|
|
return m_originalKeyToEsriSymbol.contains(originalKey);
|
|
}
|
|
|
|
QSharedPointer<SymbolResult> EsriDictionaryProcessor::symbolPropertyImage(QSharedPointer<SymbolResult> symResult, const QString &originalKey) const
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
|
|
auto it = m_originalKeyToEsriSymbol.find(originalKey);
|
|
if (it == m_originalKeyToEsriSymbol.end())
|
|
{
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " not found, " << " originalKey= '" << originalKey << "'";
|
|
return symResult;
|
|
}
|
|
|
|
const EsriSymbol &eSym = it.value();
|
|
|
|
if (eSym.swatchImage.isNull())
|
|
{
|
|
#ifdef CREATE_ESRI_SYMBOL_ON_DEMAND
|
|
if (!eSym.esriSymbol)
|
|
{
|
|
QObject *parent = const_cast<EsriDictionaryProcessor *>(this); // cast const away
|
|
eSym.esriSymbol = eSym.m_sssResult.symbol(parent); // resultItem.symbol() has to be called from mainThread???
|
|
}
|
|
#endif // CREATE_ESRI_SYMBOL_ON_DEMAND
|
|
|
|
connect(eSym.esriSymbol, &Esri::ArcGISRuntime::Symbol::createSwatchCompleted,
|
|
this, &EsriDictionaryProcessor::onEsriSymbolSwatchCompleted, Qt::UniqueConnection);
|
|
|
|
#ifdef USE_ARCADE_SYMBOLOGY
|
|
Esri::ArcGISRuntime::TaskWatcher tw;
|
|
auto it = m_baseSymbolCodeToSymbolPropertyTable.find(originalKey);
|
|
if (it != m_baseSymbolCodeToSymbolPropertyTable.end() && (it->geometryType() == GeometryType::Polygon) )
|
|
// area/Polygon -> request rectangle, otherwise quadratic swatch:
|
|
tw = eSym.esriSymbol->createSwatch(CREATE_SWATCH_PARAMETER.m_width, CREATE_SWATCH_PARAMETER.m_height, CREATE_SWATCH_PARAMETER.m_screenScale);
|
|
else
|
|
tw = eSym.esriSymbol->createSwatch(CREATE_SWATCH_PARAMETER.m_height, CREATE_SWATCH_PARAMETER.m_height, CREATE_SWATCH_PARAMETER.m_screenScale);
|
|
|
|
#else
|
|
Esri::ArcGISRuntime::TaskWatcher tw = eSym.esriSymbol->createSwatch(CREATE_SWATCH_SCREEN_SCALE);
|
|
#endif // USE_ARCADE_SYMBOLOGY
|
|
QUuid csTaskId;
|
|
if (tw.isValid())
|
|
{
|
|
csTaskId = tw.taskId();
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " csTaskWatcher valid: csTaskId=" << csTaskId;
|
|
}
|
|
else
|
|
{
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " csTaskWatcher invalid"
|
|
<< " ThreadId=" << QThread::currentThreadId();
|
|
}
|
|
|
|
if (! csTaskId.isNull())
|
|
m_symbolPropertyImage2symbolResult[csTaskId] = symResult.toWeakRef();
|
|
else
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " csTaskWatcher invalid";
|
|
|
|
}
|
|
else
|
|
{
|
|
// use from 'cache':
|
|
SymbolImage symImage;
|
|
|
|
const SymbolDataToRender &symDataToRender = symResult->symbolDataToRender();
|
|
int sizeL = symDataToRender.sizeL;
|
|
|
|
const QImage &scaledImg = scaleImage(eSym.swatchImage, sizeL);
|
|
|
|
symImage.setImage(scaledImg);
|
|
symImage.setValid(true);
|
|
symResult->setSymbolImage(symImage);
|
|
symResult->fireResultReady(); // ToDo: maybe async!?!?
|
|
}
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
return symResult;
|
|
}
|
|
|
|
} // namespace AdvancedSymbology
|
|
|