Exemplo n.º 1
0
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;
}
Exemplo n.º 2
0
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;
}
Exemplo n.º 6
0
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);
}
Exemplo n.º 8
0
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);
}
Exemplo n.º 9
0
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);
    }
}
Exemplo n.º 10
0
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;
}
Exemplo n.º 13
0
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;
}
Exemplo n.º 14
0
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;
}
Exemplo n.º 15
0
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
}
Exemplo n.º 16
0
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;
}
Exemplo n.º 17
0
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));
    }
}
Exemplo n.º 19
0
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();
}
Exemplo n.º 21
0
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();
}
Exemplo n.º 23
0
 constexpr auto operator()(Args&&...args){
   return tupleCall( inject( Data, rangeOf(Data) , forward<Args>(args)... ) , op );
 }
Exemplo n.º 24
0
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));
    }
}
Exemplo n.º 25
0
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, &regionPrefixId, &regionSuffixId, 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;
}
Exemplo n.º 26
0
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())));
}