/** * @RDE_HEADER@ * Interface-Implementation of EsriDictionaryProcessor * $Id$ * $HeadURL$ * $Author$ */ // Own #include "esri_dictionary_processor.h" #include "symbol_dictionary.h" // Interface (enums) #include // ESRI #include #include #include #include #include // Qt #include #include #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 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(); } QVector resultList; resultList.reserve(m_modifierTable.size()); QMapIterator 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 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 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 EsriDictionaryProcessor::symbolPropertyImage(QSharedPointer 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(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