QString OsmAnd::ResolvedMapStyle_P::dumpRuleNodeOutputValues( const std::shared_ptr<const RuleNode>& ruleNode, const QString& prefix, const bool allowOverride) const { QString dump; const auto builtinValueDefs = MapStyleBuiltinValueDefinitions::get(); for (const auto& ruleValueEntry : rangeOf(constOf(ruleNode->values))) { const auto valueDefId = ruleValueEntry.key(); const auto& valueDef = getValueDefinitionById(valueDefId); // Skip all non-Output values if (valueDef->valueClass != MapStyleValueDefinition::Class::Output) continue; const auto& resolvedRuleValue = ruleValueEntry.value(); dump += prefix + QString(QLatin1String("%1 %2 = %3;\n")) .arg(allowOverride ? QLatin1String("setOrOverride") : QLatin1String("setIfNotSet")) .arg(valueDef->name) .arg(dumpResolvedValue(resolvedRuleValue, valueDef->dataType)); } return dump; }
std::shared_ptr<OsmAnd::ResolvedMapStyle_P::RuleNode> OsmAnd::ResolvedMapStyle_P::resolveRuleNode( const std::shared_ptr<const UnresolvedMapStyle::RuleNode>& unresolvedRuleNode) { const std::shared_ptr<RuleNode> resolvedRuleNode(new RuleNode( unresolvedRuleNode->isSwitch)); // Resolve values for (const auto& itUnresolvedValueEntry : rangeOf(constOf(unresolvedRuleNode->values))) { const auto& name = itUnresolvedValueEntry.key(); const auto& value = itUnresolvedValueEntry.value(); // Find value definition id and object const auto valueDefId = getValueDefinitionIdByName(name); const auto& valueDef = getValueDefinitionById(valueDefId); if (valueDefId < 0 || !valueDef) { LogPrintf(LogSeverityLevel::Warning, "Ignoring unknown value '%s' = '%s'", qPrintable(name), qPrintable(value)); continue; } // Try to resolve value ResolvedValue resolvedValue; if (!resolveValue(value, valueDef->dataType, valueDef->isComplex, resolvedValue)) { LogPrintf(LogSeverityLevel::Warning, "Ignoring value for '%s' since '%s' can not be resolved", qPrintable(name), qPrintable(value)); continue; } resolvedRuleNode->values[valueDefId] = resolvedValue; } // <switch>/<case> subnodes for (const auto& unresolvedChild : constOf(unresolvedRuleNode->oneOfConditionalSubnodes)) { const auto resolvedChild = resolveRuleNode(unresolvedChild); if (!resolvedChild) return nullptr; resolvedRuleNode->oneOfConditionalSubnodes.push_back(resolvedChild); } // <apply> subnodes for (const auto& unresolvedChild : constOf(unresolvedRuleNode->applySubnodes)) { const auto resolvedChild = resolveRuleNode(unresolvedChild); if (!resolvedChild) return nullptr; resolvedRuleNode->applySubnodes.push_back(resolvedChild); } return resolvedRuleNode; }
void OsmAnd::RasterizerEnvironment_P::setSettings(const QHash< QString, QString >& newSettings) { QHash< std::shared_ptr<const MapStyleValueDefinition>, MapStyleValue > resolvedSettings; resolvedSettings.reserve(newSettings.size()); for(const auto& itSetting : rangeOf(newSettings)) { const auto& name = itSetting.key(); const auto& value = itSetting.value(); // Resolve input-value definition by name std::shared_ptr<const MapStyleValueDefinition> inputValueDef; if(!owner->style->resolveValueDefinition(name, inputValueDef)) { LogPrintf(LogSeverityLevel::Warning, "Setting of '%s' to '%s' impossible: failed to resolve input value definition failed with such name"); continue; } // Parse value MapStyleValue parsedValue; if(!owner->style->_p->parseValue(inputValueDef, value, parsedValue)) { LogPrintf(LogSeverityLevel::Warning, "Setting of '%s' to '%s' impossible: failed to parse value"); continue; } resolvedSettings.insert(inputValueDef, parsedValue); } setSettings(resolvedSettings); }
void OsmAnd::RasterizerEnvironment_P::applyTo( MapStyleEvaluator& evaluator ) const { QMutexLocker scopedLocker(&_settingsChangeMutex); for(const auto& settingEntry : rangeOf(constOf(_settings))) { const auto& valueDef = settingEntry.key(); const auto& settingValue = settingEntry.value(); switch(valueDef->dataType) { case MapStyleValueDataType::Integer: evaluator.setIntegerValue(valueDef->id, settingValue.isComplex ? settingValue.asComplex.asInt.evaluate(owner->displayDensityFactor) : settingValue.asSimple.asInt); break; case MapStyleValueDataType::Float: evaluator.setFloatValue(valueDef->id, settingValue.isComplex ? settingValue.asComplex.asFloat.evaluate(owner->displayDensityFactor) : settingValue.asSimple.asFloat); break; case MapStyleValueDataType::Boolean: case MapStyleValueDataType::String: case MapStyleValueDataType::Color: assert(!settingValue.isComplex); evaluator.setIntegerValue(valueDef->id, settingValue.asSimple.asUInt); break; } } }
bool OsmAnd::MapRendererKeyedSymbolsResource::uploadToGPU() { bool ok; bool anyUploadFailed = false; const auto link_ = link.lock(); const auto collection = static_cast<MapRendererKeyedResourcesCollection*>(&link_->collection); QHash< std::shared_ptr<MapSymbol>, std::shared_ptr<const GPUAPI::ResourceInGPU> > uploaded; for (const auto& symbol : constOf(_sourceData->symbolsGroup->symbols)) { // Prepare data and upload to GPU std::shared_ptr<const GPUAPI::ResourceInGPU> resourceInGPU; ok = resourcesManager->uploadSymbolToGPU(symbol, resourceInGPU); // If upload have failed, stop if (!ok) { LogPrintf(LogSeverityLevel::Error, "Failed to upload keyed symbol"); anyUploadFailed = true; break; } // Mark this symbol as uploaded uploaded.insert(symbol, qMove(resourceInGPU)); } // If at least one symbol failed to upload, consider entire tile as failed to upload, // and unload its partial GPU resources if (anyUploadFailed) { uploaded.clear(); return false; } // All resources have been uploaded to GPU successfully by this point _retainableCacheMetadata = _sourceData->retainableCacheMetadata; _sourceData.reset(); for (const auto& entry : rangeOf(constOf(uploaded))) { const auto& symbol = entry.key(); auto& resource = entry.value(); // Unload GPU data from symbol, since it's uploaded already resourcesManager->releaseGpuUploadableDataFrom(symbol); // Move reference _resourcesInGPU.insert(symbol, qMove(resource)); } return true; }
bool OsmAnd::ResolvedMapStyle_P::collectConstants() { // Process styles chain in reverse order, top-most parent is the last element auto citUnresolvedMapStyle = iteratorOf(owner->unresolvedMapStylesChain); citUnresolvedMapStyle.toBack(); while (citUnresolvedMapStyle.hasPrevious()) { const auto& unresolvedMapStyle = citUnresolvedMapStyle.previous(); for (const auto& constantEntry : rangeOf(constOf(unresolvedMapStyle->constants))) _constants[constantEntry.key()] = constantEntry.value(); } return true; }
void OsmAnd::MapPresentationEnvironment_P::setSettings(const QHash< QString, QString >& newSettings) { QHash< ResolvedMapStyle::ValueDefinitionId, MapStyleConstantValue > resolvedSettings; resolvedSettings.reserve(newSettings.size()); for (const auto& itSetting : rangeOf(newSettings)) { const auto& name = itSetting.key(); const auto& value = itSetting.value(); // Resolve input-value definition by name const auto valueDefId = owner->resolvedStyle->getValueDefinitionIdByName(name); const auto valueDef = owner->resolvedStyle->getValueDefinitionById(valueDefId); if (!valueDef || valueDef->valueClass != MapStyleValueDefinition::Class::Input) { LogPrintf(LogSeverityLevel::Warning, "Setting of '%s' to '%s' impossible: failed to resolve input value definition failed with such name", qPrintable(name), qPrintable(value)); continue; } // Parse value MapStyleConstantValue parsedValue; if (!owner->resolvedStyle->parseValue(value, valueDef, parsedValue)) { LogPrintf(LogSeverityLevel::Warning, "Setting of '%s' to '%s' impossible: failed to parse value", qPrintable(name), qPrintable(value)); continue; } // Special case for night mode if (valueDefId == MapStyleBuiltinValueDefinitions::get()->id_INPUT_NIGHT_MODE) { _desiredStubsStyle = (parsedValue.asSimple.asInt == 1) ? MapStubStyle::Dark : MapStubStyle::Light; } resolvedSettings.insert(valueDefId, parsedValue); } setSettings(resolvedSettings); }
OsmAnd::GeoInfoPresenter::MapObject::MapObject( const std::shared_ptr<const GeoInfoDocument>& geoInfoDocument_, const std::shared_ptr<const GeoInfoDocument::ExtraData>& extraData /*= nullptr*/) : geoInfoDocument(geoInfoDocument_) { const auto mapping = std::make_shared<AttributeMapping>(); attributeMapping = mapping; if (extraData) { auto lastUsedRuleId = 0; const auto values = extraData->getValues(); for (const auto valueEntry : rangeOf(constOf(values))) { const auto& tag = valueEntry.key(); auto value = valueEntry.value().toString(); if (tag == QLatin1String("color")) value = Utilities::resolveColorFromPalette(value, false); uint32_t attributeId; if (!mapping->encodeTagValue(tag, value, &attributeId)) { attributeId = ++lastUsedRuleId; mapping->registerMapping(attributeId, tag, value); } additionalAttributeIds.append(attributeId); } } mapping->verifyRequiredMappingRegistered(); additionalAttributeIds.append(geoInfoDocument->locationMarks.isEmpty() ? mapping->waypointsNotPresentAttributeId : mapping->waypointsPresentAttributeId); additionalAttributeIds.append(geoInfoDocument->tracks.isEmpty() ? mapping->trackpointsNotPresentAttributeId : mapping->trackpointsPresentAttributeId); additionalAttributeIds.append(geoInfoDocument->routes.isEmpty() ? mapping->routepointsNotPresentAttributeId : mapping->routepointsPresentAttributeId); }
void OsmAnd::MapStyle::dump( MapStyleRulesetType type, const QString& prefix /*= QString()*/ ) const { const auto& rules = _d->obtainRulesRef(type); for(const auto& ruleEntry : rangeOf(constOf(rules))) { auto tag = _d->getTagString(ruleEntry.key()); auto value = _d->getValueString(ruleEntry.key()); auto rule = ruleEntry.value(); OsmAnd::LogPrintf(LogSeverityLevel::Debug, "%sRule [%s (%d):%s (%d)]", qPrintable(prefix), qPrintable(tag), _d->getTagStringId(ruleEntry.key()), qPrintable(value), _d->getValueStringId(ruleEntry.key())); rule->dump(prefix); } }
void OsmAnd::MapStyleEvaluator_P::fillResultFromRuleNode( const std::shared_ptr<const IMapStyle::IRuleNode>& ruleNode, IntermediateEvaluationResult& outResultStorage, const bool allowOverride) const { const auto& values = ruleNode->getValuesRef(); for (const auto& ruleValueEntry : rangeOf(constOf(values))) { const auto valueDefId = ruleValueEntry.key(); const auto& valueDef = owner->mapStyle->getValueDefinitionRefById(valueDefId); // Skip all non-Output values if (valueDef->valueClass != MapStyleValueDefinition::Class::Output) continue; // If value already defined and override not allowed, do nothing if (outResultStorage.contains(valueDefId) && !allowOverride) continue; // Store result outResultStorage.set(valueDefId, ruleValueEntry.value()); } }
void OsmAnd::RoutePlannerContext::RoutingSubsectionContext::collectRoads( QList< std::shared_ptr<const Model::Road> >& output, QMap<uint64_t, std::shared_ptr<const Model::Road> >* duplicatesRegistry /*= nullptr*/ ) { for(const auto& routeSegmentEntry : rangeOf(constOf(_roadSegments))) { auto routeSegment = routeSegmentEntry.value(); while(routeSegment) { const auto& road = routeSegment->road; const auto isDuplicate = duplicatesRegistry && duplicatesRegistry->contains(road->id); if (!isDuplicate) { if (duplicatesRegistry) duplicatesRegistry->insert(road->id, road); output.push_back(road); } routeSegment = routeSegment->_next; } } /*TODO: to Alexey what is this? if (routes != null) { } else if (searchResult != null) { RouteDataObject[] objects = searchResult.objects; if (objects != null) { for(RouteDataObject ro : objects) { if (ro != null && !excludeDuplications.contains(ro.id)) { excludeDuplications.put(ro.id, ro); toFillIn.add(ro); } } } } */ }
bool OsmAnd::MapRendererTiledSymbolsResource::uploadToGPU() { typedef std::pair< std::shared_ptr<MapSymbol>, std::shared_ptr<const GPUAPI::ResourceInGPU> > SymbolResourceEntry; bool ok; bool anyUploadFailed = false; const auto link_ = link.lock(); const auto collection = static_cast<MapRendererTiledSymbolsResourcesCollection*>(&link_->collection); // Unique QMultiHash< std::shared_ptr<GroupResources>, SymbolResourceEntry > uniqueUploaded; for (const auto& groupResources : constOf(_uniqueGroupsResources)) { for (const auto& symbol : constOf(groupResources->group->symbols)) { // Prepare data and upload to GPU std::shared_ptr<const GPUAPI::ResourceInGPU> resourceInGPU; ok = resourcesManager->uploadSymbolToGPU(symbol, resourceInGPU); // If upload have failed, stop if (!ok) { LogPrintf(LogSeverityLevel::Error, "Failed to upload unique symbol in %dx%d@%d tile", tileId.x, tileId.y, zoom); anyUploadFailed = true; break; } // Mark this symbol as uploaded uniqueUploaded.insert(groupResources, qMove(SymbolResourceEntry(symbol, resourceInGPU))); } if (anyUploadFailed) break; } // Shared QMultiHash< std::shared_ptr<GroupResources>, SymbolResourceEntry > sharedUploaded; QMultiHash< std::shared_ptr<GroupResources>, SymbolResourceEntry > sharedReferenced; for (const auto& groupResources : constOf(_referencedSharedGroupsResources)) { if (groupResources->group->symbols.isEmpty()) continue; // This check means "continue if shared symbols were uploaded by other resource" // This check needs no special handling, since all GPU resources work is done from same thread. if (!groupResources->resourcesInGPU.isEmpty()) { for (const auto& entryResourceInGPU : rangeOf(constOf(groupResources->resourcesInGPU))) { const auto& symbol = entryResourceInGPU.key(); auto& resourceInGPU = entryResourceInGPU.value(); sharedReferenced.insert(groupResources, qMove(SymbolResourceEntry(symbol, resourceInGPU))); } continue; } for (const auto& symbol : constOf(groupResources->group->symbols)) { // Prepare data and upload to GPU std::shared_ptr<const GPUAPI::ResourceInGPU> resourceInGPU; ok = resourcesManager->uploadSymbolToGPU(symbol, resourceInGPU); // If upload have failed, stop if (!ok) { LogPrintf(LogSeverityLevel::Error, "Failed to upload unique symbol in %dx%d@%d tile", tileId.x, tileId.y, zoom); anyUploadFailed = true; break; } // Mark this symbol as uploaded sharedUploaded.insert(groupResources, qMove(SymbolResourceEntry(symbol, resourceInGPU))); } if (anyUploadFailed) break; } // If at least one symbol failed to upload, consider entire tile as failed to upload, // and unload its partial GPU resources if (anyUploadFailed) { uniqueUploaded.clear(); sharedUploaded.clear(); return false; } // All resources have been uploaded to GPU successfully by this point, // so it's safe to walk across symbols and remove bitmaps: // Unique for (const auto& entry : rangeOf(constOf(uniqueUploaded))) { const auto& groupResources = entry.key(); const auto& symbol = entry.value().first; auto& resource = entry.value().second; // Unload GPU data from symbol, since it's uploaded already resourcesManager->releaseGpuUploadableDataFrom(symbol); // Add GPU resource reference _symbolToResourceInGpuLUT.insert(symbol, resource); #if OSMAND_LOG_MAP_SYMBOLS_TO_GPU_RESOURCES_MAP_CHANGES LogPrintf(LogSeverityLevel::Debug, "Inserted GPU resource %p for map symbol %p in %p (uploadToGPU-uniqueUploaded)", resource.get(), symbol.get(), this); #endif // OSMAND_LOG_MAP_SYMBOLS_TO_GPU_RESOURCES_MAP_CHANGES groupResources->resourcesInGPU.insert(qMove(symbol), qMove(resource)); } // Shared (uploaded) for (const auto& entry : rangeOf(constOf(sharedUploaded))) { const auto& groupResources = entry.key(); auto symbol = entry.value().first; auto& resource = entry.value().second; // Unload GPU data from symbol, since it's uploaded already resourcesManager->releaseGpuUploadableDataFrom(symbol); // Add GPU resource reference _symbolToResourceInGpuLUT.insert(symbol, resource); #if OSMAND_LOG_MAP_SYMBOLS_TO_GPU_RESOURCES_MAP_CHANGES LogPrintf(LogSeverityLevel::Debug, "Inserted GPU resource %p for map symbol %p in %p (uploadToGPU-sharedUploaded)", resource.get(), symbol.get(), this); #endif // OSMAND_LOG_MAP_SYMBOLS_TO_GPU_RESOURCES_MAP_CHANGES groupResources->resourcesInGPU.insert(qMove(symbol), qMove(resource)); } // Shared (referenced) for (const auto& entry : rangeOf(constOf(sharedReferenced))) { const auto& groupResources = entry.key(); auto symbol = entry.value().first; auto& resource = entry.value().second; // Add GPU resource reference _symbolToResourceInGpuLUT.insert(symbol, resource); #if OSMAND_LOG_MAP_SYMBOLS_TO_GPU_RESOURCES_MAP_CHANGES LogPrintf(LogSeverityLevel::Debug, "Inserted GPU resource %p for map symbol %p in %p (uploadToGPU-sharedReferenced)", resource.get(), symbol.get(), this); #endif // OSMAND_LOG_MAP_SYMBOLS_TO_GPU_RESOURCES_MAP_CHANGES } return true; }
QString OsmAnd::ResolvedMapStyle_P::dump(const QString& prefix) const { QString dump; const auto decodeMapValueDataType = [] (const MapStyleValueDataType dataType) -> QString { switch (dataType) { case MapStyleValueDataType::Boolean: return QLatin1String("boolean"); case MapStyleValueDataType::Integer: return QLatin1String("integer"); case MapStyleValueDataType::Float: return QLatin1String("float"); case MapStyleValueDataType::String: return QLatin1String("string"); case MapStyleValueDataType::Color: return QLatin1String("color"); } return QLatin1String("unknown"); }; // Constants dump += prefix + QLatin1String("// Constants:\n"); for (const auto& constant : rangeOf(constOf(_constants))) dump += prefix + QString(QLatin1String("def %1 = \"%2\";\n")).arg(constant.key()).arg(constant.value()); dump += prefix + QLatin1String("\n"); // Parameters dump += prefix + QLatin1String("// Parameters:\n"); for (const auto& parameter : constOf(_parameters)) { if (parameter->possibleValues.isEmpty()) { dump += prefix + QString(QLatin1String("param %1 %2; // %3 (%4) \n")) .arg(decodeMapValueDataType(parameter->dataType)) .arg(getStringById(parameter->nameId)) .arg(parameter->title) .arg(parameter->description); } else { QStringList decodedPossibleValues; for (const auto possibleValue : constOf(parameter->possibleValues)) decodedPossibleValues.append(dumpConstantValue(possibleValue, parameter->dataType)); dump += prefix + QString(QLatin1String("param %1 %2 = (%3); // %4 (%5) \n")) .arg(decodeMapValueDataType(parameter->dataType)) .arg(getStringById(parameter->nameId)) .arg(decodedPossibleValues.join(QLatin1String(" | "))) .arg(parameter->title) .arg(parameter->description); } } dump += prefix + QLatin1String("\n"); // Attributes dump += prefix + QLatin1String("// Attributes:\n"); for (const auto& attribute : constOf(_attributes)) { dump += prefix + QString(QLatin1String("attribute %1:\n")).arg(getStringById(attribute->nameId)); dump += dumpRuleNode(attribute->rootNode, false, prefix + QLatin1String("\t")); } dump += prefix + QLatin1String("\n"); // Order rules dump += prefix + QLatin1String("// Order rules:\n"); for (const auto& ruleEntry : rangeOf(constOf(_rulesets[static_cast<unsigned int>(MapStyleRulesetType::Order)]))) { dump += prefix + QString(QLatin1String("order %1 = %2:\n")) .arg(getStringById(ruleEntry.key().tagId)) .arg(getStringById(ruleEntry.key().valueId)); dump += dumpRuleNode(ruleEntry.value()->rootNode, true, prefix + QLatin1String("\t")); } dump += prefix + QLatin1String("\n"); // Point rules dump += prefix + QLatin1String("// Point rules:\n"); for (const auto& ruleEntry : rangeOf(constOf(_rulesets[static_cast<unsigned int>(MapStyleRulesetType::Point)]))) { dump += prefix + QString(QLatin1String("point %1 = %2:\n")) .arg(getStringById(ruleEntry.key().tagId)) .arg(getStringById(ruleEntry.key().valueId)); dump += dumpRuleNode(ruleEntry.value()->rootNode, true, prefix + QLatin1String("\t")); } dump += prefix + QLatin1String("\n"); // Polyline rules dump += prefix + QLatin1String("// Polyline rules:\n"); for (const auto& ruleEntry : rangeOf(constOf(_rulesets[static_cast<unsigned int>(MapStyleRulesetType::Polyline)]))) { dump += prefix + QString(QLatin1String("polyline %1 = %2:\n")) .arg(getStringById(ruleEntry.key().tagId)) .arg(getStringById(ruleEntry.key().valueId)); dump += dumpRuleNode(ruleEntry.value()->rootNode, true, prefix + QLatin1String("\t")); } dump += prefix + QLatin1String("\n"); // Polygon rules dump += prefix + QLatin1String("// Polygon rules:\n"); for (const auto& ruleEntry : rangeOf(constOf(_rulesets[static_cast<unsigned int>(MapStyleRulesetType::Polygon)]))) { dump += prefix + QString(QLatin1String("polygon %1 = %2:\n")) .arg(getStringById(ruleEntry.key().tagId)) .arg(getStringById(ruleEntry.key().valueId)); dump += dumpRuleNode(ruleEntry.value()->rootNode, true, prefix + QLatin1String("\t")); } dump += prefix + QLatin1String("\n"); // Text rules dump += prefix + QLatin1String("// Text rules:\n"); for (const auto& ruleEntry : rangeOf(constOf(_rulesets[static_cast<unsigned int>(MapStyleRulesetType::Text)]))) { dump += prefix + QString(QLatin1String("text %1 = %2:\n")) .arg(getStringById(ruleEntry.key().tagId)) .arg(getStringById(ruleEntry.key().valueId)); dump += dumpRuleNode(ruleEntry.value()->rootNode, true, prefix + QLatin1String("\t")); } dump += prefix + QLatin1String("\n"); return dump; }
QString OsmAnd::ResolvedMapStyle_P::dumpRuleNode( const std::shared_ptr<const RuleNode>& ruleNode, const bool rejectSupported, const QString& prefix) const { QString dump; const auto builtinValueDefs = MapStyleBuiltinValueDefinitions::get(); bool hasInputValueTests = false; bool hasDisable = false; for (const auto& ruleValueEntry : rangeOf(constOf(ruleNode->values))) { const auto valueDefId = ruleValueEntry.key(); const auto& valueDef = getValueDefinitionById(valueDefId); if (valueDef->valueClass == MapStyleValueDefinition::Class::Input) hasInputValueTests = true; else if (valueDef->valueClass == MapStyleValueDefinition::Class::Output && valueDef->name == QLatin1String("disable")) hasDisable = true; if (hasInputValueTests && hasDisable) break; } if (hasInputValueTests) { dump += prefix + QLatin1String("local testPassed = true;\n"); for (const auto& ruleValueEntry : rangeOf(constOf(ruleNode->values))) { const auto valueDefId = ruleValueEntry.key(); const auto& valueDef = getValueDefinitionById(valueDefId); // Test only input values if (valueDef->valueClass != MapStyleValueDefinition::Class::Input) continue; const auto& resolvedRuleValue = ruleValueEntry.value(); //bool evaluationResult = false; if (valueDefId == builtinValueDefs->id_INPUT_MINZOOM) { dump += prefix + QString(QLatin1String("if (testPassed) testPassed = (%1 <= %2);\n")) .arg(dumpResolvedValue(resolvedRuleValue, MapStyleValueDataType::Integer)) .arg(valueDef->name); } else if (valueDefId == builtinValueDefs->id_INPUT_MAXZOOM) { dump += prefix + QString(QLatin1String("if (testPassed) testPassed = (%1 >= %2);\n")) .arg(dumpResolvedValue(resolvedRuleValue, MapStyleValueDataType::Integer)) .arg(valueDef->name); } else if (valueDefId == builtinValueDefs->id_INPUT_ADDITIONAL) { dump += prefix + QString(QLatin1String("if (testPassed and inputMapObject) testPassed = (inputMapObject contains %1);\n")) .arg(dumpResolvedValue(resolvedRuleValue, MapStyleValueDataType::String)); } else if (valueDefId == builtinValueDefs->id_INPUT_TEST) { dump += prefix + QString(QLatin1String("if (testPassed) testPassed = (%1 == 1);\n")) .arg(valueDef->name); } else if (valueDef->dataType == MapStyleValueDataType::Float) { dump += prefix + QString(QLatin1String("if (testPassed) testPassed = (%1 == %2);\n")) .arg(dumpResolvedValue(resolvedRuleValue, MapStyleValueDataType::Float)) .arg(valueDef->name); } else if (valueDef->dataType == MapStyleValueDataType::Integer) { dump += prefix + QString(QLatin1String("if (testPassed) testPassed = (%1 == %2);\n")) .arg(dumpResolvedValue(resolvedRuleValue, MapStyleValueDataType::Integer)) .arg(valueDef->name); } else if (valueDef->dataType == MapStyleValueDataType::Boolean) { dump += prefix + QString(QLatin1String("if (testPassed) testPassed = (%1 == %2);\n")) .arg(dumpResolvedValue(resolvedRuleValue, MapStyleValueDataType::Boolean)) .arg(valueDef->name); } else if (valueDef->dataType == MapStyleValueDataType::String) { dump += prefix + QString(QLatin1String("if (testPassed) testPassed = (%1 == %2);\n")) .arg(dumpResolvedValue(resolvedRuleValue, MapStyleValueDataType::String)) .arg(valueDef->name); } else { dump += prefix + QString(QLatin1String("// Unknown condition with '%1'\n")) .arg(valueDef->name); } } if (rejectSupported) dump += prefix + QLatin1String("if (not testPassed) reject;\n"); else dump += prefix + QLatin1String("if (not testPassed) exit;\n"); dump += prefix + QLatin1String("\n"); } if (hasDisable) { if (rejectSupported) dump += prefix + QLatin1String("if (disable) reject;\n"); else dump += prefix + QLatin1String("if (disable) exit;\n"); dump += prefix + QLatin1String("\n"); } if (!ruleNode->isSwitch) dump += dumpRuleNodeOutputValues(ruleNode, prefix, true); if (!ruleNode->oneOfConditionalSubnodes.isEmpty()) { dump += prefix + QLatin1String("local atLeastOneConditionalMatched = false;\n"); for (const auto& oneOfConditionalSubnode : constOf(ruleNode->oneOfConditionalSubnodes)) { dump += prefix + QLatin1String("if (not atLeastOneConditionalMatched):\n"); dump += dumpRuleNode(oneOfConditionalSubnode, false, prefix + QLatin1String("\t")); dump += prefix + QLatin1String("\tatLeastOneConditionalMatched = true;\n"); } if (ruleNode->isSwitch) { if (rejectSupported) dump += prefix + QLatin1String("if (not atLeastOneConditionalMatched) reject;\n"); else dump += prefix + QLatin1String("if (not atLeastOneConditionalMatched) exit;\n"); } dump += prefix + QLatin1String("\n"); } if (ruleNode->isSwitch) dump += dumpRuleNodeOutputValues(ruleNode, prefix, false); if (!ruleNode->applySubnodes.isEmpty()) { for (const auto& applySubnode : constOf(ruleNode->applySubnodes)) { dump += prefix + QLatin1String("apply:\n"); dump += dumpRuleNode(applySubnode, false, prefix + QLatin1String("\t")); } dump += prefix + QLatin1String("\n"); } return dump; }
void OsmAnd::ObfsCollection_P::collectSources() const { QWriteLocker scopedLocker1(&_collectedSourcesLock); QReadLocker scopedLocker2(&_sourcesOriginsLock); // Capture how many invalidations are going to be processed const auto invalidationsToProcess = _collectedSourcesInvalidated.loadAcquire(); if (invalidationsToProcess == 0) return; #if OSMAND_DEBUG const auto collectSources_Begin = std::chrono::high_resolution_clock::now(); #endif // Check all previously collected sources auto itCollectedSourcesEntry = mutableIteratorOf(_collectedSources); while(itCollectedSourcesEntry.hasNext()) { const auto& collectedSourcesEntry = itCollectedSourcesEntry.next(); const auto& originId = collectedSourcesEntry.key(); auto& collectedSources = collectedSourcesEntry.value(); const auto& itSourceOrigin = _sourcesOrigins.constFind(originId); // If current source origin was removed, // remove entire each collected source related to it if (itSourceOrigin == _sourcesOrigins.cend()) { // Ensure that ObfFile is not being read anywhere for(const auto& itCollectedSource : rangeOf(collectedSources)) { const auto obfFile = itCollectedSource.value(); //NOTE: OBF should have been locked here, but since file is gone anyways, this lock is quite useless itCollectedSource.value().reset(); assert(obfFile.use_count() == 1); } itCollectedSourcesEntry.remove(); continue; } // Check for missing files auto itObfFileEntry = mutableIteratorOf(collectedSources); while(itObfFileEntry.hasNext()) { const auto& sourceFilename = itObfFileEntry.next().key(); if (QFile::exists(sourceFilename)) continue; const auto obfFile = itObfFileEntry.value(); //NOTE: OBF should have been locked here, but since file is gone anyways, this lock is quite useless itObfFileEntry.remove(); assert(obfFile.use_count() == 1); } // If all collected sources for current source origin are gone, // remove entire collection attached to source origin ID if (collectedSources.isEmpty()) { itCollectedSourcesEntry.remove(); continue; } } // Find all files uncollected sources for(const auto& itEntry : rangeOf(constOf(_sourcesOrigins))) { const auto& originId = itEntry.key(); const auto& entry = itEntry.value(); auto itCollectedSources = _collectedSources.find(originId); if (itCollectedSources == _collectedSources.end()) itCollectedSources = _collectedSources.insert(originId, QHash<QString, std::shared_ptr<ObfFile> >()); auto& collectedSources = *itCollectedSources; if (entry->type == SourceOriginType::Directory) { const auto& directoryAsSourceOrigin = std::static_pointer_cast<const DirectoryAsSourceOrigin>(entry); QFileInfoList obfFilesInfo; Utilities::findFiles(directoryAsSourceOrigin->directory, QStringList() << QLatin1String("*.obf"), obfFilesInfo, directoryAsSourceOrigin->isRecursive); for(const auto& obfFileInfo : constOf(obfFilesInfo)) { const auto& obfFilePath = obfFileInfo.canonicalFilePath(); if (collectedSources.constFind(obfFilePath) != collectedSources.cend()) continue; auto obfFile = new ObfFile(obfFilePath, obfFileInfo.size()); collectedSources.insert(obfFilePath, std::shared_ptr<ObfFile>(obfFile)); } if (directoryAsSourceOrigin->isRecursive) { QFileInfoList directoriesInfo; Utilities::findDirectories(directoryAsSourceOrigin->directory, QStringList() << QLatin1String("*"), directoriesInfo, true); for(const auto& directoryInfo : constOf(directoriesInfo)) { const auto canonicalPath = directoryInfo.canonicalFilePath(); if (directoryAsSourceOrigin->watchedSubdirectories.contains(canonicalPath)) continue; _fileSystemWatcher->addPath(canonicalPath); directoryAsSourceOrigin->watchedSubdirectories.insert(canonicalPath); } } } else if (entry->type == SourceOriginType::File) { const auto& fileAsSourceOrigin = std::static_pointer_cast<const FileAsSourceOrigin>(entry); if (!fileAsSourceOrigin->fileInfo.exists()) continue; const auto& obfFilePath = fileAsSourceOrigin->fileInfo.canonicalFilePath(); if (collectedSources.constFind(obfFilePath) != collectedSources.cend()) continue; auto obfFile = new ObfFile(obfFilePath, fileAsSourceOrigin->fileInfo.size()); collectedSources.insert(obfFilePath, std::shared_ptr<ObfFile>(obfFile)); } } // Decrement invalidations counter with number of processed onces _collectedSourcesInvalidated.fetchAndAddOrdered(-invalidationsToProcess); #if OSMAND_DEBUG const auto collectSources_End = std::chrono::high_resolution_clock::now(); const std::chrono::duration<float> collectSources_Elapsed = collectSources_End - collectSources_Begin; LogPrintf(LogSeverityLevel::Info, "Collected OBF sources in %fs", collectSources_Elapsed.count()); #endif }
bool OsmAnd::MapStyleEvaluator_P::evaluate( const Model::MapObject* const mapObject, const std::shared_ptr<const MapStyleRule>& rule, MapStyleEvaluationResult* const outResultStorage, bool evaluateChildren) { // Check all values of a rule until all are checked. for(const auto& ruleValueEntry : rangeOf(constOf(rule->_d->_values))) { const auto& valueDef = ruleValueEntry.key(); // Test only input values, the ones that start with INPUT_* if(valueDef->valueClass != MapStyleValueClass::Input) continue; const auto& ruleValue = ruleValueEntry.value(); const auto& inputValue = _inputValues[valueDef->id]; bool evaluationResult = false; if(valueDef->id == _builtinValueDefs->id_INPUT_MINZOOM) { assert(!ruleValue.isComplex); evaluationResult = (ruleValue.asSimple.asInt <= inputValue.asInt); } else if(valueDef->id == _builtinValueDefs->id_INPUT_MAXZOOM) { assert(!ruleValue.isComplex); evaluationResult = (ruleValue.asSimple.asInt >= inputValue.asInt); } else if(valueDef->id == _builtinValueDefs->id_INPUT_ADDITIONAL) { if(!mapObject) evaluationResult = true; else { assert(!ruleValue.isComplex); const auto& strValue = owner->style->_d->lookupStringValue(ruleValue.asSimple.asUInt); auto equalSignIdx = strValue.indexOf('='); if(equalSignIdx >= 0) { const auto& tag = strValue.mid(0, equalSignIdx); const auto& value = strValue.mid(equalSignIdx + 1); evaluationResult = mapObject->containsTypeSlow(tag, value, true); } else evaluationResult = false; } } else if(valueDef->id == _builtinValueDefs->id_INPUT_TEST) { evaluationResult = (inputValue.asInt == 1); } else if(valueDef->dataType == MapStyleValueDataType::Float) { const auto lvalue = ruleValue.isComplex ? ruleValue.asComplex.asFloat.evaluate(owner->displayDensityFactor) : ruleValue.asSimple.asFloat; evaluationResult = qFuzzyCompare(lvalue, inputValue.asFloat); } else { const auto lvalue = ruleValue.isComplex ? ruleValue.asComplex.asInt.evaluate(owner->displayDensityFactor) : ruleValue.asSimple.asInt; evaluationResult = (lvalue == inputValue.asInt); } // If at least one value of rule does not match, it's failure if(!evaluationResult) return false; } // Fill output values from rule to result storage, if requested if(outResultStorage) { for(const auto& ruleValueEntry : rangeOf(constOf(rule->_d->_values))) { const auto& valueDef = ruleValueEntry.key(); if(valueDef->valueClass != MapStyleValueClass::Output) continue; const auto& ruleValue = ruleValueEntry.value(); switch(valueDef->dataType) { case MapStyleValueDataType::Boolean: assert(!ruleValue.isComplex); outResultStorage->_d->_values[valueDef->id] = (ruleValue.asSimple.asUInt == 1); break; case MapStyleValueDataType::Integer: outResultStorage->_d->_values[valueDef->id] = ruleValue.isComplex ? ruleValue.asComplex.asInt.evaluate(owner->displayDensityFactor) : ruleValue.asSimple.asInt; break; case MapStyleValueDataType::Float: outResultStorage->_d->_values[valueDef->id] = ruleValue.isComplex ? ruleValue.asComplex.asFloat.evaluate(owner->displayDensityFactor) : ruleValue.asSimple.asFloat; break; case MapStyleValueDataType::String: // Save value of a string instead of it's id outResultStorage->_d->_values[valueDef->id] = owner->style->_d->lookupStringValue(ruleValue.asSimple.asUInt); break; case MapStyleValueDataType::Color: assert(!ruleValue.isComplex); outResultStorage->_d->_values[valueDef->id] = ruleValue.asSimple.asUInt; break; } } } if(evaluateChildren) { for(const auto& child : constOf(rule->_d->_ifElseChildren)) { const auto evaluationResult = evaluate(mapObject, child, outResultStorage, true); if(evaluationResult) break; } for(const auto& child : constOf(rule->_d->_ifChildren)) evaluate(mapObject, child, outResultStorage, true); } return true; }
bool OsmAnd::MapStyleEvaluator_P::evaluate( const MapObject* const mapObject, const std::shared_ptr<const IMapStyle::IRuleNode>& ruleNode, const std::shared_ptr<const InputValues>& inputValues, bool& outDisabled, IntermediateEvaluationResult* const outResultStorage, OnDemand<IntermediateEvaluationResult>& constantEvaluationResult) const { // Check all values of a rule until all are checked. const auto& ruleNodeValues = ruleNode->getValuesRef(); for (const auto& ruleValueEntry : rangeOf(constOf(ruleNodeValues))) { const auto valueDefId = ruleValueEntry.key(); const auto& valueDef = owner->mapStyle->getValueDefinitionRefById(valueDefId); // Test only input values if (valueDef->valueClass != MapStyleValueDefinition::Class::Input) continue; const auto constantRuleValue = evaluateConstantValue( mapObject, valueDef->dataType, ruleValueEntry.value(), inputValues, constantEvaluationResult); InputValue inputValue; inputValues->get(valueDefId, inputValue); bool evaluationResult = false; if (valueDefId == _builtinValueDefs->id_INPUT_MINZOOM) { assert(!constantRuleValue.isComplex); evaluationResult = (constantRuleValue.asSimple.asInt <= inputValue.asInt); } else if (valueDefId == _builtinValueDefs->id_INPUT_MAXZOOM) { assert(!constantRuleValue.isComplex); evaluationResult = (constantRuleValue.asSimple.asInt >= inputValue.asInt); } else if (valueDefId == _builtinValueDefs->id_INPUT_ADDITIONAL) { if (!mapObject) evaluationResult = true; else { assert(!constantRuleValue.isComplex); const auto valueString = owner->mapStyle->getStringById(constantRuleValue.asSimple.asUInt); auto equalSignIdx = valueString.indexOf(QLatin1Char('=')); if (equalSignIdx >= 0) { const auto& tagRef = valueString.midRef(0, equalSignIdx); const auto& valueRef = valueString.midRef(equalSignIdx + 1); evaluationResult = mapObject->containsAttribute(tagRef, valueRef, true); } else evaluationResult = mapObject->containsTag(valueString, true); } } else if (valueDefId == _builtinValueDefs->id_INPUT_TEST) { evaluationResult = (inputValue.asInt == 1); } else if (valueDef->dataType == MapStyleValueDataType::Float) { const auto lvalue = constantRuleValue.isComplex ? constantRuleValue.asComplex.asFloat.evaluate(owner->ptScaleFactor) : constantRuleValue.asSimple.asFloat; evaluationResult = qFuzzyCompare(lvalue, inputValue.asFloat); } else { const auto lvalue = constantRuleValue.isComplex ? constantRuleValue.asComplex.asInt.evaluate(owner->ptScaleFactor) : constantRuleValue.asSimple.asInt; evaluationResult = (lvalue == inputValue.asInt); } // If at least one value of rule does not match, it's failure if (!evaluationResult) return false; } // In case rule sets "disable", stop processing const auto citDisabledValue = ruleNodeValues.constFind(_builtinValueDefs->id_OUTPUT_DISABLE); if (citDisabledValue != ruleNodeValues.cend()) { const auto disableValue = evaluateConstantValue( mapObject, _builtinValueDefs->OUTPUT_DISABLE->dataType, *citDisabledValue, inputValues, constantEvaluationResult); assert(!disableValue.isComplex); if (disableValue.asSimple.asUInt != 0) { outDisabled = true; return false; } } if (outResultStorage && !ruleNode->getIsSwitch()) fillResultFromRuleNode(ruleNode, *outResultStorage, true); bool atLeastOneConditionalMatched = false; const auto& oneOfConditionalSubnodes = ruleNode->getOneOfConditionalSubnodesRef(); for (const auto& oneOfConditionalSubnode : constOf(oneOfConditionalSubnodes)) { const auto evaluationResult = evaluate( mapObject, oneOfConditionalSubnode, inputValues, outDisabled, outResultStorage, constantEvaluationResult); if (evaluationResult) { atLeastOneConditionalMatched = true; break; } } if (!atLeastOneConditionalMatched && ruleNode->getIsSwitch()) return false; if (outResultStorage && ruleNode->getIsSwitch()) { // Fill values from <switch> keeping values previously set by <case> fillResultFromRuleNode(ruleNode, *outResultStorage, false); } const auto& applySubnodes = ruleNode->getApplySubnodesRef(); for (const auto& applySubnode : constOf(applySubnodes)) { evaluate( mapObject, applySubnode, inputValues, outDisabled, outResultStorage, constantEvaluationResult); } if (outDisabled) return false; return true; }
void OsmAnd::SymbolRasterizer_P::rasterize( const std::shared_ptr<const Primitiviser::PrimitivisedArea>& primitivizedArea, QList< std::shared_ptr<const RasterizedSymbolsGroup> >& outSymbolsGroups, std::function<bool (const std::shared_ptr<const Model::BinaryMapObject>& mapObject)> filter, const IQueryController* const controller) { const auto& env = primitivizedArea->mapPresentationEnvironment; for (const auto& symbolGroupEntry : rangeOf(constOf(primitivizedArea->symbolsGroups))) { if (controller && controller->isAborted()) return; const auto& mapObject = symbolGroupEntry.key(); const auto& symbolsGroup = symbolGroupEntry.value(); ////////////////////////////////////////////////////////////////////////// //if ((mapObject->id >> 1) == 189600735u) //{ // int i = 5; //} //else // continue; ////////////////////////////////////////////////////////////////////////// // Apply filter, if it's present if (filter && !filter(mapObject)) continue; // Create group const std::shared_ptr<RasterizedSymbolsGroup> group(new RasterizedSymbolsGroup(mapObject)); // Total offset allows several symbols to stack into column. Offset specifies center of symbol bitmap. // This offset is computed only in case symbol is not on-path and not along-path PointI totalOffset; for (const auto& symbol : constOf(symbolsGroup->symbols)) { if (controller && controller->isAborted()) return; if (const auto& textSymbol = std::dynamic_pointer_cast<const Primitiviser::TextSymbol>(symbol)) { TextRasterizer::Style style; if (!textSymbol->drawOnPath && textSymbol->shieldResourceName.isEmpty()) style.wrapWidth = textSymbol->wrapWidth; if (!textSymbol->shieldResourceName.isEmpty()) env->obtainTextShield(textSymbol->shieldResourceName, style.backgroundBitmap); style .setBold(textSymbol->isBold) .setColor(textSymbol->color) .setSize(textSymbol->size); if (textSymbol->shadowRadius > 0) { style .setHaloColor(textSymbol->shadowColor) .setHaloRadius(textSymbol->shadowRadius + 2 /*px*/); //NOTE: ^^^ This is same as specifying 'x:2' in style, but due to backward compatibility with Android, leave as-is } float lineSpacing; float symbolExtraTopSpace; float symbolExtraBottomSpace; QVector<SkScalar> glyphsWidth; const auto rasterizedText = TextRasterizer::globalInstance().rasterize( textSymbol->value, style, textSymbol->drawOnPath ? &glyphsWidth : nullptr, &symbolExtraTopSpace, &symbolExtraBottomSpace, &lineSpacing); if (!rasterizedText) continue; #if OSMAND_DUMP_SYMBOLS { QDir::current().mkpath("text_symbols"); std::unique_ptr<SkImageEncoder> encoder(CreatePNGImageEncoder()); QString filename; filename.sprintf("%s\\text_symbols\\%p.png", qPrintable(QDir::currentPath()), rasterizedText.get()); encoder->encodeFile(qPrintable(filename), *rasterizedText.get(), 100); } #endif // OSMAND_DUMP_SYMBOLS if (textSymbol->drawOnPath) { if (textSymbol->drawOnPath && textSymbol->verticalOffset > 0) { LogPrintf(LogSeverityLevel::Warning, "Hey, on-path + vertical offset is not supported!"); //assert(false); } // Publish new rasterized symbol const std::shared_ptr<RasterizedOnPathSymbol> rasterizedSymbol(new RasterizedOnPathSymbol(group, textSymbol)); rasterizedSymbol->bitmap = qMove(rasterizedText); rasterizedSymbol->order = textSymbol->order; rasterizedSymbol->contentType = RasterizedSymbol::ContentType::Text; rasterizedSymbol->content = textSymbol->value; rasterizedSymbol->languageId = textSymbol->languageId; rasterizedSymbol->minDistance = textSymbol->minDistance; rasterizedSymbol->glyphsWidth = glyphsWidth; group->symbols.push_back(qMove(rasterizedSymbol)); } else { // Calculate local offset. Since offset specifies center, it's a sum of // - vertical offset // - extra top space (which should be in texture, but not rendered, since transparent) // - height / 2 // This calculation is used only if this symbol is not first. Otherwise only following is used: // - vertical offset PointI localOffset; localOffset.y += textSymbol->verticalOffset; if (!group->symbols.isEmpty() && !textSymbol->drawAlongPath) { localOffset.y += symbolExtraTopSpace; localOffset.y += rasterizedText->height() / 2; } // Increment total offset if (!textSymbol->drawAlongPath) totalOffset += localOffset; // Publish new rasterized symbol const std::shared_ptr<RasterizedSpriteSymbol> rasterizedSymbol(new RasterizedSpriteSymbol(group, textSymbol)); rasterizedSymbol->bitmap = rasterizedText; rasterizedSymbol->order = textSymbol->order; rasterizedSymbol->contentType = RasterizedSymbol::ContentType::Text; rasterizedSymbol->content = textSymbol->value; rasterizedSymbol->languageId = textSymbol->languageId; rasterizedSymbol->minDistance = textSymbol->minDistance; rasterizedSymbol->location31 = textSymbol->location31; rasterizedSymbol->offset = textSymbol->drawAlongPath ? localOffset : totalOffset; rasterizedSymbol->drawAlongPath = textSymbol->drawAlongPath; rasterizedSymbol->intersectionSize = PointI(-1, -1); group->symbols.push_back(qMove(std::shared_ptr<const RasterizedSymbol>(rasterizedSymbol))); // Next symbol should also take into account: // - height / 2 // - extra bottom space (which should be in texture, but not rendered, since transparent) // - spacing between lines if (!textSymbol->drawAlongPath) { totalOffset.y += rasterizedText->height() / 2; totalOffset.y += symbolExtraBottomSpace; totalOffset.y += qCeil(lineSpacing); } } } else if (const auto& iconSymbol = std::dynamic_pointer_cast<const Primitiviser::IconSymbol>(symbol)) { std::shared_ptr<const SkBitmap> iconBitmap; if (!env->obtainMapIcon(iconSymbol->resourceName, iconBitmap) || !iconBitmap) continue; std::shared_ptr<const SkBitmap> backgroundBitmap; if (!iconSymbol->shieldResourceName.isEmpty()) env->obtainIconShield(iconSymbol->shieldResourceName, backgroundBitmap); // Compose final image std::shared_ptr<const SkBitmap> rasterizedIcon; if (!backgroundBitmap) rasterizedIcon = iconBitmap; else { // Compute composed image size const auto rasterizedIconWidth = qMax(iconBitmap->width(), backgroundBitmap->width()); const auto rasterizedIconHeight = qMax(iconBitmap->height(), backgroundBitmap->height()); // Create a bitmap that will be hold entire symbol const auto pRasterizedIcon = new SkBitmap(); rasterizedIcon.reset(pRasterizedIcon); pRasterizedIcon->setConfig(SkBitmap::kARGB_8888_Config, rasterizedIconWidth, rasterizedIconHeight); pRasterizedIcon->allocPixels(); pRasterizedIcon->eraseColor(SK_ColorTRANSPARENT); // Create canvas SkBitmapDevice target(*pRasterizedIcon); SkCanvas canvas(&target); // Draw the background canvas.drawBitmap(*backgroundBitmap, (rasterizedIconWidth - backgroundBitmap->width()) / 2.0f, (rasterizedIconHeight - backgroundBitmap->height()) / 2.0f, nullptr); // Draw the icon canvas.drawBitmap(*iconBitmap, (rasterizedIconWidth - iconBitmap->width()) / 2.0f, (rasterizedIconHeight - iconBitmap->height()) / 2.0f, nullptr); // Flush all operations canvas.flush(); } #if OSMAND_DUMP_SYMBOLS { QDir::current().mkpath("icon_symbols"); std::unique_ptr<SkImageEncoder> encoder(CreatePNGImageEncoder()); QString filename; filename.sprintf("%s\\icon_symbols\\%p.png", qPrintable(QDir::currentPath()), rasterizedIcon.get()); encoder->encodeFile(qPrintable(filename), *rasterizedIcon, 100); } #endif // OSMAND_DUMP_SYMBOLS // Calculate local offset. Since offset specifies center, it's a sum of // - height / 2 // This calculation is used only if this symbol is not first. Otherwise nothing is used. PointI localOffset; if (!group->symbols.isEmpty() && !iconSymbol->drawAlongPath) localOffset.y += rasterizedIcon->height() / 2; // Increment total offset if (!iconSymbol->drawAlongPath) totalOffset += localOffset; // Publish new rasterized symbol const std::shared_ptr<RasterizedSpriteSymbol> rasterizedSymbol(new RasterizedSpriteSymbol(group, iconSymbol)); rasterizedSymbol->bitmap = rasterizedIcon; rasterizedSymbol->order = iconSymbol->order; rasterizedSymbol->contentType = RasterizedSymbol::ContentType::Icon; rasterizedSymbol->content = iconSymbol->resourceName; rasterizedSymbol->languageId = LanguageId::Invariant; rasterizedSymbol->minDistance = PointI(); rasterizedSymbol->location31 = iconSymbol->location31; rasterizedSymbol->offset = iconSymbol->drawAlongPath ? localOffset : totalOffset; rasterizedSymbol->drawAlongPath = iconSymbol->drawAlongPath; rasterizedSymbol->intersectionSize = PointI(iconSymbol->intersectionSize, iconSymbol->intersectionSize); group->symbols.push_back(qMove(std::shared_ptr<const RasterizedSymbol>(rasterizedSymbol))); // Next symbol should also take into account: // - height / 2 if (!iconSymbol->drawAlongPath) totalOffset.y += rasterizedIcon->height() / 2; } } // Add group to output outSymbolsGroups.push_back(qMove(group)); } }
bool OsmAnd::ObfDataInterface::scanAmenitiesByName( const QString& query, QList< std::shared_ptr<const OsmAnd::Amenity> >* outAmenities, const AreaI* const pBbox31 /*= nullptr*/, const TileAcceptorFunction tileFilter /*= nullptr*/, const QHash<QString, QStringList>* const categoriesFilter /*= nullptr*/, const ObfPoiSectionReader::VisitorFunction visitor /*= nullptr*/, const std::shared_ptr<const IQueryController>& queryController /*= nullptr*/) { for (const auto& obfReader : constOf(obfReaders)) { if (queryController && queryController->isAborted()) return false; const auto& obfInfo = obfReader->obtainInfo(); for (const auto& poiSection : constOf(obfInfo->poiSections)) { if (queryController && queryController->isAborted()) return false; if (pBbox31) { bool accept = false; accept = accept || poiSection->area31.contains(*pBbox31); accept = accept || poiSection->area31.intersects(*pBbox31); accept = accept || pBbox31->contains(poiSection->area31); if (!accept) continue; } QSet<ObfPoiCategoryId> categoriesFilterById; if (categoriesFilter) { std::shared_ptr<const ObfPoiSectionCategories> categories; OsmAnd::ObfPoiSectionReader::loadCategories( obfReader, poiSection, categories, queryController); if (!categories) continue; for (const auto& categoriesFilterEntry : rangeOf(constOf(*categoriesFilter))) { const auto mainCategoryIndex = categories->mainCategories.indexOf(categoriesFilterEntry.key()); if (mainCategoryIndex < 0) continue; const auto& subcategories = categories->subCategories[mainCategoryIndex]; if (categoriesFilterEntry.value().isEmpty()) { for (auto subCategoryIndex = 0; subCategoryIndex < subcategories.size(); subCategoryIndex++) categoriesFilterById.insert(ObfPoiCategoryId::create(mainCategoryIndex, subCategoryIndex)); } else { for (const auto& subcategory : constOf(categoriesFilterEntry.value())) { const auto subCategoryIndex = subcategories.indexOf(subcategory); if (subCategoryIndex < 0) continue; categoriesFilterById.insert(ObfPoiCategoryId::create(mainCategoryIndex, subCategoryIndex)); } } } } OsmAnd::ObfPoiSectionReader::scanAmenitiesByName( obfReader, poiSection, query, outAmenities, pBbox31, tileFilter, categoriesFilter ? &categoriesFilterById : nullptr, visitor, queryController); } } return true; }
void OsmAnd::MapRendererTiledSymbolsResource::unloadFromGPU() { const auto link_ = link.lock(); const auto collection = static_cast<MapRendererTiledSymbolsResourcesCollection*>(&link_->collection); // Remove quick references #if OSMAND_LOG_MAP_SYMBOLS_TO_GPU_RESOURCES_MAP_CHANGES for (const auto& symbolToResourceInGpuEntry : rangeOf(constOf(_symbolToResourceInGpuLUT))) { LogPrintf(LogSeverityLevel::Debug, "Removed GPU resource %p for map symbol %p in %p (unloadFromGPU)", symbolToResourceInGpuEntry.value().get(), symbolToResourceInGpuEntry.key().get(), this); } #endif // OSMAND_LOG_MAP_SYMBOLS_TO_GPU_RESOURCES_MAP_CHANGES _symbolToResourceInGpuLUT.clear(); // Unique for (const auto& groupResources : constOf(_uniqueGroupsResources)) { // For unique group resources it's safe to clear 'MapSymbol->ResourceInGPU' map groupResources->resourcesInGPU.clear(); } _uniqueGroupsResources.clear(); // Shared auto& sharedGroupsResources = collection->_sharedGroupsResources[zoom]; for (auto& groupResources : _referencedSharedGroupsResources) { const auto symbolsGroupWithId = std::dynamic_pointer_cast<const IMapSymbolsGroupWithUniqueId>(groupResources->group); bool wasRemoved = false; uintmax_t* pRefsRemaining = nullptr; #if OSMAND_LOG_SHARED_MAP_SYMBOLS_GROUPS_LIFECYCLE uintmax_t refsRemaining = 0; pRefsRemaining = &refsRemaining; #endif // OSMAND_LOG_SHARED_MAP_SYMBOLS_GROUPS_LIFECYCLE // Release reference first, since GroupResources will be released after GPU resource will be dereferenced, // other tile may catch empty non-loadable GroupResources. auto groupResources_ = groupResources; sharedGroupsResources.releaseReference(symbolsGroupWithId->getId(), groupResources_, true, &wasRemoved, pRefsRemaining); #if OSMAND_LOG_SHARED_MAP_SYMBOLS_GROUPS_LIFECYCLE LogPrintf(LogSeverityLevel::Debug, "Shared GroupResources(%p) dereferenced for %s in %p (%dx%d@%d): %" PRIu64 " ref(s) remain, %s", groupResources.get(), qPrintable(symbolsGroup->getDebugTitle()), this, tileId.x, tileId.y, zoom, static_cast<uint64_t>(refsRemaining), wasRemoved ? "removed" : "not removed"); #endif // OSMAND_LOG_SHARED_MAP_SYMBOLS_GROUPS_LIFECYCLE // In case final reference to shared group resources was removed, it's safe to clear resources, // since no-one else will check for this map being empty. Otherwise, these resources are still needed // somewhere if (wasRemoved) groupResources->resourcesInGPU.clear(); } _referencedSharedGroupsResources.clear(); }
void OsmAnd::SymbolRasterizer_P::rasterize( const std::shared_ptr<const Primitiviser::PrimitivisedArea>& primitivizedArea, QList< std::shared_ptr<const RasterizedSymbolsGroup> >& outSymbolsGroups, std::function<bool (const std::shared_ptr<const Model::BinaryMapObject>& mapObject)> filter, const IQueryController* const controller) { const auto& env = primitivizedArea->mapPresentationEnvironment; for (const auto& symbolsEntry : rangeOf(constOf(primitivizedArea->symbolsBySourceObjects))) { if (controller && controller->isAborted()) return; // Apply filter, if it's present if (filter && !filter(symbolsEntry.key())) continue; // Create group const auto constructedGroup = new RasterizedSymbolsGroup(symbolsEntry.key()); std::shared_ptr<const RasterizedSymbolsGroup> group(constructedGroup); // Total offset allows several symbols to stack into column. // Offset specifies center of symbol bitmap PointI totalOffset; for (const auto& symbol : constOf(symbolsEntry.value())) { if (controller && controller->isAborted()) return; if (const auto& textSymbol = std::dynamic_pointer_cast<const Primitiviser::TextSymbol>(symbol)) { TextRasterizer::Style style; if (!textSymbol->drawOnPath && textSymbol->shieldResourceName.isEmpty()) style.wrapWidth = textSymbol->wrapWidth; if (!textSymbol->shieldResourceName.isEmpty()) env->obtainTextShield(textSymbol->shieldResourceName, style.backgroundBitmap); style .setBold(textSymbol->isBold) .setColor(textSymbol->color) .setSize(textSymbol->size); if (textSymbol->shadowRadius > 0) { style .setHaloColor(textSymbol->shadowColor) .setHaloRadius(textSymbol->shadowRadius + 2 /*px*/); //NOTE: ^^^ This is same as specifying 'x:2' in style, but due to backward compatibility with Android, leave as-is } float lineSpacing; float symbolExtraTopSpace; float symbolExtraBottomSpace; QVector<SkScalar> glyphsWidth; const auto rasterizedText = TextRasterizer::getInstance().rasterize( textSymbol->value, style, textSymbol->drawOnPath ? &glyphsWidth : nullptr, &symbolExtraTopSpace, &symbolExtraBottomSpace, &lineSpacing); #if OSMAND_DUMP_SYMBOLS { QDir::current().mkpath("text_symbols"); std::unique_ptr<SkImageEncoder> encoder(CreatePNGImageEncoder()); QString filename; filename.sprintf("%s\\text_symbols\\%p.png", qPrintable(QDir::currentPath()), rasterizedText.get()); encoder->encodeFile(qPrintable(filename), *rasterizedText.get(), 100); } #endif // OSMAND_DUMP_SYMBOLS if (textSymbol->drawOnPath) { // Publish new rasterized symbol const auto rasterizedSymbol = new RasterizedOnPathSymbol( group, constructedGroup->mapObject, qMove(rasterizedText), textSymbol->order, textSymbol->value, textSymbol->languageId, textSymbol->minDistance, glyphsWidth); assert(static_cast<bool>(rasterizedSymbol->bitmap)); constructedGroup->symbols.push_back(qMove(std::shared_ptr<const RasterizedSymbol>(rasterizedSymbol))); } else { // Calculate local offset. Since offset specifies center, it's a sum of // - vertical offset // - extra top space (which should be in texture, but not rendered, since transparent) // - height / 2 // This calculation is used only if this symbol is not first. Otherwise only following is used: // - vertical offset PointI localOffset; localOffset.y += textSymbol->verticalOffset; if (!constructedGroup->symbols.isEmpty()) { localOffset.y += symbolExtraTopSpace; localOffset.y += rasterizedText->height() / 2; } // Increment total offset totalOffset += localOffset; // Publish new rasterized symbol const auto rasterizedSymbol = new RasterizedSpriteSymbol( group, constructedGroup->mapObject, qMove(rasterizedText), textSymbol->order, textSymbol->value, textSymbol->languageId, textSymbol->minDistance, textSymbol->location31, totalOffset); assert(static_cast<bool>(rasterizedSymbol->bitmap)); constructedGroup->symbols.push_back(qMove(std::shared_ptr<const RasterizedSymbol>(rasterizedSymbol))); // Next symbol should also take into account: // - height / 2 // - extra bottom space (which should be in texture, but not rendered, since transparent) // - spacing between lines totalOffset.y += rasterizedText->height() / 2; totalOffset.y += symbolExtraBottomSpace; totalOffset.y += qCeil(lineSpacing); } } else if (const auto& iconSymbol = std::dynamic_pointer_cast<const Primitiviser::IconSymbol>(symbol)) { std::shared_ptr<const SkBitmap> bitmap; if (!env->obtainMapIcon(iconSymbol->resourceName, bitmap) || !bitmap) continue; #if OSMAND_DUMP_SYMBOLS { QDir::current().mkpath("icon_symbols"); std::unique_ptr<SkImageEncoder> encoder(CreatePNGImageEncoder()); QString filename; filename.sprintf("%s\\icon_symbols\\%p.png", qPrintable(QDir::currentPath()), bitmap.get()); encoder->encodeFile(qPrintable(filename), *bitmap, 100); } #endif // OSMAND_DUMP_SYMBOLS // Calculate local offset. Since offset specifies center, it's a sum of // - height / 2 // This calculation is used only if this symbol is not first. Otherwise nothing is used. PointI localOffset; if (!constructedGroup->symbols.isEmpty()) localOffset.y += bitmap->height() / 2; // Increment total offset totalOffset += localOffset; // Publish new rasterized symbol const auto rasterizedSymbol = new RasterizedSpriteSymbol( group, constructedGroup->mapObject, qMove(bitmap), iconSymbol->order, iconSymbol->resourceName, LanguageId::Invariant, PointI(), iconSymbol->location31, totalOffset); assert(static_cast<bool>(rasterizedSymbol->bitmap)); constructedGroup->symbols.push_back(qMove(std::shared_ptr<const RasterizedSymbol>(rasterizedSymbol))); // Next symbol should also take into account: // - height / 2 totalOffset.y += bitmap->height() / 2; } } // Add group to output outSymbolsGroups.push_back(qMove(group)); } }
void OsmAnd::MapRendererTiledSymbolsResource::releaseData() { const auto link_ = link.lock(); const auto collection = static_cast<MapRendererTiledSymbolsResourcesCollection*>(&link_->collection); const auto& self = shared_from_this(); // Unregister all registered map symbols for (const auto& publishedMapSymbolsEntry : rangeOf(constOf(_publishedMapSymbolsByGroup))) { const auto& symbolsGroup = publishedMapSymbolsEntry.key(); const auto& mapSymbols = publishedMapSymbolsEntry.value(); for (const auto& mapSymbol : publishedMapSymbolsEntry.value()) resourcesManager->unpublishMapSymbol(symbolsGroup, mapSymbol, self); } _publishedMapSymbolsByGroup.clear(); // Remove quick references (if any left) #if OSMAND_LOG_MAP_SYMBOLS_TO_GPU_RESOURCES_MAP_CHANGES for (const auto& symbolToResourceInGpuEntry : rangeOf(constOf(_symbolToResourceInGpuLUT))) { LogPrintf(LogSeverityLevel::Debug, "Removed GPU resource %p for map symbol %p in %p (releaseData)", symbolToResourceInGpuEntry.value().get(), symbolToResourceInGpuEntry.key().get(), this); } #endif // OSMAND_LOG_MAP_SYMBOLS_TO_GPU_RESOURCES_MAP_CHANGES _symbolToResourceInGpuLUT.clear(); // Unique for (const auto& groupResources : constOf(_uniqueGroupsResources)) { // In case GPU resources was not released before, perform this operation here // Otherwise, groupResources->resourcesInGPU array is empty for unique group resources for (const auto& entryResourceInGPU : rangeOf(groupResources->resourcesInGPU)) { const auto& symbol = entryResourceInGPU.key(); auto& resourceInGPU = entryResourceInGPU.value(); // Unload symbol from GPU thread (using dispatcher) auto resourceInGPU_ = qMove(resourceInGPU); #ifndef Q_COMPILER_RVALUE_REFS resourceInGPU.reset(); #endif //!Q_COMPILER_RVALUE_REFS resourcesManager->renderer->getGpuThreadDispatcher().invokeAsync( [resourceInGPU_] () mutable { resourceInGPU_.reset(); }); } } _uniqueGroupsResources.clear(); // Shared auto& sharedGroupsResources = collection->_sharedGroupsResources[zoom]; for (auto& groupResources : _referencedSharedGroupsResources) { // Otherwise, perform dereferencing const auto symbolsGroupWithId = std::dynamic_pointer_cast<const IMapSymbolsGroupWithUniqueId>(groupResources->group); uintmax_t* pRefsRemaining = nullptr; #if OSMAND_LOG_SHARED_MAP_SYMBOLS_GROUPS_LIFECYCLE uintmax_t refsRemaining = 0; pRefsRemaining = &refsRemaining; #endif // OSMAND_LOG_SHARED_MAP_SYMBOLS_GROUPS_LIFECYCLE bool wasRemoved = false; auto groupResources_ = groupResources; sharedGroupsResources.releaseReference(symbolsGroupWithId->getId(), groupResources_, true, &wasRemoved, pRefsRemaining); #if OSMAND_LOG_SHARED_MAP_SYMBOLS_GROUPS_LIFECYCLE LogPrintf(LogSeverityLevel::Debug, "Shared GroupResources(%p) dereferenced for %s in %p (%dx%d@%d): %" PRIu64 " ref(s) remain, %s", groupResources.get(), qPrintable(symbolsGroup->getDebugTitle()), this, tileId.x, tileId.y, zoom, static_cast<uint64_t>(refsRemaining), wasRemoved ? "removed" : "not removed"); #endif // OSMAND_LOG_SHARED_MAP_SYMBOLS_GROUPS_LIFECYCLE // GPU resources of a shared group resources should be unloaded only in case this was the last // reference to shared group resources if (!wasRemoved) continue; // If this was the last reference to those shared group resources, it's safe to unload // all GPU resources assigned to it. And clear the entire mapping, since it's not needed // by anyone anywhere for (const auto& entryResourceInGPU : rangeOf(groupResources->resourcesInGPU)) { const auto& symbol = entryResourceInGPU.key(); auto& resourceInGPU = entryResourceInGPU.value(); // Unload symbol from GPU thread (using dispatcher) auto resourceInGPU_ = qMove(resourceInGPU); #ifndef Q_COMPILER_RVALUE_REFS resourceInGPU.reset(); #endif //!Q_COMPILER_RVALUE_REFS resourcesManager->renderer->getGpuThreadDispatcher().invokeAsync( [resourceInGPU_] () mutable { resourceInGPU_.reset(); }); } groupResources->resourcesInGPU.clear(); } _referencedSharedGroupsResources.clear(); _sourceData.reset(); }
constexpr auto operator()(Args&&...args){ return tupleCall( inject( Data, rangeOf(Data) , forward<Args>(args)... ) , op ); }
void OsmAnd::SymbolRasterizer_P::rasterize( const std::shared_ptr<const MapPrimitiviser::PrimitivisedObjects>& primitivisedObjects, QList< std::shared_ptr<const RasterizedSymbolsGroup> >& outSymbolsGroups, const FilterByMapObject filter, const std::shared_ptr<const IQueryController>& queryController) const { const auto& env = primitivisedObjects->mapPresentationEnvironment; for (const auto& symbolGroupEntry : rangeOf(constOf(primitivisedObjects->symbolsGroups))) { if (queryController && queryController->isAborted()) return; const auto& mapObject = symbolGroupEntry.key(); const auto& symbolsGroup = symbolGroupEntry.value(); ////////////////////////////////////////////////////////////////////////// //if (mapObject->toString().contains("1333827773")) //{ // int i = 5; //} ////////////////////////////////////////////////////////////////////////// // Apply filter, if it's present if (filter && !filter(mapObject)) continue; // Create group const std::shared_ptr<RasterizedSymbolsGroup> group(new RasterizedSymbolsGroup( mapObject)); // Total offset allows several symbols to stack into column. Offset specifies center of symbol bitmap. // This offset is computed only in case symbol is not on-path and not along-path PointI totalOffset; for (const auto& symbol : constOf(symbolsGroup->symbols)) { if (queryController && queryController->isAborted()) return; if (const auto& textSymbol = std::dynamic_pointer_cast<const MapPrimitiviser::TextSymbol>(symbol)) { TextRasterizer::Style style; if (!textSymbol->drawOnPath && textSymbol->shieldResourceName.isEmpty()) style.wrapWidth = textSymbol->wrapWidth; QList< std::shared_ptr<const SkBitmap> > backgroundLayers; if (!textSymbol->shieldResourceName.isEmpty()) { std::shared_ptr<const SkBitmap> shield; env->obtainTextShield(textSymbol->shieldResourceName, shield); if (shield) backgroundLayers.push_back(shield); } if (!textSymbol->underlayIconResourceName.isEmpty()) { std::shared_ptr<const SkBitmap> icon; env->obtainMapIcon(textSymbol->underlayIconResourceName, icon); if (icon) backgroundLayers.push_back(icon); } style.backgroundBitmap = SkiaUtilities::mergeBitmaps(backgroundLayers); if (!qFuzzyCompare(textSymbol->scaleFactor, 1.0f) && style.backgroundBitmap) { style.backgroundBitmap = SkiaUtilities::scaleBitmap( style.backgroundBitmap, textSymbol->scaleFactor, textSymbol->scaleFactor); } style .setBold(textSymbol->isBold) .setItalic(textSymbol->isItalic) .setColor(textSymbol->color) .setSize(static_cast<int>(textSymbol->size)); if (textSymbol->shadowRadius > 0) { style .setHaloColor(textSymbol->shadowColor) .setHaloRadius(textSymbol->shadowRadius); } float lineSpacing; float symbolExtraTopSpace; float symbolExtraBottomSpace; QVector<SkScalar> glyphsWidth; const auto rasterizedText = owner->textRasterizer->rasterize( textSymbol->value, style, textSymbol->drawOnPath ? &glyphsWidth : nullptr, &symbolExtraTopSpace, &symbolExtraBottomSpace, &lineSpacing); if (!rasterizedText) continue; #if OSMAND_DUMP_SYMBOLS { QDir::current().mkpath("text_symbols"); std::unique_ptr<SkImageEncoder> encoder(CreatePNGImageEncoder()); QString filename; filename.sprintf("%s\\text_symbols\\%p.png", qPrintable(QDir::currentPath()), rasterizedText.get()); encoder->encodeFile(qPrintable(filename), *rasterizedText.get(), 100); } #endif // OSMAND_DUMP_SYMBOLS if (textSymbol->drawOnPath) { // Publish new rasterized symbol const std::shared_ptr<RasterizedOnPathSymbol> rasterizedSymbol(new RasterizedOnPathSymbol( group, textSymbol)); rasterizedSymbol->bitmap = qMove(rasterizedText); rasterizedSymbol->order = textSymbol->order; rasterizedSymbol->contentType = RasterizedSymbol::ContentType::Text; rasterizedSymbol->content = textSymbol->value; rasterizedSymbol->languageId = textSymbol->languageId; rasterizedSymbol->minDistance = textSymbol->minDistance; rasterizedSymbol->glyphsWidth = glyphsWidth; group->symbols.push_back(qMove(rasterizedSymbol)); } else { // Calculate local offset. Since offset specifies center, it's a sum of // - vertical offset // - extra top space (which should be in texture, but not rendered, since transparent) // - height / 2 // This calculation is used only if this symbol is not first. Otherwise only following is used: // - vertical offset PointI localOffset; localOffset.y += textSymbol->verticalOffset; if (!group->symbols.isEmpty() && !textSymbol->drawAlongPath) { localOffset.y += symbolExtraTopSpace; localOffset.y += rasterizedText->height() / 2; } // Increment total offset if (!textSymbol->drawAlongPath) totalOffset += localOffset; // Publish new rasterized symbol const std::shared_ptr<RasterizedSpriteSymbol> rasterizedSymbol(new RasterizedSpriteSymbol(group, textSymbol)); rasterizedSymbol->bitmap = rasterizedText; rasterizedSymbol->order = textSymbol->order; rasterizedSymbol->contentType = RasterizedSymbol::ContentType::Text; rasterizedSymbol->content = textSymbol->value; rasterizedSymbol->languageId = textSymbol->languageId; rasterizedSymbol->minDistance = textSymbol->minDistance; rasterizedSymbol->location31 = textSymbol->location31; rasterizedSymbol->offset = textSymbol->drawAlongPath ? localOffset : totalOffset; rasterizedSymbol->drawAlongPath = textSymbol->drawAlongPath; if (!qIsNaN(textSymbol->intersectionSizeFactor)) { rasterizedSymbol->intersectionBBox = AreaI::fromCenterAndSize(PointI(), PointI( static_cast<int>(textSymbol->intersectionSizeFactor * rasterizedText->width()), static_cast<int>(textSymbol->intersectionSizeFactor * rasterizedText->height()))); } else if (!qIsNaN(textSymbol->intersectionSize)) { rasterizedSymbol->intersectionBBox = AreaI::fromCenterAndSize(PointI(), PointI( static_cast<int>(textSymbol->intersectionSize), static_cast<int>(textSymbol->intersectionSize))); } else if (!qIsNaN(textSymbol->intersectionMargin)) { rasterizedSymbol->intersectionBBox = AreaI::fromCenterAndSize(PointI(), PointI( rasterizedText->width() + static_cast<int>(textSymbol->intersectionMargin), rasterizedText->height() + static_cast<int>(textSymbol->intersectionMargin))); } else { rasterizedSymbol->intersectionBBox = AreaI::fromCenterAndSize(PointI(), PointI( static_cast<int>(rasterizedText->width()), static_cast<int>(rasterizedText->height()))); rasterizedSymbol->intersectionBBox.top() -= static_cast<int>(symbolExtraTopSpace); rasterizedSymbol->intersectionBBox.bottom() += static_cast<int>(symbolExtraBottomSpace); } group->symbols.push_back(qMove(std::shared_ptr<const RasterizedSymbol>(rasterizedSymbol))); // Next symbol should also take into account: // - height / 2 // - extra bottom space (which should be in texture, but not rendered, since transparent) // - spacing between lines if (!textSymbol->drawAlongPath) { totalOffset.y += rasterizedText->height() / 2; totalOffset.y += symbolExtraBottomSpace; totalOffset.y += qCeil(lineSpacing); } } } else if (const auto& iconSymbol = std::dynamic_pointer_cast<const MapPrimitiviser::IconSymbol>(symbol)) { std::shared_ptr<const SkBitmap> iconBitmap; if (!env->obtainMapIcon(iconSymbol->resourceName, iconBitmap) || !iconBitmap) continue; if (!qFuzzyCompare(iconSymbol->scaleFactor, 1.0f)) { iconBitmap = SkiaUtilities::scaleBitmap( iconBitmap, iconSymbol->scaleFactor, iconSymbol->scaleFactor); } std::shared_ptr<const SkBitmap> backgroundBitmap; if (!iconSymbol->shieldResourceName.isEmpty()) { env->obtainIconShield(iconSymbol->shieldResourceName, backgroundBitmap); if (!qFuzzyCompare(iconSymbol->scaleFactor, 1.0f) && backgroundBitmap) { backgroundBitmap = SkiaUtilities::scaleBitmap( backgroundBitmap, iconSymbol->scaleFactor, iconSymbol->scaleFactor); } } QList< std::shared_ptr<const SkBitmap> > layers; if (backgroundBitmap) layers.push_back(backgroundBitmap); for (const auto& overlayResourceName : constOf(iconSymbol->underlayResourceNames)) { std::shared_ptr<const SkBitmap> underlayBitmap; if (!env->obtainMapIcon(overlayResourceName, underlayBitmap) || !underlayBitmap) continue; layers.push_back(underlayBitmap); } layers.push_back(iconBitmap); for (const auto& overlayResourceName : constOf(iconSymbol->overlayResourceNames)) { std::shared_ptr<const SkBitmap> overlayBitmap; if (!env->obtainMapIcon(overlayResourceName, overlayBitmap) || !overlayBitmap) continue; layers.push_back(overlayBitmap); } // Compose final image const auto rasterizedIcon = SkiaUtilities::mergeBitmaps(layers); #if OSMAND_DUMP_SYMBOLS { QDir::current().mkpath("icon_symbols"); std::unique_ptr<SkImageEncoder> encoder(CreatePNGImageEncoder()); QString filename; filename.sprintf("%s\\icon_symbols\\%p.png", qPrintable(QDir::currentPath()), rasterizedIcon.get()); encoder->encodeFile(qPrintable(filename), *rasterizedIcon, 100); } #endif // OSMAND_DUMP_SYMBOLS // Calculate local offset. Since offset specifies center, it's a sum of // - height / 2 // This calculation is used only if this symbol is not first. Otherwise nothing is used. PointI localOffset; if (!qFuzzyIsNull(iconSymbol->offsetFactor.x)) localOffset.x = qRound(iconSymbol->offsetFactor.x * rasterizedIcon->width()); if (!qFuzzyIsNull(iconSymbol->offsetFactor.y)) localOffset.y = qRound(iconSymbol->offsetFactor.y * rasterizedIcon->height()); if (!group->symbols.isEmpty() && !iconSymbol->drawAlongPath) localOffset.y += rasterizedIcon->height() / 2; // Increment total offset if (!iconSymbol->drawAlongPath) totalOffset += localOffset; // Publish new rasterized symbol const std::shared_ptr<RasterizedSpriteSymbol> rasterizedSymbol(new RasterizedSpriteSymbol(group, iconSymbol)); rasterizedSymbol->bitmap = rasterizedIcon; rasterizedSymbol->order = iconSymbol->order; rasterizedSymbol->contentType = RasterizedSymbol::ContentType::Icon; rasterizedSymbol->content = iconSymbol->resourceName; rasterizedSymbol->languageId = LanguageId::Invariant; rasterizedSymbol->minDistance = iconSymbol->minDistance; rasterizedSymbol->location31 = iconSymbol->location31; rasterizedSymbol->offset = iconSymbol->drawAlongPath ? localOffset : totalOffset; rasterizedSymbol->drawAlongPath = iconSymbol->drawAlongPath; if (!qIsNaN(iconSymbol->intersectionSizeFactor)) { rasterizedSymbol->intersectionBBox = AreaI::fromCenterAndSize(PointI(), PointI( static_cast<int>(iconSymbol->intersectionSizeFactor * rasterizedIcon->width()), static_cast<int>(iconSymbol->intersectionSizeFactor * rasterizedIcon->height()))); } else if (!qIsNaN(iconSymbol->intersectionSize)) { rasterizedSymbol->intersectionBBox = AreaI::fromCenterAndSize(PointI(), PointI( static_cast<int>(iconSymbol->intersectionSize), static_cast<int>(iconSymbol->intersectionSize))); } else if (!qIsNaN(iconSymbol->intersectionMargin)) { rasterizedSymbol->intersectionBBox = AreaI::fromCenterAndSize(PointI(), PointI( rasterizedIcon->width() + static_cast<int>(iconSymbol->intersectionMargin), rasterizedIcon->height() + static_cast<int>(iconSymbol->intersectionMargin))); } else { rasterizedSymbol->intersectionBBox = AreaI::fromCenterAndSize(PointI(), PointI( static_cast<int>(rasterizedIcon->width()), static_cast<int>(rasterizedIcon->height()))); } group->symbols.push_back(qMove(std::shared_ptr<const RasterizedSymbol>(rasterizedSymbol))); // Next symbol should also take into account: // - height / 2 if (!iconSymbol->drawAlongPath) totalOffset.y += rasterizedIcon->height() / 2; } } // Add group to output outSymbolsGroups.push_back(qMove(group)); } }
bool OsmAnd::WorldRegions_P::loadWorldRegions( QHash< QString, std::shared_ptr<const WorldRegion> >& outRegions, const IQueryController* const controller) const { const std::shared_ptr<QIODevice> ocbfFile(new QFile(owner->ocbfFileName)); if (!ocbfFile->open(QIODevice::ReadOnly)) return false; const std::shared_ptr<ObfReader> ocbfReader(new ObfReader(ocbfFile)); const auto& obfInfo = ocbfReader->obtainInfo(); if (!obfInfo) { ocbfFile->close(); return false; } for(const auto& mapSection : constOf(obfInfo->mapSections)) { // Check if request is aborted if (controller && controller->isAborted()) { ocbfFile->close(); return false; } bool idsCaptured = false; uint32_t nameId = std::numeric_limits<uint32_t>::max(); uint32_t idId = std::numeric_limits<uint32_t>::max(); uint32_t downloadNameId = std::numeric_limits<uint32_t>::max(); uint32_t regionPrefixId = std::numeric_limits<uint32_t>::max(); uint32_t regionSuffixId = std::numeric_limits<uint32_t>::max(); const QLatin1String localizedNameTagPrefix("name:"); const auto localizedNameTagPrefixLen = localizedNameTagPrefix.size(); const auto worldRegionsCollector = [&outRegions, &idsCaptured, &nameId, &idId, &downloadNameId, ®ionPrefixId, ®ionSuffixId, localizedNameTagPrefix, localizedNameTagPrefixLen] (const std::shared_ptr<const OsmAnd::Model::BinaryMapObject>& worldRegionMapObject) -> bool { const auto& rules = worldRegionMapObject->section->encodingDecodingRules; if (!idsCaptured) { nameId = rules->name_encodingRuleId; QHash< QString, QHash<QString, uint32_t> >::const_iterator citRule; if ((citRule = rules->encodingRuleIds.constFind(QLatin1String("key_name"))) != rules->encodingRuleIds.cend()) idId = citRule->constBegin().value(); if ((citRule = rules->encodingRuleIds.constFind(QLatin1String("download_name"))) != rules->encodingRuleIds.cend()) downloadNameId = citRule->constBegin().value(); if ((citRule = rules->encodingRuleIds.constFind(QLatin1String("region_prefix"))) != rules->encodingRuleIds.cend()) regionPrefixId = citRule->constBegin().value(); if ((citRule = rules->encodingRuleIds.constFind(QLatin1String("region_suffix"))) != rules->encodingRuleIds.cend()) regionSuffixId = citRule->constBegin().value(); idsCaptured = true; } QString name; QString id; QString downloadName; QString regionPrefix; QString regionSuffix; QHash<QString, QString> localizedNames; for(const auto& localizedNameEntry : rangeOf(constOf(worldRegionMapObject->names))) { const auto ruleId = localizedNameEntry.key(); if (ruleId == idId) { id = localizedNameEntry.value().toLower(); continue; } else if (ruleId == nameId) { name = localizedNameEntry.value(); continue; } else if (ruleId == downloadNameId) { downloadName = localizedNameEntry.value().toLower(); continue; } else if (ruleId == regionPrefixId) { regionPrefix = localizedNameEntry.value().toLower(); continue; } else if (ruleId == regionSuffixId) { regionSuffix = localizedNameEntry.value().toLower(); continue; } const auto& nameTag = rules->decodingRules[localizedNameEntry.key()].tag; if (!nameTag.startsWith(localizedNameTagPrefix)) continue; const auto languageId = nameTag.mid(localizedNameTagPrefixLen).toLower(); localizedNames.insert(languageId, localizedNameEntry.value()); } QString parentId = regionSuffix; if (!regionPrefix.isEmpty()) { if (!parentId.isEmpty()) parentId.prepend(regionPrefix + QLatin1String("_")); else parentId = regionPrefix; } // Build up full id if (!regionPrefix.isEmpty()) id.prepend(regionPrefix + QLatin1String("_")); if (!regionSuffix.isEmpty()) id.append(QLatin1String("_") + regionSuffix); std::shared_ptr<const WorldRegion> newRegion(new WorldRegion(id, downloadName, name, localizedNames, parentId)); outRegions.insert(id, qMove(newRegion)); return false; }; // Read objects from each map section OsmAnd::ObfMapSectionReader::loadMapObjects( ocbfReader, mapSection, mapSection->levels.first()->minZoom, nullptr, // Query entire world nullptr, // No need for map objects to be stored nullptr, // Foundation is not needed nullptr, // No filtering by ID worldRegionsCollector, controller); } ocbfFile->close(); return true; }
std::shared_ptr<OsmAnd::MapSymbolsGroup> OsmAnd::MapMarker_P::inflateSymbolsGroup() const { QReadLocker scopedLocker(&_lock); bool ok; // Construct new map symbols group for this marker const std::shared_ptr<MapSymbolsGroup> symbolsGroup(new LinkedMapSymbolsGroup( std::const_pointer_cast<MapMarker_P>(shared_from_this()))); symbolsGroup->presentationMode |= MapSymbolsGroup::PresentationModeFlag::ShowAllOrNothing; int order = owner->baseOrder; if (owner->isAccuracyCircleSupported) { // Add a circle that represent precision circle const std::shared_ptr<AccuracyCircleMapSymbol> accuracyCircleSymbol(new AccuracyCircleMapSymbol( symbolsGroup)); accuracyCircleSymbol->order = order++; accuracyCircleSymbol->position31 = _position; VectorMapSymbol::generateCirclePrimitive(*accuracyCircleSymbol, owner->accuracyCircleBaseColor.withAlpha(0.25f)); accuracyCircleSymbol->isHidden = _isHidden && !_isAccuracyCircleVisible; accuracyCircleSymbol->scale = _accuracyCircleRadius; accuracyCircleSymbol->scaleType = VectorMapSymbol::ScaleType::InMeters; accuracyCircleSymbol->direction = Q_SNAN; symbolsGroup->symbols.push_back(accuracyCircleSymbol); // Add a ring-line that represent precision circle const std::shared_ptr<AccuracyCircleMapSymbol> precisionRingSymbol(new AccuracyCircleMapSymbol( symbolsGroup)); precisionRingSymbol->order = order++; precisionRingSymbol->position31 = _position; VectorMapSymbol::generateRingLinePrimitive(*precisionRingSymbol, owner->accuracyCircleBaseColor.withAlpha(0.4f)); precisionRingSymbol->isHidden = _isHidden && !_isAccuracyCircleVisible; precisionRingSymbol->scale = _accuracyCircleRadius; precisionRingSymbol->scaleType = VectorMapSymbol::ScaleType::InMeters; precisionRingSymbol->direction = Q_SNAN; symbolsGroup->symbols.push_back(precisionRingSymbol); } // Set of OnSurfaceMapSymbol from onMapSurfaceIcons for (const auto& itOnMapSurfaceIcon : rangeOf(constOf(owner->onMapSurfaceIcons))) { const auto key = itOnMapSurfaceIcon.key(); const auto& onMapSurfaceIcon = itOnMapSurfaceIcon.value(); std::shared_ptr<SkBitmap> iconClone(new SkBitmap()); ok = onMapSurfaceIcon->deepCopyTo(iconClone.get()); assert(ok); // Get direction float direction = 0.0f; const auto citDirection = _directions.constFind(key); if (citDirection != _directions.cend()) direction = *citDirection; const std::shared_ptr<KeyedOnSurfaceRasterMapSymbol> onMapSurfaceIconSymbol(new KeyedOnSurfaceRasterMapSymbol( key, symbolsGroup)); onMapSurfaceIconSymbol->order = order++; onMapSurfaceIconSymbol->bitmap = iconClone; onMapSurfaceIconSymbol->size = PointI(iconClone->width(), iconClone->height()); onMapSurfaceIconSymbol->content = QString().sprintf("markerGroup(%p:%p)->onMapSurfaceIconBitmap:%p", this, symbolsGroup.get(), iconClone->getPixels()); onMapSurfaceIconSymbol->languageId = LanguageId::Invariant; onMapSurfaceIconSymbol->position31 = _position; onMapSurfaceIconSymbol->direction = direction; onMapSurfaceIconSymbol->isHidden = _isHidden; symbolsGroup->symbols.push_back(onMapSurfaceIconSymbol); } // SpriteMapSymbol with pinIconBitmap as an icon if (owner->pinIcon) { std::shared_ptr<SkBitmap> pinIcon(new SkBitmap()); ok = owner->pinIcon->deepCopyTo(pinIcon.get()); assert(ok); const std::shared_ptr<BillboardRasterMapSymbol> pinIconSymbol(new BillboardRasterMapSymbol( symbolsGroup)); pinIconSymbol->order = order++; pinIconSymbol->bitmap = pinIcon; pinIconSymbol->size = PointI(pinIcon->width(), pinIcon->height()); pinIconSymbol->content = QString().sprintf("markerGroup(%p:%p)->pinIconBitmap:%p", this, symbolsGroup.get(), pinIcon->getPixels()); pinIconSymbol->languageId = LanguageId::Invariant; pinIconSymbol->position31 = _position; pinIconSymbol->offset = PointI(0, -pinIcon->height() / 2); pinIconSymbol->isHidden = _isHidden; pinIconSymbol->modulationColor = _pinIconModulationColor; symbolsGroup->symbols.push_back(pinIconSymbol); } return symbolsGroup; }
void OsmAnd::MapStyleEvaluationResult::pack(PackedResult& packedResult) { packedResult.reserve(_d->_values.size()); for(const auto& entry : rangeOf(constOf(_d->_values))) packedResult.push_back(qMove(PackedResultEntry(entry.key(), entry.value()))); }