/** * @RDE_HEADER@ * Interface-Implementation of SymbolDictionaryImpl * $Id$ * $HeadURL$ * $Author$ */ // Own #include "symbol_dictionary_impl.h" #include "symbol_dictionary.h" // Interface (enums) #include "symbol_extension_renderer.h" #include "esri_dictionary_processor_mil2525d.h" #include "esri_dictionary_processor_app6.h" #include "esri_dictionary_processor_app6d.h" #include "sidc_builder_base.h" #include "base_symbol_model.h" #include #include #include #include #include #include #include #include // ESRI #include #include #include #include #include # include // This file is created by RME, not ESRI #if ACRGIS_VERSION >= 0x640100 // 100.1.0 / Update 1 # define SYM_PROTECTION_AREAS_BECOMES_LINE #endif // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // findSymbol and createSwatch : // Lucas Danzinger: these are both async, and will both run in worker threads. // https://earlyadopter.esri.com/project/forum/thread.html.... - Title: 'SymbolDictionary - createSwatch' // : findSymbol and createSwatch namespace AdvancedSymbology { // make sure, that both enums have identical values assigned and a static_cast works fine: static_assert(static_cast(AdvancedSymbology::LoadStatus::Loaded) == static_cast(Esri::ArcGISRuntime::LoadStatus::Loaded), "LoadStatus mismatch"); static_assert(static_cast(AdvancedSymbology::LoadStatus::Loading) == static_cast(Esri::ArcGISRuntime::LoadStatus::Loading), "LoadStatus mismatch"); static_assert(static_cast(AdvancedSymbology::LoadStatus::FailedToLoad) == static_cast(Esri::ArcGISRuntime::LoadStatus::FailedToLoad), "LoadStatus mismatch"); static_assert(static_cast(AdvancedSymbology::LoadStatus::NotLoaded) == static_cast(Esri::ArcGISRuntime::LoadStatus::NotLoaded), "LoadStatus mismatch"); static_assert(static_cast(AdvancedSymbology::LoadStatus::Unknown) == static_cast(Esri::ArcGISRuntime::LoadStatus::Unknown), "LoadStatus mismatch"); } // namespace AdvancedSymbology namespace AdvancedSymbology { QStringList listOfEtsPointSidcs_Internal() { QStringList sidcs; sidcs += SidcCodec::encodeCivilEts("PersFueAllg"); sidcs += SidcCodec::encodeCivilEts("Polizei_Person"); sidcs += SidcCodec::encodeCivilEts("Pers_FW_BerFue"); return sidcs; } QStringList listOfPointSidcs_Internal() { QStringList sidcs; sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10011500002102000000"); // mine_impl sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10012500002108000000"); // single_fire_impl sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10012500002707050000"); // 25270705 -> Protection Areas : Minefield : Dummy // SymbolSet 01 == air sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10030100001101000000"); // differs in APP6(D) sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10030100001201000000"); // SymbolSet 10 == Land Unit sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10031000001100000000"); sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10031000001101000000"); sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10031000001102000000"); sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10031000001103000000"); sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10031000001104000000"); sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10031000001105000000"); sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10031000001106000000"); // SymbolSet 15 == Land Equipment sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10031500001110000000"); // baseSymbolCode = 15 111000 Missle Launcher sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10031500001100000000"); sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10031500001101000000"); sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10031500001101010000"); sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10031500001101020000"); sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10031500001101030000"); sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10011000001213030000"); // Movement and Maneuver : Reconnaissance 10 121303 (FULL_FRAME, but no real MainIcon) sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10031500001202000000"); // Vehicle : Tank 15 120200 // Guez-IEDs sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10011500002104000000"); // LAND MINE - IED sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10013500001202000000"); // Weapon - IED sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10014000001103000000"); // IED SUICIDE BOMBER (OHNE ZickZack) sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10014000001103010000"); // IED SUICIDE BOMBER (MIT ZickZack) // ^ sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10014000001103040000"); // IED suicide bomber sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10014000001101110000"); // BOOBY TRAP (replaced by 'IED suicide bomber') return sidcs; } QStringList listOfLineSidcs_Internal() { QStringList sidcs; sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10032500001406030000"); // hit_line_impl sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10012500001402000000"); // 25140200_6 == "Maneuver Lines : Line of Contact" (no key8-Entry) #ifdef SYM_PROTECTION_AREAS_BECOMES_LINE sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10012500002711000000"); // Control Measure: Protection Areas : sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10012500002713000000"); // Control Measure: Protection Areas : Assult Crossing (former GeometryType::area, now GeometryType::Line) sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10012500002714000000"); // Control Measure: Protection Areas : Bridge #endif // SYM_PROTECTION_AREAS_BECOMES_LINE return sidcs; } QStringList listOfAreaSidcs_Internal() { QStringList sidcs; sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10012500002408010000"); // area_fire_impl sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10012500002717000000"); // biological_agent_impl sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10042500002701000000"); // building_outline_impl sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10012500002718000000"); // chemical_agent_impl sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10012500001511000000"); // danger_area_impl // sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D,"10012500002707010000"); // MineField sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10012500002707060000"); // mine_field_impl sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10012500002719000000"); // nuclear_agent_impl #ifndef SYM_PROTECTION_AREAS_BECOMES_LINE sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10012500002711000000"); // Control Measure: Protection Areas : sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10012500002713000000"); // Control Measure: Protection Areas : Assult Crossing (former GeometryType::area, now GeometryType::Line) sidcs += SidcCodec::encodeComplete(SymbologyType::MIL2525D, "10012500002714000000"); // Control Measure: Protection Areas : Bridge #endif // SYM_PROTECTION_AREAS_BECOMES_LINE return sidcs; } QStringList listOfPlanEditorSidcs_Internal() { QStringList sidcs; sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25342300"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25140500"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25152100"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25141400"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25141700"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25290500"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25270503"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25341900"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25270504"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25230100"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25271100"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25341500"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25241202"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25241602"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25340800"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25270502"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25342201"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25271204"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25240103"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25240802"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25270501"); sidcs += SidcCodec::encodeBaseSymbolCode(SymbologyType::MIL2525D, "25271600"); return sidcs; } // helper function bool check(QRegExp ®Exp, const QStringList ®ExStrings, const QString &token) { for (const QString &rgExStr : regExStrings) { regExp.setPattern(rgExStr); if (regExp.exactMatch(token)) return true; if (token.contains(rgExStr, Qt::CaseInsensitive)) return true; } // foreach return false; } // helper function bool check(QRegExp ®Exp, const QString ®ExString, const QString &token) { regExp.setPattern(regExString); if (regExp.exactMatch(token)) return true; if (token.contains(regExString)) return true; return false; } SymbolDictionaryImpl::SymbolDictionaryImpl(SymbologyType symbologyType, const QString &symbologyResourcePath) : SymbolDictionary() // Interface , m_baseSymbolCodeToSymbolPropertyTable() , m_etsSidcSymbolProperties(SymbologyType::None) , m_etsSidcImages(SymbologyType::None) , m_xmlSidcSymbolProperties(SymbologyType::None) , m_xmlSymbols(SymbologyType::None,SidcCodec::SidcType::CivilXml) , m_xmlSymbolCache() , m_esriBaseSymbolModel(new BaseSymbolModel(this)) , m_xmlBaseSymbolModel(new BaseSymbolModel(this)) , m_etsBaseSymbolModel(new BaseSymbolModel(this)) , m_changedDelayTimer(new QTimer(this)) , m_isLoading(false) , m_logger(getLogger("SymbolDictionaryImpl")) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " symbologyType=" << symbologyType << " resPath='" << symbologyResourcePath << "'" << " ThreadId=" << QThread::currentThreadId(); DEBUG_LOG(m_logger) << LOGFUNCTION << " symbologyType=" << symbologyType << ", resPath='" << symbologyResourcePath << "'"; switch (symbologyType) { case SymbologyType::APP6B: m_esriDictionaryProcessor = new EsriDictionaryProcessorApp6; // not full supported yet break; case SymbologyType::APP6D: m_esriDictionaryProcessor = new EsriDictionaryProcessorApp6D; // not full supported yet break; case SymbologyType::MIL2525C: m_esriDictionaryProcessor = nullptr; // not supported yet break; case SymbologyType::MIL2525D: m_esriDictionaryProcessor = new EsriDictionaryProcessorMil2525D; break; case SymbologyType::None: m_esriDictionaryProcessor = nullptr; break; } if (!m_esriDictionaryProcessor) { FATAL_LOG(m_logger) << LOGFUNCTION << " !!! no EsriDictionaryProcessor !!!"; FATAL_LOG(m_logger) << LOGFUNCTION << " symbologyType=" << symbologyType << ", resPath='" << symbologyResourcePath << "'"; } m_changedDelayTimer->setSingleShot(true); m_changedDelayTimer->setInterval(500); connect(m_changedDelayTimer, &QTimer::timeout, this, &SymbolDictionaryImpl::changedDelayed); m_initOk = init(symbologyType, symbologyResourcePath); // QTimer *timer = new QTimer(this); // timer->setInterval(5000); // connect(timer, &QTimer::timeout, this, [this]() { // qCritical() << " ----------------------------------------------"; // qCritical() << " m_findSymbol2symbolResult.size() = " << m_findSymbol2symbolResult.size(); // qCritical() << " m_createSwatch2findSymbolTaskId.size() = " << m_createSwatch2findSymbolTaskId.size(); // }); // timer->start(); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } SymbolDictionaryImpl::~SymbolDictionaryImpl() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; m_changedDelayTimer->stop(); if (m_esriDictionaryProcessor) { m_esriDictionaryProcessor->cancel(); m_esriDictionaryProcessor->waitForFinished(); } delete m_esri_dictionary; delete m_esriDictionaryProcessor; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } bool SymbolDictionaryImpl::isOk() const { return m_initOk && (nullptr != m_esri_dictionary); } // bool SymbolDictionaryImpl::isOk() const bool SymbolDictionaryImpl::init(SymbologyType symbologyType, const QString &symbologyResourcePath) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; // cleanup : delete m_esri_dictionary; clearEtsSymbols(); m_createSwatch2findSymbolTaskId.clear(); m_findSymbol2propertyResult.clear(); m_findSymbol2symbolResult.clear(); // clear EsriDictProcessor!? // : cleanup m_symbolDictionaryType_own = symbologyType; m_symbologyResourcePath = symbologyResourcePath; switch (m_symbolDictionaryType_own) { case SymbologyType::MIL2525D: m_specificationType = QStringLiteral("mil2525d"); // 'mil2525d' - fixed string for QuartzRelease break; case SymbologyType::MIL2525C: m_specificationType = QStringLiteral("mil2525c"); break; case SymbologyType::APP6B: m_specificationType = QStringLiteral("app6b"); break; case SymbologyType::APP6D: m_specificationType = QStringLiteral("app6d"); break; case SymbologyType::None: // empty break; } // switch if (m_symbolDictionaryType_own != SymbologyType::None) { #ifdef USE_ARCADE_SYMBOLOGY // m_specificationType = QStringLiteral("/usr/share/arcgis/resources/symbols//mil2525d/mil2525d.stylx"); // m_specificationType = QStringLiteral("/usr/share/arcgis/resources/symbols_arcade//mil2525d/mil2525d.stylx"); QFile resFile(m_symbologyResourcePath); if (m_symbologyResourcePath.isEmpty() || !resFile.exists()) { ERROR_LOG(m_logger) << LOGFUNCTION << " path is empty or does not exist: " << " m_symbologyResourcePath='" << m_symbologyResourcePath << "'"; Q_ASSERT(false); return false; } NOTICE_LOG(m_logger) << LOGFUNCTION << " create EsriDict:" << " m_specificationType='" << m_specificationType << "'" << " m_symbologyResourcePath='" << m_symbologyResourcePath << "'"; // use ESRIs new static method introduced with V100.6: m_esri_dictionary = Esri::ArcGISRuntime::DictionarySymbolStyle::createFromFile(m_symbologyResourcePath); #else // QString symbologyResourcePath("/usr/share/arcgis/resources/symbols//mil2525d/mil2525d.stylx"); if (symbologyResourcePath.isEmpty()) { INFO_LOG(m_logger) << LOGFUNCTION << " create EsriDict:" << " m_specificationType='" << m_specificationType << "'"; m_esri_dictionary = new Esri::ArcGISRuntime::DictionarySymbolStyle(m_specificationType); } else { QFile resFile(symbologyResourcePath); if (!resFile.exists()) { ERROR_LOG(m_logger) << LOGFUNCTION << " path does not exist: " << " symbologyResourcePath='" << symbologyResourcePath << "'"; Q_ASSERT(false); return false; } NOTICE_LOG(m_logger) << LOGFUNCTION << " create EsriDict:" << " m_specificationType='" << m_specificationType << "'" << " m_symbologyResourcePath='" << m_symbologyResourcePath << "'"; m_esri_dictionary = new Esri::ArcGISRuntime::DictionarySymbolStyle(m_specificationType, m_symbologyResourcePath); } #endif // USE_ARCADE_SYMBOLOGY if (m_esri_dictionary) { connect(m_esri_dictionary, &Esri::ArcGISRuntime::DictionarySymbolStyle::loadStatusChanged, this, &SymbolDictionaryImpl::onLoadStatusChanged); connect(m_esri_dictionary, &Esri::ArcGISRuntime::DictionarySymbolStyle::errorOccurred, this, &SymbolDictionaryImpl::onErrorDictionary); } } m_xmlSymbolCache = SymbologyXML::PixmapSymbolCache::instance(); if (m_xmlSymbolCache && !m_xmlSymbolCache->symbolTablesAlreadySet()) { SymbologyXML::SymbolTable *symbolFrames; ///< SymbolTable with Symbol-Frames SymbologyXML::SymbolTable *mobilitySymbols; ///< SymbolTable with Mobility-Symbols SymbologyXML::SymbolTable *bcdabSymbols; ///< SymbolTable with BCDAB-Symbols SymbologyXML::SymbolTable *commandLevelSymbols; ///< SymbolTable with CommandLevel-Symbols SymbologyXML::SymbolTable *branchSymbols; ///< SymbolTable with Branch (Truppengattung)-Symbols SymbologyXML::SymbolTable *vehicleSymbols; ///< SymbolTable with Vehicle-Symbols SymbologyXML::SymbolTable *intrinsicMobilitySymbols; ///< SymbolTable with Intrinsic-Mobility-Symbols SymbologyXML::SymbolTable *armingSymbols; ///< SymbolTable with Arming-Symbols SymbologyXML::SymbolTable *miscSymbols; ///< SymbolTable with Misc-Symbols SymbologyXML::SymbolTable *addOnSymbols; ///< SymbolTable with addOn-Symbols SymbologyXML::SymbolTable *sectorIcons; ///< SymbolTable with Sector-Icons m_xmlSymbolCache->setSymbolTables( symbolFrames = new SymbologyXML::SymbolTable() /* SymbolTable *frames, */ , mobilitySymbols = new SymbologyXML::SymbolTable() /* SymbolTable *mobility, */ , bcdabSymbols = new SymbologyXML::SymbolTable() /* SymbolTable *bcdab, */ , commandLevelSymbols = new SymbologyXML::SymbolTable() /* SymbolTable *commandLevel, */ , branchSymbols = new SymbologyXML::SymbolTable() /* SymbolTable *branches, */ , vehicleSymbols = new SymbologyXML::SymbolTable() /* SymbolTable *vehicle, */ , intrinsicMobilitySymbols = new SymbologyXML::SymbolTable() /* SymbolTable *intrinsicMobility, */ , armingSymbols = new SymbologyXML::SymbolTable() /* SymbolTable *arming, */ , miscSymbols = new SymbologyXML::SymbolTable() /* SymbolTable *misc, */ , addOnSymbols = new SymbologyXML::SymbolTable() /* SymbolTable *addOnSymbole, */ , sectorIcons = new SymbologyXML::SymbolTable() /* SymbolTable *sectorIcon */ ); auto setDirectory = [this](SymbologyXML::SymbolTable * symTab, const QString &path, const char *errMsg) { if (!symTab->setDirectory(path, QStringList(QStringLiteral("*.xml")))) { ERROR_LOG(m_logger) << errMsg; } }; setDirectory( symbolFrames , QStringLiteral(":/Symbole/Attributes/symbolframes") , "Error while reading SymbolFrame-XML-Files!" ); setDirectory( branchSymbols , QStringLiteral(":/Symbole/Attributes/branch") , "Error while reading Branch/Truppengattung-XML-Files!" ); setDirectory( mobilitySymbols , QStringLiteral(":/Symbole/Attributes/mobility") , "Error while reading Mobility-XML-Files!" ); setDirectory( bcdabSymbols , QStringLiteral(":/Symbole/Attributes/bcdab") , "Error while reading BCDAB-XML-Files!" ); setDirectory( commandLevelSymbols , QStringLiteral(":/Symbole/Attributes/commandlevel") , "Error while reading CommandLevel-XML-Files!" ); setDirectory( vehicleSymbols , QStringLiteral(":/Symbole/Attributes/vehicle") , "Error while reading vehicle-XML-Files!" ); setDirectory( intrinsicMobilitySymbols, QStringLiteral(":/Symbole/Attributes/intrinsicmobility"), "Error while reading Intrinsic-Mobility-XML-Files!" ); setDirectory( armingSymbols , QStringLiteral(":/Symbole/Attributes/arming") , "Error while reading arming-XML-Files!" ); setDirectory( miscSymbols , QStringLiteral(":/Symbole/Attributes/misc") , "Error while reading misc-XML-Files!" ); setDirectory( addOnSymbols , QStringLiteral(":/Symbole/AddOnSymbole") , "Error while reading AddOn-Symbol-XML-Files!" ); setDirectory( sectorIcons , QStringLiteral(":/Symbole/Attributes/sectoricon") , "Error while reading sectoricon-XML-Files!" ); // ToDo: delete SymbolTables at program end to avoid warning because mem leak } TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return true; } void SymbolDictionaryImpl::connectSignals() { if (m_esri_dictionary) { connect(m_esri_dictionary, &Esri::ArcGISRuntime::DictionarySymbolStyle::fetchSymbolCompleted, this, &SymbolDictionaryImpl::onFetchSymbolCompleted); } } SymbologyType SymbolDictionaryImpl::symbologyType() const { return m_symbolDictionaryType_own; } QString SymbolDictionaryImpl::specificationType() const { return m_specificationType; } QString SymbolDictionaryImpl::symbologyResourcePath() const { return m_symbologyResourcePath; } const Esri::ArcGISRuntime::DictionarySymbolStyle *SymbolDictionaryImpl::getEsriSymbolDictionary() const { return m_esri_dictionary; } QImage SymbolDictionaryImpl::temporaryImage(int sizeL) { QImage image = ExtensionRenderer::createFullImage(sizeL, QColor(27, 27, 27, 127)); QPainter painter(&image); QPen pen(Qt::black); pen.setWidth(2.0); painter.setPen(pen); const QRect &rect = image.rect(); painter.drawRect(image.rect()); pen.setColor(Qt::darkGray); pen.setWidth(5.0); painter.setPen(pen); painter.drawLine( rect.topLeft(), QPointF(rect.width() / 2.0, rect.height() * 0.75)); painter.drawLine(QPointF(rect.width() / 2.0, rect.height() * 0.75), QPointF(rect.width() / 2.0, rect.height() * 0.25)); painter.drawLine(QPointF(rect.width() / 2.0, rect.height() * 0.25), rect.bottomRight()); return image; } QImage SymbolDictionaryImpl::notFoundImage(int sizeL, const QColor &fillColor) { QImage image = ExtensionRenderer::createFullImage(sizeL); QPainter painter(&image); painter.setPen(QPen(Qt::transparent)); painter.setBrush(QBrush(fillColor)); const int minLength = std::min(image.rect().width(), image.rect().height()) * 0.42; painter.drawEllipse(QPointF(image.rect().center()), minLength, minLength); painter.setPen(QPen(Qt::black)); QFont font = painter.font(); font.setBold(true); font.setPixelSize(static_cast(sizeL * 1.4)); painter.setFont(font); QPointF pos(image.width() * 0.30, image.height() * 0.80); painter.drawText(pos, QLatin1String("?")); return image; } QSharedPointer SymbolDictionaryImpl::notFoundSymbolResult(const SymbolDataToRender &symDataToRender) { QSharedPointer symResultNotFound = QSharedPointer(new SymbolResult); SymbolImage symImage; QColor finalFillColor = Qt::yellow; // maybe AdvancedSymbology::SymbolDataToRender::unknownColor(); if (symDataToRender.fillColorReplace && symDataToRender.fillColor.isValid()) finalFillColor.setAlpha(symDataToRender.fillColor.alpha()); symImage.setImage(notFoundImage(symDataToRender.sizeL, finalFillColor)); symResultNotFound->setSymbolDataToRender(symDataToRender); symResultNotFound->setSymbolImage(symImage); symResultNotFound->fireResultReady(); // ToDo: maybe async!?!? return symResultNotFound; } QSharedPointer SymbolDictionaryImpl::notFoundSymbolResult(int sizeL) { SymbolDataToRender symDataToRender; symDataToRender.sizeL = sizeL; symDataToRender.fillColorReplace = false; return notFoundSymbolResult(symDataToRender); } QStringList SymbolDictionaryImpl::symbologyAttributeKeyList() const { QStringList result = m_esri_dictionary->symbologyFieldNames() << m_esri_dictionary->textFieldNames(); result.removeDuplicates(); return result; } // QStringList SymbolDictionaryImpl::symbologyAttributeKeyList() const void SymbolDictionaryImpl::load() { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " ThreadId=" << QThread::currentThreadId(); if (m_isLoading) return; m_isLoading = true; if (!m_esri_dictionary || isLoaded()) return; m_esri_dictionary->load(); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } bool SymbolDictionaryImpl::isLoaded() const { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " ThreadId=" << QThread::currentThreadId(); bool isLoaded = loadStatus() == LoadStatus::Loaded; TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " isLoaded=" << isLoaded << " (loadStatus()=" << loadStatus() << ")"; return isLoaded; } LoadStatus SymbolDictionaryImpl::loadStatus() const { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; // LoadStatus for Esri: Esri::ArcGISRuntime::LoadStatus esriLoadStatus = Esri::ArcGISRuntime::LoadStatus::Unknown; // SHOULD we have an esriDict or not? If not, it´s already loaded: if (m_symbolDictionaryType_own == SymbologyType::None) esriLoadStatus = Esri::ArcGISRuntime::LoadStatus::Loaded; else { // Do we really have an esriDict? if (m_esri_dictionary) esriLoadStatus = m_esri_dictionary->loadStatus(); else esriLoadStatus = Esri::ArcGISRuntime::LoadStatus::Unknown; if (esriLoadStatus == Esri::ArcGISRuntime::LoadStatus::Loaded) { if (m_esriDictionaryProcessor && (! m_esriDictionaryProcessor->allJobsDone())) esriLoadStatus = Esri::ArcGISRuntime::LoadStatus::Loading; } } LoadStatus finalLoadStatus = static_cast(esriLoadStatus); TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " finalLoadStatus=" << finalLoadStatus << " (esriLoadStatus=" << static_cast(esriLoadStatus); return finalLoadStatus; } bool SymbolDictionaryImpl::setEtsSymbol(int fileId, const QString &encodedSidc, const SymbolProperties &properties, const QImage &image) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " fileId=" << fileId << " sidc='" << encodedSidc << "'" << " props.name='" << properties.name() << "'"; // some checks: if (encodedSidc.isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << " sidc is empty"; return false; } if (properties.name().isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << " properties.name() is empty, " << " sidc='" << encodedSidc << "'"; return false; } if (!SidcCodec::isEncoded(encodedSidc)) { ERROR_LOG(m_logger) << LOGFUNCTION << "sidc is not encoded, sidc='" << encodedSidc << "'"; return false; } if (!SidcCodec::checkSidcSyntax(encodedSidc)) { ERROR_LOG(m_logger) << LOGFUNCTION << "wrong encoding in sidc, sidc='" << encodedSidc << "'"; return false; } if (SidcCodec::decodeSidcType(encodedSidc) != SidcCodec::SidcType::CivilEts) { ERROR_LOG(m_logger) << LOGFUNCTION << "wrong sidc type, sidc='" << encodedSidc << "'. Type must be CivilEts!"; return false; } // 1: add to m_etsSidcSymbolProperties m_etsSidcSymbolProperties[encodedSidc] = properties; // 2: add to m_etsSidcImages m_etsSidcImages[encodedSidc] = image; // 3: add to m_etsSidcOrdered if (!m_etsSidcOrdered.contains(encodedSidc)) m_etsSidcOrdered.append(encodedSidc); // m_etsBaseSymbolModel : m_etsBaseSymbolModel->insertEtsSymbol(fileId, encodedSidc, properties, image); // : m_etsBaseSymbolModel // 4. emit signal to update view: // emit nearly immediately // QTimer::singleShot(0, [this]() { emit changed(); }); emit changed(); // emit delayed m_changedDelayTimer->start(); // is restart timer if already running TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " sidc='" << encodedSidc << "'"; return true; } void SymbolDictionaryImpl::removeEtsSymbol(int fileId) { // m_etsBaseSymbolModel : m_etsBaseSymbolModel->removeEtsSymbol(fileId); // : m_etsBaseSymbolModel } void SymbolDictionaryImpl::removeEtsSymbol(const QString &encodedSidc) { m_etsSidcSymbolProperties.remove(encodedSidc); m_etsSidcImages.remove(encodedSidc); m_etsSidcOrdered.removeAll(encodedSidc); emit changed(); m_changedDelayTimer->start(); // is restart timer if already running } void SymbolDictionaryImpl::clearEtsSymbols() { m_etsSidcSymbolProperties.clear(); m_etsSidcImages.clear(); m_etsSidcOrdered.clear(); emit changed(); m_changedDelayTimer->start(); // is restart timer if already running } int SymbolDictionaryImpl::addEtsSymbols(const QString &path) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " path ='" << path << "'" << " current etsSymbolCount=" << this->etsSymbolCount(); if (path.isEmpty()) return -1; // use qrc path!? int addedFiles = 0; QDir rootDir(path); // real work: DEBUG_LOG(m_logger) << "Searching in path '" << path << "'"; QStringList slFilePaths; QDirIterator dir(path, QDir::Files | QDir::Readable, QDirIterator::Subdirectories); const QString gif(QStringLiteral(".gif")); while (dir.hasNext()) { const QString &filePath = dir.next(); if (!filePath.endsWith(gif)) continue; slFilePaths.append(filePath); } // while // sort: slFilePaths.sort(); // iterate over sorted files: int fileId_substitute = 0; for (const auto &filePath : qAsConst(slFilePaths)) { ++fileId_substitute; // DEBUG_LOG(m_logger) << LOGFUNCTION << " found file= '" << filePath << "'"; QFileInfo fileInfo(filePath); const QString &curFilename = fileInfo.baseName(); // without suffix const QString &humanReadableName = curFilename; const QString key = curFilename; AdvancedSymbology::SymbolProperties symProps; // resultItem.key ='25152000_3' (mpl/symbol_dictionary_impl.cpp:418) // resultItem.name ='Maneuver Areas : Attack by Fire Position : Friend' (mpl/symbol_dictionary_impl.cpp:419) // resultItem.category ='Control Measure : Line' (mpl/symbol_dictionary_impl.cpp:420) // resultItem.symbolClass ='4' (mpl/symbol_dictionary_impl.cpp:421) // resultItem.tags ='Control Measure;Maneuver Areas;Attack by Fire Position;gct_arrow_with_tail;G*GPOAF---****X;MAIN;Line;Maneuver Areas : Attack by Fire Position;25152000' (mpl/symbol_dictionary_impl.cpp:422) // 1 key symProps.setKey(key); // 2 name symProps.setName(humanReadableName); symProps.setSidc(SidcCodec::encodeCivilEts(key)); // 3 category // eg: 3_Person (1_GIFs_Aktuell/WRett/3_Person/xy.gif // rootDir.absolutePath(): ':/advancedSymbology/CivilSymbols' // filepath : ':/advancedSymbology/CivilSymbols/Sonstige/Bundeswehr_Stelle.gif' // remove path from filePath: QStringRef filePathReduced = filePath.midRef(rootDir.absolutePath().size() + 1); // filepath 'Sonstige/Bundeswehr_Stelle.gif' QVector parts = filePathReduced.split(QLatin1Char('/')); // remove filename (to get dirname's as tags: parts.removeLast(); if (!parts.isEmpty()) { const QString &dirName = parts.last().toString(); // eg: 3_Person (1_GIFs_Aktuell/WRett/3_Person/xy.gif symProps.setCategory(dirName); } // 4 symbolClass symProps.setSymbolClass(QLatin1String("3")); // 18.10.2016, N.Thoden: 3 == PointSymbol (mil2525d.stylx - QuartzRelease) // 5 tags QStringList tags; tags.reserve(parts.size() + 2); tags.append(ETS_SYMBOL_KEYWORD); // 'ETS' for (const auto &item : qAsConst(parts)) tags.append(item.toString()); tags.append(humanReadableName); symProps.setTags(tags); // 6 GeometryType symProps.setGeometryType(GeometryType::Point); // ETS-Symbols are always Points // Image QImage image(filePath); if (!image.isNull()) { if (this->setEtsSymbol(fileId_substitute, symProps.sidc(), symProps, image)) ++addedFiles; else ERROR_LOG(m_logger) << LOGFUNCTION << " error proccessing1 " << filePath << "'"; } else ERROR_LOG(m_logger) << LOGFUNCTION << " error proccessing2 " << filePath << "'"; } DEBUG_LOG(m_logger) << LOGFUNCTION << " path ='" << path << "'," << " addedFiles ='" << addedFiles << "'"; TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " path ='" << path << "'" << " addedFiles ='" << addedFiles << "'" << " current etsSymbolCount=" << this->etsSymbolCount(); return addedFiles; } int AdvancedSymbology::SymbolDictionaryImpl::etsSymbolCount() const { return m_etsSidcSymbolProperties.count(); } QStringList SymbolDictionaryImpl::etsSymbols() const { return m_etsSidcOrdered; } bool SymbolDictionaryImpl::containsXmlSymbol(const QString &encodedSidc) const { return m_xmlSymbols.containsSidc(encodedSidc); } // bool SymbolDictionaryImpl::containsXmlSymbol(const QString &sidc) const bool SymbolDictionaryImpl::setXmlSymbol(int fileId, const QString &name, const QString &operatorName, qint64 lastModified, const QString &xmlSymbol, const SymbolProperties &properties) { SymbologyXML::SymbolReader xmlReader; SymbologyXML::Symbol symbol; if (!xmlReader.read(xmlSymbol, symbol)) { ERROR_LOG(m_logger) << LOGFUNCTION << " read symbol failed"; return false; } // maybe xmlSymbol has an internal name. updated: symbol.setName(name); symbol.setID(fileId); if (!setXmlSymbol(fileId, name, operatorName, lastModified, symbol, properties)) { ERROR_LOG(m_logger) << LOGFUNCTION << " setXmlSymbol failed"; return false; } return true; } bool SymbolDictionaryImpl::setXmlSymbol(int fileId, const QString &name, const QString &operatorName, qint64 lastModified, const SymbologyXML::Symbol &symbol, const SymbolProperties &properties) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " fileId=" << fileId << " name= '" << name << "'" << " operatorName='" << operatorName << "'" << " lastModified=" << lastModified << " symbol.id=" << symbol.id() << " symbol.getName()= '" << symbol.getName() << "'" << " properties.name= '" << properties.name() << "'"; const int symbolId = fileId; // decision: we use the fileId as symbolId const QString &skey = QString::number(symbolId); const QString &sidc = SidcCodec::encodeCivilXml(skey); if (m_xmlSymbols.containsSidc(sidc)) DEBUG_LOG(m_logger) << LOGFUNCTION << " symbol to insert is already defined, update... (id=" << symbol.id() << ")"; m_xmlSymbols[sidc] = symbol; SymbolProperties propsCopy = properties; propsCopy.setName(name); propsCopy.setSidc(sidc); propsCopy.setKey(skey); propsCopy.setGeometryType( GeometryType::Point ); propsCopy.setSymbolClass(QStringLiteral("3")); m_xmlSidcSymbolProperties[sidc] = propsCopy; // clear cache: if (m_xmlSymbolCache) { m_xmlSymbolCache->clearCache(SymbologyXML::PixmapSymbolCache::CacheType::CacheMainSymbols); m_xmlSymbolCache->clearCache(SymbologyXML::PixmapSymbolCache::CacheType::CacheFullSymbols); } // m_xmlBaseSymbolModel : if (!m_xmlBaseSymbolModel->insertXmlSymbol(fileId, operatorName, lastModified, symbol, propsCopy)) ERROR_LOG(m_logger) << LOGFUNCTION << " insertXmlSymbol failed, fileId=" << fileId; // : m_xmlBaseSymbolModel emit changed(); m_changedDelayTimer->start(); // is restart timer if already running return true; } void SymbolDictionaryImpl::removeXmlSymbol(int fileId) { // m_xmlBaseSymbolModel : if (!m_xmlBaseSymbolModel->removeXmlSymbol(fileId)) ERROR_LOG(m_logger) << LOGFUNCTION << " removeXmlSymbol failed: fileId= '" << fileId << " (m_xmlBaseSymbolModel->rowCount()=" << m_xmlBaseSymbolModel->rowCount(); } void SymbolDictionaryImpl::removeXmlSymbol(const QString &encodedSidc) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << ", sidc= '" << encodedSidc << "'" << ", m_xmlSymbols.size= " << m_xmlSymbols.size() << ", m_xmlSidcSymbolProperties.size= " << m_xmlSidcSymbolProperties.size(); const SidcCodec::SidcType sidcType = SidcCodec::decodeSidcType(encodedSidc); if (SidcCodec::SidcType::CivilXml == sidcType) { m_xmlSymbols.remove(encodedSidc); m_xmlSidcSymbolProperties.remove(encodedSidc); if (m_xmlSymbols.empty()) { if (m_xmlSymbolCache) { m_xmlSymbolCache->clearCache( SymbologyXML::PixmapSymbolCache::CacheMainSymbols ); m_xmlSymbolCache->clearCache( SymbologyXML::PixmapSymbolCache::CacheFullSymbols ); } } DEBUG_LOG(m_logger) << LOGFUNCTION << " emit CHANGED, sidc= '" << encodedSidc << "'" << ", sidcType= '" << static_cast(sidcType); emit changed(); m_changedDelayTimer->start(); // is restart timer if already running } else { WARNING_LOG(m_logger) << LOGFUNCTION << " Wrong sidc/sidcType: sidc= '" << encodedSidc << "'" << ", sidcType= '" << static_cast(sidcType); } TRACE_LOG(m_logger) << LOGFUNCTION << " END" << ", sidc= '" << encodedSidc << "'" << ", m_xmlSymbols.size= " << m_xmlSymbols.size() << ", m_xmlSidcSymbolProperties.size= " << m_xmlSidcSymbolProperties.size(); } void SymbolDictionaryImpl::clearXmlSymbols() { // m_xmlBaseSymbolModel : m_xmlBaseSymbolModel->clear(); m_xmlSymbols.clear(); m_xmlSidcSymbolProperties.clear(); if (nullptr == m_xmlSymbolCache) return; m_xmlSymbolCache->clearCache( SymbologyXML::PixmapSymbolCache::CacheMainSymbols ); m_xmlSymbolCache->clearCache( SymbologyXML::PixmapSymbolCache::CacheFullSymbols ); emit changed(); m_changedDelayTimer->start(); // is restart timer if already running } void SymbolDictionaryImpl::clearXmlSymbolCache() { if (!m_xmlSymbolCache) return; m_xmlSymbolCache->clearCache( SymbologyXML::PixmapSymbolCache::CacheMainSymbols ); m_xmlSymbolCache->clearCache( SymbologyXML::PixmapSymbolCache::CacheFullSymbols ); m_xmlSymbolCache->clearCache( SymbologyXML::PixmapSymbolCache::CacheAddOn ); } int SymbolDictionaryImpl::xmlSymbolCount() const { return m_xmlSymbols.count(); } // int SymbolDictionaryImpl::xmlSymbolCount() const QStringList SymbolDictionaryImpl::xmlSymbols() const { return m_xmlSymbols.keys(); } // QStringList SymbolDictionaryImpl::xmlSymbols() const QString SymbolDictionaryImpl::xmlSidc(const QString &xmlSymbol) const { SymbologyXML::SymbolReader xmlReader; SymbologyXML::Symbol symbol; if (xmlReader.read(xmlSymbol, symbol)) { return xmlSidc(symbol); } return QString(); } // QString xmlSidc(const QString &xmlSymbol) const QString SymbolDictionaryImpl::xmlSidc(const SymbologyXML::Symbol &symbol) const { return symbol.isValid() ? SidcCodec::encodeCivilXml(QString::number(symbol.id())) : QString(); } // QString SymbolDictionaryImpl::xmlSidc(const SymbologyXML::Symbol &symbol) const SymbologyXML::Symbol SymbolDictionaryImpl::xmlSymbol(const QString &encodedSidc) const { const SidcCodec::SidcType sidcType = SidcCodec::decodeSidcType(encodedSidc); if (SidcCodec::SidcType::CivilXml == sidcType) { if (m_xmlSymbols.contains(encodedSidc)) { return m_xmlSymbols[encodedSidc]; } } return SymbologyXML::Symbol(); } // SymbologyXML::Symbol SymbolDictionaryImpl::xmlSymbol(const QString &sidc) const SymbolProperties SymbolDictionaryImpl::xmlSymbolProps(const QString &encodedSidc) const { const SidcCodec::SidcType sidcType = SidcCodec::decodeSidcType(encodedSidc); if (SidcCodec::SidcType::CivilXml == sidcType) { if (m_xmlSidcSymbolProperties.contains(encodedSidc)) { return m_xmlSidcSymbolProperties[encodedSidc]; } } return SymbolProperties(); } // SymbolProperties SymbolDictionaryImpl::xmlSymbolProps(const QString &sidc) const void SymbolDictionaryImpl::onLoadStatusChanged(Esri::ArcGISRuntime::LoadStatus loadStatus) { DEBUG_LOG(m_logger) << LOGFUNCTION << " m_esri_dictionary(" << m_esri_dictionary << ")" << " symType= '" << this->symbologyType() << "'" << " loadStatus changes to " << static_cast(loadStatus) << " ThreadId=" << QThread::currentThreadId(); if (!m_esriDictionaryProcessor) return; if (loadStatus == Esri::ArcGISRuntime::LoadStatus::Loaded) { DEBUG_LOG(m_logger) << LOGFUNCTION << " ***** m_esri_dictionary " << m_esri_dictionary << " loading done "; m_esriDictionaryProcessor->init(m_esri_dictionary); m_esriDictionaryProcessor->prepare(); // continue if main job is finished: connect(m_esriDictionaryProcessor, &EsriDictionaryProcessor::allIsDone, this, &SymbolDictionaryImpl::onProcessorAllIsDone); } } void SymbolDictionaryImpl::onProcessorAllIsDone() { if (!m_esriDictionaryProcessor) return; // take results into own member (to have easy access) m_baseSymbolCodeToSymbolPropertyTable = m_esriDictionaryProcessor->baseSymbolCodeToSymbolPropertyTable(); // connect typical signals after main job of esriDictionaryProcessor is done: connectSignals(); INFO_LOG(m_logger) << LOGFUNCTION << " SymbolDictionaryImpl: m_baseSymbolCodeToSymbolPropertyTable.size=" << m_baseSymbolCodeToSymbolPropertyTable.size(); TRACE_LOG(m_logger) << LOGFUNCTION << " ***** emit loaded"; // setup some items to new symbol_model_esri: m_esriBaseSymbolModel->clear(); INFO_LOG(m_logger) << LOGFUNCTION << " ######### m_BaseSymbolModel.rowCount = " << m_esriBaseSymbolModel->rowCount(); emit loaded(); } BaseSymbolModel* SymbolDictionaryImpl::getBaseSymbolModelEsri() { if (m_esriBaseSymbolModel->rowCount() == 0) { // Note: 14.01. 2020, N. Thoden // initModel with the vecData does not really work. // The m_vecData of m_baseSymbolModel contains a Esri::ArcGISRuntime::Symbol*. // This object has to be created using the mainThread (V100.0 .. V100.6) // That could be done by the EsriDictionaryProcessorXYZ, but that increases // the startup time :-( // So it (Esri::ArcGISRuntime::Symbol) has to be created on demand. // That means if required (columns SEARCH_ICON, FETCH_ICON) within the model. // That possibility is choosen: const QList &searchResultList = m_esriDictionaryProcessor->getSearchResultListValid(); m_esriBaseSymbolModel->initModel(this, searchResultList, m_baseSymbolCodeToSymbolPropertyTable); } return m_esriBaseSymbolModel; } bool SymbolDictionaryImpl::searchResultIsValid(const QHash &key8LookupTable, const Esri::ArcGISRuntime::SymbolStyleSearchResult &resultItem, QString *toBeRemovedFromName) { if (!m_esriDictionaryProcessor) return false; return m_esriDictionaryProcessor->searchResultIsValid(key8LookupTable, resultItem, toBeRemovedFromName); } QSharedPointer SymbolDictionaryImpl::symbolPropertyImage(QSharedPointer symResult, const SymbolProperties &symbolProperties, int sizeL) const { const QString &sidc = symbolProperties.sidc(); const QString &originalKey = symbolProperties.originalKey(); TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " sidc='" << sidc << "'" << " originalKey='" << originalKey << "'" << " symbolProperties.name='" << symbolProperties.name() << "'" << " sizeL=" << sizeL; if (symResult.isNull()) { ERROR_LOG(m_logger) << LOGFUNCTION << " symResult is null!!!" << " props.name= '" << symbolProperties.name() << "'"; return notFoundSymbolResult(sizeL); } if (!SidcCodec::isEncoded(sidc)) { ERROR_LOG(m_logger) << LOGFUNCTION << " sidc not encoded: sidc= '" << sidc << "'"; return notFoundSymbolResult(sizeL); } if (originalKey.isEmpty()) { // ExtraSymbols: originalKey can be empty. if (!SidcCodec::isCivil(sidc)) ERROR_LOG(m_logger) << LOGFUNCTION << " originalKey is empty" << " props.name= '" << symbolProperties.name() << "'"; return notFoundSymbolResult(sizeL); } if (! m_esriDictionaryProcessor) { ERROR_LOG(m_logger) << LOGFUNCTION << " originalKey= '" << originalKey << "' no esriDictionaryProcessor"; return notFoundSymbolResult(sizeL); } if (! m_esriDictionaryProcessor->symbolPropertyImageContains(originalKey)) { ERROR_LOG(m_logger) << LOGFUNCTION << " originalKey= '" << originalKey << "' not found"; return notFoundSymbolResult(sizeL); } // transfer GeometryType: symResult->setGeometryType(symbolProperties.geometryType()); // transfer spiOriginalKey, spiSizeL - used at onEsriSymbolSwatchCompleted (save image in cache etc) SymbolDataToRender symDataToRender = symResult->symbolDataToRender(); symDataToRender.spiOriginalKey = originalKey; symDataToRender.spiSizeL = sizeL; symResult->setSymbolDataToRender(symDataToRender); symResult = m_esriDictionaryProcessor->symbolPropertyImage(symResult, originalKey); TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " originalKey='" << originalKey << "'" << " symbolProperties.name='" << symbolProperties.name() << "'" << " sizeL=" << sizeL; return symResult; } QSharedPointer SymbolDictionaryImpl::symbolPropertyImage(const SymbolProperties &symbolProperties, int sizeL) const { return symbolPropertyImage(QSharedPointer(new SymbolResult), symbolProperties, sizeL); } QStringList SymbolDictionaryImpl::cachedBaseSymbolCodes() const { return QStringList() << m_baseSymbolCodeToSymbolPropertyTable.keys() << m_etsSidcOrdered; } // QStringList SymbolDictionaryImpl::cachedBaseSymbolCodes() const SymbolProperties SymbolDictionaryImpl::symbolProperties(const QString &baseSymbolCode) const { auto it = m_baseSymbolCodeToSymbolPropertyTable.find(baseSymbolCode); if (it != m_baseSymbolCodeToSymbolPropertyTable.end()) { return it.value(); } // try ets: auto itEts = m_etsSidcSymbolProperties.find(baseSymbolCode); if (itEts != m_etsSidcSymbolProperties.end()) { return itEts.value(); } // try xml auto itXml = m_xmlSidcSymbolProperties.find(baseSymbolCode); if (itXml != m_xmlSidcSymbolProperties.end()) { return itXml.value(); } return SymbolProperties(); } QSharedPointer SymbolDictionaryImpl::fetchSymbol(const QString &sidc, int sizeL) const { SymbolDataToRender symDataToRender; symDataToRender.sidc = sidc; symDataToRender.sizeL = sizeL; return fetchSymbol(symDataToRender); } QSharedPointer SymbolDictionaryImpl::fetchSymbol(const SymbolDataToRender &symDataToRender) const { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " symDataToRender=" << symDataToRender.toString(); // const QString &sidc = symDataToRender.sidc; const QString &sidc = symDataToRender.sidcAccordingRole(); if (!SidcCodec::isEncoded(sidc)) { WARNING_LOG(m_logger) << LOGFUNCTION << " sidc not encoded: sidc= '" << sidc << "'"; return notFoundSymbolResult(symDataToRender); } // if (!SidcCodec::isEncoded(sidc)) // { // if (!SidcCodec::compatibilityMode()) // { // ERROR_LOG(m_logger) << LOGFUNCTION << " Wrong sidc codec" << " sidc= '" << sidc; // return notFoundSymbolResult(symDataToRender.sizeL); // } // sidc = SidcCodec::compatibilityIsM25DSymbol(sidc) ? SidcCodec::encodeComplete(SymbologyType::MIL2525D,sidc) : SidcCodec::encodeCivilEts(sidc); // just for backwards compatibility // } const SidcCodec::SidcType sidcType = SidcCodec::decodeSidcType(sidc); switch (sidcType) { case SidcCodec::SidcType::CivilEts : { // // handle ETS Symbols // auto it = m_etsSidcImages.find(sidc); if (it != m_etsSidcImages.end()) { // Create SymbolResult as ret-value QSharedPointer symResult = QSharedPointer(new SymbolResult); // save some values from caller: symResult->setSymbolDataToRender(symDataToRender); // ETS-Symbols are always Points symResult->setGeometryType(GeometryType::Point); const QImage &extraImg = it.value(); // Ets -> scaling is done in renderExtensions QColor colorToBeReplaced; auto itSymbolProps = m_etsSidcSymbolProperties.find(sidc); if (itSymbolProps != m_etsSidcSymbolProperties.end()) { const SymbolProperties &symProps = itSymbolProps.value(); colorToBeReplaced = symProps.colorToBeReplaced(); } #define FETCH_SYMBOL_ETS_CONCURRENT #ifdef FETCH_SYMBOL_ETS_CONCURRENT // symDataToRender contains flag 'symDataToRender' symResult->setFuture(QtConcurrent::run(renderExtensions, extraImg, symDataToRender, colorToBeReplaced, symDataToRender.renderJustCoreImage)); #else QImage imageCopy = extraImg; imageCopy = renderExtensions(imageCopy, symDataToRender, colorToBeReplaced, symDataToRender.renderJustCoreImage); SymbolImage symImage; symImage.setImage(imageCopy); symImage.setValid(true); symResult->setSymbolImage(symImage); symResult->fireResultReady(); // ToDo: maybe async!?!? #endif // FETCH_SYMBOL_ETS_CONCURRENT return symResult; } // if (m_extraSidcImages.contains(sidc)) } break; case SidcCodec::SidcType::CivilXml : { // // OR XML Symbols // if (m_xmlSymbolCache) { if (m_xmlSymbols.containsSidc(SidcCodec::extractCivilXmlBaseSidc(sidc))) //// use only base-sidc to lookup symbol { QSharedPointer symResult = QSharedPointer(new SymbolResult); symResult->setSymbolDataToRender(symDataToRender); symResult->setGeometryType(GeometryType::Point); /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Don't use async rendering here, because the image will be drawn with QGraphicsItems. This must * be done in main thread othwise the application can crash. * JT 2017/12/14 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ QImage image = renderXML_Symbol(m_xmlSymbolCache, m_xmlSymbols, symDataToRender, symDataToRender.renderJustCoreImage); SymbolImage symImage; symImage.setImage(image); symImage.setValid(!image.isNull()); symResult->setSymbolImage(symImage); symResult->fireResultReady(); return symResult; } else ERROR_LOG(m_logger) << LOGFUNCTION << " symbol must be inserted before usage (sidc=" << sidc << ")"; } else ERROR_LOG(m_logger) << LOGFUNCTION << " get instance of SymbologyXML::SymbolCacheWithTables failed."; } break; default: { // // handle Esri-Dictionary // if (! m_esri_dictionary) { ERROR_LOG(m_logger) << LOGFUNCTION << " no esri_dictionary"; return notFoundSymbolResult(symDataToRender); } if (! isLoaded()) { ERROR_LOG(m_logger) << LOGFUNCTION << " esri_dictionary not loaded"; return notFoundSymbolResult(symDataToRender); } const SymbologyType requestedSymType = SidcCodec::decodeSymbologyType(sidc); if (requestedSymType != m_symbolDictionaryType_own) { ERROR_LOG(m_logger) << LOGFUNCTION << " requested SymbologyType is not available" << " (sidc = '" << sidc << "'" << ", symbolDictionaryType_own=" << m_symbolDictionaryType_own << ", decodeSymbologyType=" << SidcCodec::decodeSymbologyType(sidc) << ")"; // return notFoundSymbolResult(symDataToRender); } if (SidcCodec::SidcType::Complete != SidcCodec::decodeSidcType(sidc)) { ERROR_LOG(m_logger) << LOGFUNCTION << " Sidc is not complete " << " sidc= '" << sidc; return notFoundSymbolResult(symDataToRender); } // linear search - maybe optimize. But how many different icons are really rendered in parallel? for (const QWeakPointer &res : qAsConst(m_findSymbol2symbolResult)) { auto sp = res.toStrongRef(); if (!sp) continue; if (sp->symbolDataToRender() == symDataToRender) { DEBUG_LOG(m_logger) << "Found render request: " << sp.data(); return sp; } } // Create SymbolResult as ret-value QSharedPointer symResult = QSharedPointer(new SymbolResult); symResult->setSymbolDataToRender(symDataToRender); const QVariantMap keys({{QStringLiteral("sidc"), SidcCodec::decodeRawSidc(sidc)}, // set sidc of symbol in case of MIL2525 {QStringLiteral("sic"), SidcCodec::decodeRawSidc(sidc)}}); // set sidc of symbol in case of APP6 Esri::ArcGISRuntime::TaskWatcher tw = m_esri_dictionary->fetchSymbol(keys); QUuid fsTaskId; if (tw.isValid()) { fsTaskId = tw.taskId(); if (doParanoiaChecks()) if (fsTaskId.isNull()) abort(); // store taskId in symResult: symResult->setTaskId(fsTaskId); // and put into map to be able to inform customer if swatch is completed: m_findSymbol2symbolResult[fsTaskId] = symResult.toWeakRef(); DEBUG_LOG(m_logger) << LOGFUNCTION << " TaskWatcher valid: fsTaskId=" << fsTaskId; } else { ERROR_LOG(m_logger) << LOGFUNCTION << " TaskWatcher invalid" << " ThreadId=" << QThread::currentThreadId(); if (doParanoiaChecks()) abort(); } TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " symDataToRender=" << symDataToRender.toString() << " fsTaskId=" << fsTaskId; return symResult; } } // switch(sidcType) return notFoundSymbolResult(symDataToRender); } QSharedPointer SymbolDictionaryImpl::findImage(const SymbolDataToRender &symDataToRender) const { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " symDataToRender=" << symDataToRender.toString(); const QString &sidc = symDataToRender.sidc; if (!SidcCodec::isEncoded(sidc)) { WARNING_LOG(m_logger) << LOGFUNCTION << " sidc not encoded: sidc= '" << sidc << "'"; return notFoundSymbolResult(symDataToRender); } const QString &baseSymbolCode = SidcBuilderBase::concreteSidcToBaseSymbolCode(sidc); // // !isCivil && m_baseSymbolCodeToSymbolPropertyTable.contains(baseSymbolCode) && // (geoType == GeometryType::Polygon) || (geoType == GeometryType::Polyline) // -> createSwatch() // if ((!SidcCodec::isCivil(sidc)) && m_baseSymbolCodeToSymbolPropertyTable.contains(baseSymbolCode)) { const SymbolProperties &symbolProperties = m_baseSymbolCodeToSymbolPropertyTable[baseSymbolCode]; const GeometryType geoType = symbolProperties.geometryType(); if ((geoType == GeometryType::Polygon) || (geoType == GeometryType::Polyline)) { // prepare SymbolResult as ret-value QSharedPointer preparedSymResult = QSharedPointer(new SymbolResult); // save some values from caller: preparedSymResult->setSymbolDataToRender(symDataToRender); QSharedPointer symResult = symbolPropertyImage(preparedSymResult, symbolProperties, symDataToRender.sizeL); // including GeometryType return symResult; } } TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " symDataToRender=" << symDataToRender.toString(); return fetchSymbol(symDataToRender); } QSharedPointer SymbolDictionaryImpl::findPreview(const QString &sidc, int sizeL) const { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " sidc='" << sidc << "'"; SymbolDataToRender symDataToRender; symDataToRender.role = Role::Neutral; symDataToRender.fillColor = Qt::yellow; // for XML symDataToRender.fillColor.setAlpha(128); // for XML symDataToRender.sidc = sidc; symDataToRender.sizeL = sizeL; symDataToRender.renderJustCoreImage = true; TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " sidc='" << sidc << "'"; return findImage(symDataToRender); // maybe avoid to surrounding area (for Injury, NoPosition etc) for PointSymbols!? } QSharedPointer SymbolDictionaryImpl::findBaseSymbol(const QString &sidc) const { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " sidc_8or20chars='" << sidc << "'"; if (!SidcCodec::isEncoded(sidc)) { WARNING_LOG(m_logger) << LOGFUNCTION << " sidc not encoded: sidc= '" << sidc << "'"; return notFoundSymbolResult(); } SidcCodec::SidcType sidcType = SidcCodec::decodeSidcType(sidc); switch(sidcType) { case SidcCodec::SidcType::CivilEts : { // // handle ExtraDir // auto it = m_etsSidcImages.find(sidc); if (it != m_etsSidcImages.end()) { // Create SymbolResult as ret-value QSharedPointer symResult = notFoundSymbolResult(); // save some values from caller: symResult->setSymbolDataToRender(SymbolDataToRender(it.key())); // ETS-Symbols are always Points symResult->setGeometryType(GeometryType::Point); const QImage &image = it.value(); SymbolImage symImage; symImage.setImage(image); symImage.setValid(true); symResult->setSymbolImage(symImage); symResult->fireResultReady(); // ToDo: maybe async!?!? return symResult; } // if (m_extraSidcImages.contains(sidc)) } break; case SidcCodec::SidcType::CivilXml : { // // OR handle XML symbols // if (m_xmlSymbolCache) { if (m_xmlSymbols.containsSidc(sidc)) { SymbolDataToRender symRender(sidc); int symWidth = qRound(symRender.sizeL*1.7); int symHeight = qRound(symRender.sizeL*1.7); QSharedPointer symResult = notFoundSymbolResult(); symResult->setSymbolDataToRender(symRender); symResult->setGeometryType(GeometryType::Point); const QImage image = m_xmlSymbolCache-> getComponentPixmap(m_xmlSymbols[sidc], SymbologyXML::PixmapSymbolCache::CacheType::CacheMainSymbols, SymbologyXML::SymbolTypes::AdditionalSymbolData(), QSize(symWidth,symHeight), QColor(), /* const QColor& rgbaLine */ QColor() /* rgbaRole */ ); if (!image.isNull()) { SymbolImage symImage; QTransform flipVertical; flipVertical.scale(1.0, -1.0); symImage.setImage(image.transformed(flipVertical)); symImage.setValid(true); symResult->setSymbolImage(symImage); symResult->fireResultReady(); TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " sidc='" << sidc << "'"; return symResult; } ERROR_LOG(m_logger) << LOGFUNCTION << "creating base symbol im from xml symbol failed (sidc=" << sidc << ")"; } else ERROR_LOG(m_logger) << LOGFUNCTION << "symbol must be inserted before usage (sidc=" << sidc << ")"; } else ERROR_LOG(m_logger) << LOGFUNCTION << "get instance of SymbologyXML::SymbolCacheWithTables failed."; } break; default: { // // OR handle Esri-Dictionary // // If requested (encoded) sidc is CMPL, so convert to BASE: QString baseSymbolCode = sidc; if (SidcCodec::decodeSidcType(sidc) == SidcCodec::SidcType::Complete) baseSymbolCode = SidcBuilderBase::concreteSidcToBaseSymbolCode(sidc); if (m_baseSymbolCodeToSymbolPropertyTable.contains(baseSymbolCode)) { const SymbolProperties &symbolProperties = m_baseSymbolCodeToSymbolPropertyTable[baseSymbolCode]; // prepare SymbolResult QSharedPointer preparedSymResult = QSharedPointer(new SymbolResult); // save some values from caller: preparedSymResult->setSymbolDataToRender(SymbolDataToRender(baseSymbolCode)); // Create SymbolResult as ret-value QSharedPointer symResult = symbolPropertyImage(preparedSymResult, symbolProperties); // including GeometryType return symResult; } // if (m_baseSymbolCodeToSymbolPropertyTable.contains(baseSymbolCode)) else { ERROR_LOG(m_logger) << LOGFUNCTION << " not found:" << " sidc='" << sidc << "'" << " baseSymbolCode='" << baseSymbolCode << "'"; } } break; } // switch(sidcType) TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " sidc='" << sidc << "'"; return notFoundSymbolResult(); } void SymbolDictionaryImpl::onFetchSymbolCompleted(QUuid fsTaskId, Esri::ArcGISRuntime::Symbol *symbol) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " symType= '" << this->symbologyType() << "'" << " fsTaskId= '" << fsTaskId << "'" << " symbol= '" << symbol << "'" << " ThreadId=" << QThread::currentThreadId(); if (! symbol) { ERROR_LOG(m_logger) << LOGFUNCTION << " START" << " symType= '" << this->symbologyType() << "'" << " fsTaskId= '" << fsTaskId << "'" << " no symbol!!!"; if (doParanoiaChecks()) abort(); return; } QSharedPointer symResult = m_findSymbol2symbolResult.value(fsTaskId); if (!symResult) { // it´s legal, that the result does not exist anymore. // Example: if used in a model::data(), the request occur very quickly. // So the second request may be done before the first can be answered. // So just a Debug msg: DEBUG_LOG(m_logger) << LOGFUNCTION << " SymbolResult not found or already been deleted" << " fsTaskId= '" << fsTaskId << "'"; return; } // // retrieve GeometryType : // const QString &sidc = symResult->symbolDataToRender().sidc; // try baseSymbolCode const QString &baseSymbolCode = SidcBuilderBase::concreteSidcToBaseSymbolCode(sidc); DEBUG_LOG(m_logger) << LOGFUNCTION << " baseSymbolCode = '" << baseSymbolCode << "'"; if (m_baseSymbolCodeToSymbolPropertyTable.contains(baseSymbolCode)) { const SymbolProperties &symbolProperties = m_baseSymbolCodeToSymbolPropertyTable[baseSymbolCode]; symResult->setGeometryType(symbolProperties.geometryType()); } else { ERROR_LOG(m_logger) << LOGFUNCTION << " baseSymbolCode not found for sidc='" << sidc << "'" << ", baseSymbolCode= '" << baseSymbolCode << "'" << ", fsTaskId= '" << fsTaskId << "'"; } // : retrieve GeometryType // FATAL_LOG(m_logger) << LOGFUNCTION << " XXXXXXXXXX geometryType = " << symResult->geometryType(); // // retrieve Image : // { connect(symbol, &Esri::ArcGISRuntime::Symbol::createSwatchCompleted, this, &SymbolDictionaryImpl::onSwatchCompleted); #ifdef USE_ARCADE_SYMBOLOGY Esri::ArcGISRuntime::TaskWatcher tw; if (symResult->geometryType() == GeometryType::Polygon) // area/Polygon -> request rectangle, otherwise quadratic swatch: tw = symbol->createSwatch(CREATE_SWATCH_PARAMETER.m_width, CREATE_SWATCH_PARAMETER.m_height, CREATE_SWATCH_PARAMETER.m_screenScale); else tw = symbol->createSwatch(CREATE_SWATCH_PARAMETER.m_height, CREATE_SWATCH_PARAMETER.m_height, CREATE_SWATCH_PARAMETER.m_screenScale); #else Esri::ArcGISRuntime::TaskWatcher tw = symbol->createSwatch(CREATE_SWATCH_SCREEN_SCALE); // 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; if (doParanoiaChecks()) if (csTaskId.isNull()) abort(); } else { ERROR_LOG(m_logger) << LOGFUNCTION << " csTaskWatcher invalid" << " ThreadId=" << QThread::currentThreadId(); if (doParanoiaChecks()) abort(); } if (! csTaskId.isNull()) m_createSwatch2findSymbolTaskId[csTaskId] = fsTaskId; else ERROR_LOG(m_logger) << LOGFUNCTION << " csTaskWatcher invalid"; } // retrieve Image TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " fs-taskId= '" << fsTaskId << "'" // << " cs-taskId= '" << csTaskId << "'" << " symbol= '" << symbol << "'"; } void SymbolDictionaryImpl::onSwatchCompleted(const QUuid &csTaskId, const QImage &image) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " symType= '" << this->symbologyType() << "'" << " csTaskId= '" << csTaskId << "'" << " image.isNull= '" << image.isNull() << "'" << " image.size()= '" << image.size().width() << " - " << image.size().height() << " image.colorTable().size()= '" << image.colorTable().size() << "'" << " ThreadId=" << QThread::currentThreadId(); auto it = m_createSwatch2findSymbolTaskId.find(csTaskId); if (it == m_createSwatch2findSymbolTaskId.end()) { ERROR_LOG(m_logger) << LOGFUNCTION << " no taskIdMapping" << " csTaskId= '" << csTaskId << "'"; if (doParanoiaChecks()) abort(); return; } // map to external (findSymbol) taskId const QUuid &fsTaskId = it.value(); DEBUG_LOG(m_logger) << LOGFUNCTION << " START" << " fsTaskId= '" << fsTaskId << "'" << " csTaskId= '" << csTaskId << "'"; //// emit findSymbolCompleted(fsTaskId, image); if (m_findSymbol2symbolResult.contains(fsTaskId)) { DEBUG_LOG(m_logger) << LOGFUNCTION << " SymbolResult found..." << " fsTaskId= '" << fsTaskId << "'"; QSharedPointer symResult = m_findSymbol2symbolResult[fsTaskId]; if (symResult) { DEBUG_LOG(m_logger) << LOGFUNCTION << " SymbolResult found and still valid..." << " fsTaskId= '" << fsTaskId << "'"; const SymbolDataToRender &symToRender = symResult->symbolDataToRender(); // DEBUG_LOG(m_logger) << LOGFUNCTION << " sidc=" << symToRender.sidc << " image.size=" << image.width() << " - " << image.height(); // 'renderExtensions': maybe transfer to workerThread: // N. Thoden, 2016-08-18: // On my maschine (AS502541 with i7;3.5GHz;octa-core) 100 calls took ~60ms // -> one call tooks ~0.6ms #define CONCURRENT #ifdef CONCURRENT // start computation like: // QFuture QtConcurrent::run(Function function, arg1, arc2...) // ^^^^^^^^^^ without() symResult->setFuture(QtConcurrent::run(renderExtensions, image, symToRender, QColor(), symToRender.renderJustCoreImage)); #else // CONCURRENT image = renderExtensions(image, symToRender, QColor(), symToRender.renderJustCoreImage); SymbolImage symImage; symImage.setImage(image); symImage.setValid(true); symResult->setSymbolImage(symImage); symResult->fireResultReady(); // ToDo: maybe async!?!? #endif // CONCURRENT } // else // { // // THis can happen in case of high number of request... // DEBUG_LOG(m_logger) << LOGFUNCTION << " SymbolResult has already been deleted" << " fsTaskId= '" << fsTaskId << "'"; // } // cleanup m_findSymbol2symbolResult.remove(fsTaskId); } else { ERROR_LOG(m_logger) << LOGFUNCTION << " no taskIdMapping" << " csTaskId= '" << csTaskId << "'"; if (doParanoiaChecks()) abort(); } // cleanup m_createSwatch2findSymbolTaskId.erase(it); // see also: "SymbolDictionary - findSymbolCompleted without taskId" // https://earlyadopter.esri.com/project/forum/thread.html?cap=FA3A32558952489280FB472D26FA8192&forid={A612A2F3-E344-4BD0-A1EA-0CE2DA5E694E}&topid={432F3A6B-E323-4628-BF39-77DB7BAAC9A1} Esri::ArcGISRuntime::Symbol *symbol = qobject_cast(sender()); if (symbol) symbol->deleteLater(); TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " symType= '" << this->symbologyType() << "'" << " csTaskId= '" << csTaskId << "'"; } QImage renderXML_Symbol(const QSharedPointer &xmlSymbolCache, const SidcCodec::SidcHashTable &xmlSymbols, const SymbolDataToRender &symbolDataToRender, bool renderJustCoreImage) { const QString &sidc = symbolDataToRender.sidc; const QColor &roleColor = symbolDataToRender.fillColor; const QColor &injuryColor = symbolDataToRender.injuryColor; SymbologyXML::SymbolTypes::t_state state = SymbologyXML::SymbolTypes::STATE_UNDEFINED; SymbologyXML::SymbolTypes::t_uncertainty uncertainty = SymbologyXML::SymbolTypes::UNCERTAINTY_UNDEFINED; SymbologyXML::SymbolTypes::t_affil affil = SymbologyXML::SymbolTypes::AFFIL_UNDEFINED; SymbologyXML::SymbolTypes::t_mob mob = SymbologyXML::SymbolTypes::MOB_UNDEFINED; SymbologyXML::SymbolTypes::AdditionalSymbolData additionalSymbolData; const QString & rawSidc = SidcCodec::decodeRawSidc(sidc); QString symbolId; SymbologyXML::Symbol xmlSymbol; if (SymbologyXML::xmlSymbolDecodeId(rawSidc,symbolId,state,uncertainty,affil,mob)) { xmlSymbol = xmlSymbols[SidcCodec::encodeCivilXml(symbolId)]; xmlSymbol.setAffil( affil ); xmlSymbol.setMob( mob ); additionalSymbolData.m_state = state; additionalSymbolData.m_uncertainty = uncertainty; } else { xmlSymbol = xmlSymbols[sidc]; // use base symbol additionalSymbolData.m_state = SymbologyXML::SymbolTypes::STATE_PRESENT; additionalSymbolData.m_uncertainty = SymbologyXML::SymbolTypes::UNCERTAINTY_UNDEFINED; } double symbolScale = symbolDataToRender.sizeL * 1.7 * ((xmlSymbol.getScale() > 0) ? xmlSymbol.getScale() : 1.0); const QSize &imgSize = QSize(symbolScale, symbolScale); // transfer symbolDataToRender.role to xmlSymbol.affiliation: // ToDo: distinguish using role from Sidc or from symbolDataToRender if (SymbologyXML::SymbolTypes::AFFIL_UNDEFINED == affil) { xmlSymbol.setAffil( SymbologyXML::Symbol::roleToAffiliation(symbolDataToRender.role) ); } additionalSymbolData.m_filltype = SymbologyXML::SymbolTypes::FILLTYPE_FILLED; additionalSymbolData.m_scale = SymbologyXML::SymbolTypes::SCALE_UNDEFINED; if (!renderJustCoreImage) { switch(symbolDataToRender.injury) { case Injury::Hit : additionalSymbolData.m_vbg.vbg = SymbologyXML::SymbolTypes::vbg_et::MITTLERER_TREFFER; break; case Injury::MobilityKill : additionalSymbolData.m_vbg.vbg = SymbologyXML::SymbolTypes::vbg_et::BEWEGUNGSUNFAEHIG; break; case Injury::WeaponKill : additionalSymbolData.m_vbg.vbg = SymbologyXML::SymbolTypes::vbg_et::AUSFALL_WAFFENANLAGE; break; case Injury::TotalKill : additionalSymbolData.m_vbg.vbg = SymbologyXML::SymbolTypes::vbg_et::TOTALAUSFALL; break; case Injury::NoInjury : default : additionalSymbolData.m_vbg.vbg = SymbologyXML::SymbolTypes::vbg_et::EINSATZBEREIT; break; } additionalSymbolData.m_vbg.color = injuryColor; for (const AdditionalSymbolType additionalSymbolType : qAsConst(symbolDataToRender.additionalSymbols)) { switch (additionalSymbolType) { case AdditionalSymbolType::Manipulation : additionalSymbolData.m_modifier.setManipulation(true); break; case AdditionalSymbolType::NoPosition : additionalSymbolData.m_modifier.setNoPosition(true); break; case AdditionalSymbolType::BiteError : additionalSymbolData.m_modifier.setByteError(true); break; case AdditionalSymbolType::NbcProtection : additionalSymbolData.m_modifier.setNbcProtection(true); break; default : break; } } } QImage image = xmlSymbolCache->getPixmap(xmlSymbol, additionalSymbolData, imgSize, QColor(0,0,0), /* const QColor& rgbaLine */ roleColor, /* rgbaRole */ true, true); if (image.isNull()) { LOGGER_T m_logger = getLogger("SymbolDictionaryImpl"); ERROR_LOG(m_logger) << LOGFUNCTION << " render XML symbol (sidc='" << sidc <<"') failed."; return QImage(); } if (symbolDataToRender.marking) { QImage fullImage(image.width(), image.height(), QImage::Format_ARGB32); fullImage.fill(Qt::transparent); // TODO: check ExtensionRenderer::renderMarking(fullImage, roleColor); QPainter painter(&fullImage); painter.drawImage(image.rect(), image, image.rect()); painter.end(); return fullImage; } return image; } // QImage renderXML_Symbol(...) QImage renderExtensions(QImage &coreImage, const SymbolDataToRender &symbolDataToRender, const QColor &colorToBeReplaced, bool renderJustCoreImage) { LOGGER_T m_logger = getLogger("SymbolDictionaryImpl"); TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " ThreadId=" << QThread::currentThreadId() << " symbolDataToRender= " << symbolDataToRender.toString(); if (coreImage.isNull()) WARNING_LOG(m_logger) << LOGFUNCTION << " coreImage.isNull"; if (symbolDataToRender.sizeL <= 0) WARNING_LOG(m_logger) << LOGFUNCTION << " sizeL is <= 0! sizeL=" << symbolDataToRender.sizeL; // manual delay : //QThread::msleep(1000); // : manual delay const QString &sidc = symbolDataToRender.sidcAccordingRole(); // // Prepare FullImage // QImage fullImage = ExtensionRenderer::createFullImage(symbolDataToRender.sizeL); bool isEts = (SidcCodec::SidcType::CivilEts == SidcCodec::decodeSidcType(sidc)); // // CoreImage // // // CoreImage: replace Color: // if (symbolDataToRender.fillColorReplace) { // oldColor QVector oldColorVec; if (isEts) { if (colorToBeReplaced.isValid()) oldColorVec.append(colorToBeReplaced); else ERROR_LOG(m_logger) << LOGFUNCTION << " colorToBeReplaced is invalid" << " sidc=" << symbolDataToRender.sidc; } else oldColorVec = ExtensionRenderer::identityToColor(sidc); // coreImage.save(QLatin1String("/tmp/dummy.png")); for (const QColor &oldColor : qAsConst(oldColorVec)) { if (oldColor.isValid()) { if (ExtensionRenderer::replaceColor(coreImage, oldColor.rgba(), symbolDataToRender.fillColor.rgba())) break; // finish loop, if first oldColor was found (and image is modfied) } else { ERROR_LOG(m_logger) << LOGFUNCTION << " sidc='" << symbolDataToRender.sidc << "', identityToColor failed"; } } } // if (symbolDataToRender.fillColorReplace) // // CoreImage: Scale coreImage (to fit into fullImage): // QSize newSize; if (isEts) { newSize = fullImage.size(); // ETS has more height then width } else { #ifdef USE_ARCADE_SYMBOLOGY newSize = fullImage.size(); #else Q_DECL_CONSTEXPR double esriL = (CREATE_SWATCH_SCREEN_SCALE * 20) * 1.3; // to let the big clover&diamond (incl. echelon) fit into the marking rect // ( 5 * 20) * 1.15 = 115 newSize = coreImage.size() * static_cast(symbolDataToRender.sizeL) / esriL; #endif // USE_ARCADE_SYMBOLOGY // replace honoring the hotSpot by simple scaling : int hqFdTf = QStringRef(&sidc, 7, 1).toInt(); // int amplifierDescriptor = QStringRef(&sidc, 8, 2).toInt(); if (((hqFdTf > 0) && ((hqFdTf < 8)))) // HeadQuarter/FeintDummy/TaskFore have more height then width newSize = fullImage.size(); // : replace honoring the hotSpot by simple scaling } // if (isEts) // scale coreImage = coreImage.scaled(newSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); if (coreImage.isNull()) WARNING_LOG(m_logger) << LOGFUNCTION << " image isNull "; // Just CoreImage? if (renderJustCoreImage) return coreImage; // // FullImage: 1. create Marking: // if (symbolDataToRender.marking) ExtensionRenderer::renderMarking(fullImage, symbolDataToRender.fillColor); // // FullImage: 2. merge CoreImage // ExtensionRenderer::mergeCoreImageIntoFullImage(fullImage, coreImage); // // FullImage: 5. Injury and AddOns: // ExtensionRenderer::renderInjuryAddSymbols(fullImage, symbolDataToRender.injury, symbolDataToRender.injuryColor, symbolDataToRender.additionalSymbols, symbolDataToRender.fillColor); if (fullImage.isNull()) WARNING_LOG(m_logger) << LOGFUNCTION << " fullImage is Null!"; TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " ThreadId=" << QThread::currentThreadId() << " symbolDataToRender= " << symbolDataToRender.toString(); return fullImage; } QStringList SymbolDictionaryImpl::symbolModifierKeys() const { if (!m_esriDictionaryProcessor) return QStringList(); return m_esriDictionaryProcessor->symbolModifierKeys(); } bool SymbolDictionaryImpl::symbolModifierData(const QString &key, ModifierData &data) const { if (!m_esriDictionaryProcessor) return false; return m_esriDictionaryProcessor->symbolModifierData(key, data); } QList SymbolDictionaryImpl::symbolModifierList(SidcDefinitions::SymbolSet symbolSet, unsigned int modifierSector) const { if (!m_esriDictionaryProcessor) return QList(); return m_esriDictionaryProcessor->symbolModifierList(symbolSet, modifierSector).toList(); } QList SymbolDictionaryImpl::symbolModifierList(const QString &baseSymbolCode, unsigned int modifierSector) const { if (!m_esriDictionaryProcessor) return QList(); // check baseSymbolCode, must contains 8 digits if (!QRegExp(QStringLiteral("^[0-9]{8,8}$")).exactMatch(baseSymbolCode)) { ERROR_LOG(m_logger) << LOGFUNCTION << " Invalid baseSymbolCode \"" << baseSymbolCode << "\". Must be made up of 8 digits."; return QList(); } SidcDefinitions::SymbolSet symbolSetBaseCode = SidcDefinitions::castToSymbolSet(baseSymbolCode.midRef(0, 2).toUInt()); return m_esriDictionaryProcessor->symbolModifierList(symbolSetBaseCode, modifierSector).toList(); } int SymbolDictionaryImpl::symbologySpecificCount() const { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; int count = m_baseSymbolCodeToSymbolPropertyTable.size(); TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " count=" << count; return count; } bool SymbolDictionaryImpl::insertXmlTestSymbol(const SymbolProperties &symbolProps) { QString xmlString = QString::fromUtf8 ( R"XML( test_lines )XML" ); return setXmlSymbol(4711, QLatin1String("Name"), QLatin1String("OperatorTestSymbol"), 0, xmlString, symbolProps); } bool SymbolDictionaryImpl::insertXmlComplexTestSymbol(const SymbolProperties &symbolProps) { QString xmlString = QString::fromUtf8 ( R"XML( test complex symbol )XML" ); return setXmlSymbol(4712, QLatin1String("Name"), QLatin1String("OperatorComplexSymbol"), 0, xmlString, symbolProps); } int SymbolDictionaryImpl::count() const { return symbologySpecificCount() + etsSymbolCount() + xmlSymbolCount(); } QString SymbolDictionaryImpl::toString() const { QString str; str.append(QStringLiteral(" m_specificationType='%1'").arg(m_specificationType)); str.append(QStringLiteral(" SymbologyType=%1").arg(AdvancedSymbology::toString(m_symbolDictionaryType_own))); str.append(QStringLiteral(" m_symbologyResourcePath='%1'").arg(m_symbologyResourcePath)); str.append(QStringLiteral(" m_createSwatch2findSymbolTaskId.size=%1").arg(m_createSwatch2findSymbolTaskId.size())); str.append(QStringLiteral(" m_findSymbol2symbolResult.size=%1").arg(m_findSymbol2symbolResult.size())); return str; } QStringList SymbolDictionaryImpl::symbologyFieldNamesInternal() const { if (!m_esri_dictionary) return QStringList(); return m_esri_dictionary->symbologyFieldNames(); } QStringList SymbolDictionaryImpl::textFieldNamesInternal() const { if (!m_esri_dictionary) return QStringList(); return m_esri_dictionary->textFieldNames(); } void SymbolDictionaryImpl::onErrorDictionary(const Esri::ArcGISRuntime::Error &error) { FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- occured: " << " code=" << error.code() << ", domain=" << static_cast(error.domain()) << ", extendedErrorType=" << static_cast(error.extendedErrorType()) << ", message='" << error.message() << "'" << ", additionalMessage='" << error.additionalMessage() << "'"; } int minNumberOfPoints(GeometryType gt) { switch (gt) { case GeometryType::Undefined: return -1; case GeometryType::Point: return 1; case GeometryType::Polyline: return 2; case GeometryType::Polygon: return 3; } return -1; } //----------------------------------------------------------------------------- // output operator: //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- QString toString(LoadStatus type) { switch (type) { case LoadStatus::Loaded: return QLatin1String("Loaded"); case LoadStatus::Loading: return QLatin1String("Loading"); case LoadStatus::FailedToLoad: return QLatin1String("FailedToLoad"); case LoadStatus::NotLoaded: return QLatin1String("NotLoaded"); case LoadStatus::Unknown: return QLatin1String("Unknown"); } // switch return QLatin1String("LoadStatusToStringERROR"); } //----------------------------------------------------------------------------- QString toString(SymbologyType type) { switch (type) { case SymbologyType::None: return QLatin1String("None"); case SymbologyType::MIL2525C: return QLatin1String("MIL2525C"); case SymbologyType::APP6B: return QLatin1String("APP-6(B)"); case SymbologyType::APP6D: return QLatin1String("APP-6(D)"); case SymbologyType::MIL2525D: return QLatin1String("MIL2525D"); } // switch return QLatin1String("symbologyToStringERROR"); } bool fromString(SymbologyType &symbologyType, const QString &str) { if (str.compare(QLatin1String("NONE"), Qt::CaseInsensitive) == 0) { symbologyType = SymbologyType::None; return true; } if (str.compare(QLatin1String("APP6B"), Qt::CaseInsensitive) == 0) { symbologyType = SymbologyType::APP6B; return true; } if (str.compare(QLatin1String("APP6D"), Qt::CaseInsensitive) == 0) { symbologyType = SymbologyType::APP6D; return true; } if (str.compare(QLatin1String("MIL2525C"), Qt::CaseInsensitive) == 0) { symbologyType = SymbologyType::MIL2525C; return true; } if (str.compare(QLatin1String("MIL2525D"), Qt::CaseInsensitive) == 0) { symbologyType = SymbologyType::MIL2525D; return true; } return false; } QString toString(AdvancedSymbology::GeometryType gt) { switch (gt) { case GeometryType::Undefined: return QLatin1String("Undefined"); case GeometryType::Point: return QLatin1String("Point"); case GeometryType::Polyline: return QLatin1String("Polyline"); case GeometryType::Polygon: return QLatin1String("Polygon"); } return QLatin1String("geometryTypeToStringERROR"); } //----------------------------------------------------------------------------- QString toString(AdvancedSymbology::Role value) { switch (value) { case Role::Pending: return QLatin1String("Pending"); case Role::Unknown: return QLatin1String("Unknown"); case Role::AssumedFriend: return QLatin1String("AssumedFriend"); case Role::Friend: return QLatin1String("Friend"); case Role::Neutral: return QLatin1String("Neutral"); case Role::SuspectJoker: return QLatin1String("SuspectJoker"); case Role::HostileFaker: return QLatin1String("HostileFaker"); } // switch return QLatin1String("ERROR-Role"); } std::ostream &operator<<(std::ostream &stream, const AdvancedSymbology::Role &value) { stream << AdvancedSymbology::toString(value); return stream; } std::ostream &operator<<(std::ostream &stream, const AdvancedSymbology::LoadStatus &value) { stream << AdvancedSymbology::toString(value); return stream; } std::ostream &operator<<(std::ostream &stream, const AdvancedSymbology::GeometryType &value) { stream << AdvancedSymbology::toString(value); return stream; } std::ostream &operator<<(std::ostream &stream, const AdvancedSymbology::SymbologyType &value) { stream << AdvancedSymbology::toString(value); return stream; } } // namespace AdvancedSymbology