bool OsmAnd::MapPrimitivesMetricsLayerProvider_P::obtainData(
    const TileId tileId,
    const ZoomLevel zoom,
    std::shared_ptr<MapPrimitivesMetricsLayerProvider::Data>& outTiledData,
    const IQueryController* const queryController)
{
    MapPrimitivesProvider_Metrics::Metric_obtainData obtainDataMetric;

    // Obtain offline map primitives tile
    std::shared_ptr<MapPrimitivesProvider::Data> primitivesTile;
    owner->primitivesProvider->obtainData(tileId, zoom, primitivesTile, &obtainDataMetric, nullptr);
    if (!primitivesTile)
    {
        outTiledData.reset();
        return true;
    }

    // Prepare drawing canvas
    const std::shared_ptr<SkBitmap> bitmap(new SkBitmap());
    if (!bitmap->tryAllocPixels(SkImageInfo::MakeN32Premul(owner->tileSize, owner->tileSize)))
    {
        LogPrintf(LogSeverityLevel::Error,
            "Failed to allocate buffer for rasterization surface %dx%d",
            owner->tileSize,
            owner->tileSize);
        return false;
    }
    SkBitmapDevice target(*bitmap);
    SkCanvas canvas(&target);
    canvas.clear(SK_ColorDKGRAY);

    QString text;
    text += QString(QLatin1String("TILE   %1x%2@%3\n"))
        .arg(tileId.x)
        .arg(tileId.y)
        .arg(zoom);
    QString obtainBinaryMapObjectsElapsedTime(QLatin1String("?"));
    if (const auto obtainBinaryMapObjectsMetric = obtainDataMetric.findSubmetricOfType<ObfMapObjectsProvider_Metrics::Metric_obtainData>())
    {
        obtainBinaryMapObjectsElapsedTime = QString::number(obtainBinaryMapObjectsMetric->elapsedTime, 'f', 2);
    }
    QString primitiviseElapsedTime(QLatin1String("?"));
    if (const auto primitiviseMetric = obtainDataMetric.findSubmetricOfType<MapPrimitiviser_Metrics::Metric_primitiviseWithSurface>())
    {
        text += QString(QLatin1String("order  %1/-%2 %3s\n"))
            .arg(primitiviseMetric->orderEvaluations)
            .arg(primitiviseMetric->orderRejects)
            .arg(QString::number(primitiviseMetric->elapsedTimeForOrderEvaluation, 'f', 2));
        text += QString(QLatin1String("polyg  %1/-%2(-%3) %4s\n"))
            .arg(primitiviseMetric->polygonEvaluations)
            .arg(primitiviseMetric->polygonRejects)
            .arg(primitiviseMetric->polygonsRejectedByArea)
            .arg(QString::number(primitiviseMetric->elapsedTimeForPolygonEvaluation, 'f', 2));
        text += QString(QLatin1String("polyl  %1/-%2 %3s\n"))
            .arg(primitiviseMetric->polylineEvaluations)
            .arg(primitiviseMetric->polylineRejects)
            .arg(QString::number(primitiviseMetric->elapsedTimeForPolylineEvaluation, 'f', 2));
        text += QString(QLatin1String("point  %1/-%2 %3s\n"))
            .arg(primitiviseMetric->pointEvaluations)
            .arg(primitiviseMetric->pointRejects)
            .arg(QString::number(primitiviseMetric->elapsedTimeForPointEvaluation, 'f', 2));
        text += QString(QLatin1String("groups %1s\n"))
            .arg(QString::number(primitiviseMetric->elapsedTimeForObtainingPrimitivesGroups, 'f', 2));
        text += QString(QLatin1String("d/b/c  %1s/%2s/%3s\n"))
            .arg(QString::number(primitiviseMetric->elapsedTimeForObtainingPrimitivesFromDetailedmap, 'f', 2))
            .arg(QString::number(primitiviseMetric->elapsedTimeForObtainingPrimitivesFromBasemap, 'f', 2))
            .arg(QString::number(primitiviseMetric->elapsedTimeForObtainingPrimitivesFromCoastlines, 'f', 2));
        text += QString(QLatin1String("sym    %1s\n"))
            .arg(QString::number(primitiviseMetric->elapsedTimeForObtainingPrimitivesSymbols, 'f', 2));
        primitiviseElapsedTime = QString::number(primitiviseMetric->elapsedTime, 'f', 2);
    }
    text += QString(QLatin1String("TIME   r%1+p%2+?=%3s\n"))
        .arg(obtainBinaryMapObjectsElapsedTime)
        .arg(primitiviseElapsedTime)
        .arg(QString::number(obtainDataMetric.elapsedTime, 'f', 2));
    text = text.trimmed();

    const auto fontSize = 16.0f;

    SkPaint textPaint;
    textPaint.setAntiAlias(true);
    textPaint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
    textPaint.setTextSize(fontSize);
    textPaint.setColor(SK_ColorGREEN);

    auto topOffset = fontSize;
    const auto lines = text.split(QLatin1Char('\n'), QString::SkipEmptyParts);
    for (const auto& line : lines)
    {
        canvas.drawText(
            line.constData(), line.length()*sizeof(QChar),
            5, topOffset,
            textPaint);
        topOffset += 1.25f * fontSize;
    }

    outTiledData.reset(new MapPrimitivesMetricsLayerProvider::Data(
        tileId,
        zoom,
        AlphaChannelPresence::NotPresent,
        owner->densityFactor,
        bitmap,
        primitivesTile,
        new RetainableCacheMetadata(primitivesTile->retainableCacheMetadata)));

    return true;
}
bool OsmAnd::MapPrimitivesMetricsLayerProvider_P::obtainData(
    const IMapDataProvider::Request& request_,
    std::shared_ptr<IMapDataProvider::Data>& outData,
    std::shared_ptr<Metric>* const pOutMetric)
{
    const auto& request = MapDataProviderHelpers::castRequest<MapPrimitivesMetricsLayerProvider::Request>(request_);
    if (pOutMetric)
        pOutMetric->reset();

    MapPrimitivesProvider_Metrics::Metric_obtainData obtainDataMetric;

    // Obtain offline map primitives tile
    std::shared_ptr<MapPrimitivesProvider::Data> primitivesTile;
    owner->primitivesProvider->obtainTiledPrimitives(request, primitivesTile, &obtainDataMetric);
    if (!primitivesTile)
    {
        outData.reset();
        return true;
    }

    // Prepare drawing canvas
    const std::shared_ptr<SkBitmap> bitmap(new SkBitmap());
    if (!bitmap->tryAllocPixels(SkImageInfo::MakeN32Premul(owner->tileSize, owner->tileSize)))
    {
        LogPrintf(LogSeverityLevel::Error,
            "Failed to allocate buffer for rasterization surface %dx%d",
            owner->tileSize,
            owner->tileSize);
        return false;
    }
    SkBitmapDevice target(*bitmap);
    SkCanvas canvas(&target);
    canvas.clear(SK_ColorDKGRAY);

    QString text;
    text += QString(QLatin1String("TILE   %1x%2@%3\n"))
        .arg(request.tileId.x)
        .arg(request.tileId.y)
        .arg(request.zoom);
    QString obtainBinaryMapObjectsElapsedTime(QLatin1String("?"));
    if (const auto obtainBinaryMapObjectsMetric = obtainDataMetric.findSubmetricOfType<ObfMapObjectsProvider_Metrics::Metric_obtainData>(true))
    {
        obtainBinaryMapObjectsElapsedTime = QString::number(obtainBinaryMapObjectsMetric->elapsedTime, 'f', 2);
    }
    QString primitiviseElapsedTime(QLatin1String("?"));
    if (const auto primitiviseMetric = obtainDataMetric.findSubmetricOfType<MapPrimitiviser_Metrics::Metric_primitiviseWithSurface>(true))
    {
        text += QString(QLatin1String("order %1/-%2 %3s ~%4us/e\n"))
            .arg(primitiviseMetric->orderEvaluations)
            .arg(primitiviseMetric->orderRejects)
            .arg(QString::number(primitiviseMetric->elapsedTimeForOrderEvaluation, 'f', 2))
            .arg(static_cast<int>(primitiviseMetric->elapsedTimeForOrderEvaluation * 1000000.0f / primitiviseMetric->orderEvaluations));
        text += QString(QLatin1String("polyg %1/-%2(-%3) %4s ~%5us/e\n"))
            .arg(primitiviseMetric->polygonEvaluations)
            .arg(primitiviseMetric->polygonRejects)
            .arg(primitiviseMetric->polygonsRejectedByArea)
            .arg(QString::number(primitiviseMetric->elapsedTimeForPolygonEvaluation, 'f', 2))
            .arg(static_cast<int>(primitiviseMetric->elapsedTimeForPolygonEvaluation * 1000000.0f / primitiviseMetric->polygonEvaluations));
        text += QString(QLatin1String("%1s ~%2us/p\n"))
            .arg(QString::number(primitiviseMetric->elapsedTimeForPolygonProcessing, 'f', 2))
            .arg(static_cast<int>(primitiviseMetric->elapsedTimeForPolygonProcessing * 1000000.0f / primitiviseMetric->polygonPrimitives));
        text += QString(QLatin1String("polyl %1/-%2(-%3) %4s ~%5us/e\n"))
            .arg(primitiviseMetric->polylineEvaluations)
            .arg(primitiviseMetric->polylineRejects)
            .arg(primitiviseMetric->polylineRejectedByDensity)
            .arg(QString::number(primitiviseMetric->elapsedTimeForPolylineEvaluation, 'f', 2))
            .arg(static_cast<int>(primitiviseMetric->elapsedTimeForPolylineEvaluation * 1000000.0f / primitiviseMetric->polylineEvaluations));
        text += QString(QLatin1String("%1s ~%2us/p\n"))
            .arg(QString::number(primitiviseMetric->elapsedTimeForPolylineProcessing, 'f', 2))
            .arg(static_cast<int>(primitiviseMetric->elapsedTimeForPolylineProcessing * 1000000.0f / primitiviseMetric->polylinePrimitives));
        text += QString(QLatin1String("point %1/-%2 %3s ~%4us/e\n"))
            .arg(primitiviseMetric->pointEvaluations)
            .arg(primitiviseMetric->pointRejects)
            .arg(QString::number(primitiviseMetric->elapsedTimeForPointEvaluation, 'f', 2))
            .arg(static_cast<int>(primitiviseMetric->elapsedTimeForPointEvaluation * 1000000.0f / primitiviseMetric->pointEvaluations));
        text += QString(QLatin1String("%1s ~%2us/p\n"))
            .arg(QString::number(primitiviseMetric->elapsedTimeForPointProcessing, 'f', 2))
            .arg(static_cast<int>(primitiviseMetric->elapsedTimeForPointProcessing * 1000000.0f / primitiviseMetric->pointPrimitives));
        const auto deltaGroups =
            primitiviseMetric->elapsedTimeForObtainingPrimitivesGroups -
            primitiviseMetric->elapsedTimeForOrderEvaluation -
            primitiviseMetric->elapsedTimeForOrderProcessing -
            primitiviseMetric->elapsedTimeForPolygonEvaluation -
            primitiviseMetric->elapsedTimeForPolygonProcessing -
            primitiviseMetric->elapsedTimeForPolylineEvaluation -
            primitiviseMetric->elapsedTimeForPolylineProcessing -
            primitiviseMetric->elapsedTimeForPointEvaluation -
            primitiviseMetric->elapsedTimeForPointProcessing;
        text += QString(QLatin1String("grp %1s (-^=%2s)\n"))
            .arg(QString::number(primitiviseMetric->elapsedTimeForObtainingPrimitivesGroups, 'f', 2))
            .arg(QString::number(deltaGroups, 'f', 2));
        text += QString(QLatin1String("prim %1s+s%2s+?=%3s\n"))
            .arg(QString::number(primitiviseMetric->elapsedTimeForObtainingPrimitivesGroups, 'f', 2))
            .arg(QString::number(primitiviseMetric->elapsedTimeForFutureSharedPrimitivesGroups, 'f', 2))
            .arg(QString::number(primitiviseMetric->elapsedTimeForPrimitives, 'f', 2));
        text += QString(QLatin1String("d/b/c %1s/%2s/%3s\n"))
            .arg(QString::number(primitiviseMetric->elapsedTimeForObtainingPrimitivesFromDetailedmap, 'f', 2))
            .arg(QString::number(primitiviseMetric->elapsedTimeForObtainingPrimitivesFromBasemap, 'f', 2))
            .arg(QString::number(primitiviseMetric->elapsedTimeForObtainingPrimitivesFromCoastlines, 'f', 2));
        text += QString(QLatin1String("txt %1(-%2) %3s ~%4us/e ~%5us/p\n"))
            .arg(primitiviseMetric->obtainedTextSymbols)
            .arg(primitiviseMetric->rejectedTextSymbols)
            .arg(QString::number(primitiviseMetric->elapsedTimeForTextSymbolsEvaluation + primitiviseMetric->elapsedTimeForTextSymbolsProcessing, 'f', 2))
            .arg(static_cast<int>(primitiviseMetric->elapsedTimeForTextSymbolsEvaluation * 1000000.0f / primitiviseMetric->textSymbolsEvaluations))
            .arg(static_cast<int>(primitiviseMetric->elapsedTimeForTextSymbolsProcessing * 1000000.0f / (primitiviseMetric->obtainedTextSymbols + primitiviseMetric->rejectedTextSymbols)));
        text += QString(QLatin1String("icn %1(-%2) %3s ~%4us/p\n"))
            .arg(primitiviseMetric->obtainedIconSymbols)
            .arg(primitiviseMetric->rejectedIconSymbols)
            .arg(QString::number(primitiviseMetric->elapsedTimeForIconSymbolsProcessing, 'f', 2))
            .arg(static_cast<int>(primitiviseMetric->elapsedTimeForIconSymbolsProcessing * 1000000.0f / (primitiviseMetric->obtainedIconSymbols + primitiviseMetric->rejectedIconSymbols)));
        const auto deltaSymbols =
            primitiviseMetric->elapsedTimeForSymbolsGroupsProcessing -
            primitiviseMetric->elapsedTimeForTextSymbolsEvaluation -
            primitiviseMetric->elapsedTimeForTextSymbolsProcessing -
            primitiviseMetric->elapsedTimeForIconSymbolsProcessing;
        text += QString(QLatin1String("sym %1s(-^=%2s) %3->%4\n"))
            .arg(QString::number(primitiviseMetric->elapsedTimeForSymbolsGroupsProcessing, 'f', 2))
            .arg(QString::number(deltaSymbols, 'f', 2))
            .arg(primitiviseMetric->symbolsGroupsProcessed)
            .arg(primitiviseMetric->obtainedTextSymbols + primitiviseMetric->obtainedIconSymbols);
        primitiviseElapsedTime = QString::number(primitiviseMetric->elapsedTime, 'f', 2);
    }
    text += QString(QLatin1String("total r%1+p%2+?=%3s\n"))
        .arg(obtainBinaryMapObjectsElapsedTime)
        .arg(primitiviseElapsedTime)
        .arg(QString::number(obtainDataMetric.elapsedTime, 'f', 2));
    text = text.trimmed();

    const auto fontSize = 14.0f * owner->densityFactor;

    SkPaint textPaint;
    textPaint.setAntiAlias(true);
    textPaint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
    textPaint.setTextSize(fontSize);
    textPaint.setColor(SK_ColorGREEN);

    auto topOffset = fontSize;
    const auto lines = text.split(QLatin1Char('\n'), QString::SkipEmptyParts);
    for (const auto& line : lines)
    {
        canvas.drawText(
            line.constData(), line.length()*sizeof(QChar),
            5, topOffset,
            textPaint);
        topOffset += 1.15f * fontSize;
    }

    outData.reset(new MapPrimitivesMetricsLayerProvider::Data(
        request.tileId,
        request.zoom,
        AlphaChannelPresence::NotPresent,
        owner->densityFactor,
        bitmap,
        primitivesTile,
        new RetainableCacheMetadata(primitivesTile->retainableCacheMetadata)));

    return true;
}