C

Qt Quick Ultralite map example

// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial

#include "offlinetilefetcher.h"

OfflineTileFetcher::OfflineTileFetcher()
    : m_fileCache(Qul::Private::FileCache::instance())
    , maxCacheSize(MAX_CACHE_SIZE)
{}

bool OfflineTileFetcher::getTileImage(const Qul::Private::TileSpec &spec, Qul::Private::TileImage &tileImage)
{
    // user has to configure url to point to tiles images root dir.
    QulString url = generateTileUrl(TILES_BASE_DIR, spec);
    if (url.empty()) {
        url = PLACEHOLDER_TILE;
        Qul::PlatformInterface::log("warning: tile url is empty. using placeholder tile at '%s'.\n", PLACEHOLDER_TILE);
    }

    const TileCacheMap::iterator it = tileCache.find(url);
    if (it != tileCache.end()) {
        tileImage.texture = it->second.texture();
        return true;
    }

    if (tileCache.size() >= maxCacheSize)
        removeOldestImage();

    Qul::SharedImage newTileImage = m_fileCache->get(url.c_str());
    if (!newTileImage && url != PLACEHOLDER_TILE) {
#ifndef NDEBUG
        Qul::PlatformInterface::log("warning: could not fetch a tile with url '%s'. using placeholder tile.\n",
                                    url.c_str());
#endif

        newTileImage = m_fileCache->get(PLACEHOLDER_TILE);

        if (!newTileImage) {
            Qul::PlatformInterface::log("error: could not fetch a placeholder tile from '%s'.\n", PLACEHOLDER_TILE);
            return false;
        }
    } else if (!newTileImage) {
        Qul::PlatformInterface::log("error: could not fetch a placeholder tile from '%s'.\n", PLACEHOLDER_TILE);
        return false;
    }

    tileImage.texture = newTileImage.texture();
    tileCache[url] = newTileImage;
    cacheOrder.push_back(url);

    return true;
}

void OfflineTileFetcher::removeOldestImage()
{
    for (int i = 0; i < MAX_CACHE_SIZE / 5 && !cacheOrder.empty(); ++i) {
        const QulString oldestUrl = cacheOrder.front();
        cacheOrder.pop_front();
        tileCache.erase(oldestUrl);
    }
}

OfflineTileFetcher::QulString OfflineTileFetcher::generateTileUrl(const char *baseDir,
                                                                  const Qul::Private::TileSpec &spec) const
{
    const int size = std::snprintf(nullptr, 0, "%s%d/%d/%d.jpeg", baseDir, spec.zoom, spec.x, spec.y) + 1;
    if (size <= 0) {
        Qul::PlatformInterface::log("error: failed to calculate the required buffer size for the tile url.");
        return QulString();
    }

    char *buffer = static_cast<char *>(Qul::Platform::qul_malloc(sizeof(char) * size));
    if (!buffer) {
        Qul::PlatformInterface::log("error: failed to allocate memory for the tile url.");
        return QulString();
    }

    std::snprintf(buffer, size, "%s%d/%d/%d.jpeg", baseDir, spec.zoom, spec.x, spec.y);

    QulString url(buffer);

    Qul::Platform::qul_free(buffer);

    return url;
}