C

Qt Quick Ultralite map example

Demonstrates how to use the Map item to display a map in Qt Quick Ultralite.

Overview

This example demonstrates how to display a map in Qt Quick Ultralite application using the Map item.

Minimal map mode

In this mode, the example provides map tiles for four zoom levels (from 0 to 3). To zoom in, select the button, and to zoom out, select the button. To rotate the map clockwise, at any zoom level, select the button. To rotate the map anticlockwise, select the button. You can also pan around by dragging the map.

User position demo mode

The demo mode simulates the change in the user's current position on the map on a predefined route. To start the demo mode, select the button. The demo mode hides all buttons except the and the buttons.

The demo starts with the map zooming in to zoom level 17. The icon, which represents the user's current position, and the icon, which represents a point of interest, are placed on the map. To pause the demo, select the button which changes to the button. While the demo is paused, you can pan around by dragging the map. To resume the demo, select . To stop the demo and return back to the minimal map mode, select the button.

The example implements Qul::MapTileFetcher to fetch the map tiles from a file system. The file system implementation is provided from the fileloading example. The map tiles are in JPEG format, which requires a JPEG decoder. The JPEG decoder implementation is provided from the imagedecoder example. For more information, see Fetching map tiles.

The example implements Qul::GeoPositionSource to get position information from a dummy position source. For more information, see Retrieving position information.

Target platforms

Running the example on desktop

The example uses the JPEG decoder implementation from the imagedecoder example which uses Qt to decode the images. Hence the project requires a development Qt for building to provide the headers.

cmake examples/map -DCMAKE_PREFIX_PATH=$HOME/Qt/6.2.4/gcc_64 -DQUL_PLATFORM=Qt -DQul_ROOT=${QUL_ROOT}
cmake examples\map -DCMAKE_PREFIX_PATH=C:\Qt\6.2.4\msvc_2019 -DQUL_COMPILER_NAME=msvc -DQUL_PLATFORM=Qt -DQul_ROOT=%QUL_ROOT%

To avoid linking issues with the Qt version the platform backend is linked against, this has to be the same version. For custom built platforms use the same Qt that was used to build the desktop platform backend. For prebuilt platform libraries it has to be Qt 6.2.4.

Note: When using MinGW, use gnu as the compiler name and C:\Qt\6.2.4\mingw_64 as CMAKE_PREFIX_PATH.

Note: On Linux Qt has dependency on OpenGL. If you get an error while configuring the example about missing OpenGL, you can install it using this command

sudo apt install libgl1-mesa-dev

Running the example on a device

To run the example on the target platform, you will need a FAT32 formatted SD Card. Copy the tiles/ directory, found in the example directory under offline_tiles_provider/, to the root directory of your SD Card.

Hardware requirements

A device that supports accelerated imagedecoding and accelerated rotation is cruicial for good performance, high frame rate and smooth user experience.

The RAM requirement varies depending on the size of the map. A full screen map on a STM32F769i device with screen size of 800 x 480 consumes 32 KB of heap and stack memory. For the same screen size, 3840 KB of SDRAM is required for caching the map tiles images. See Image Caching for more information.

Map tiles

The example includes five zoom levels of Web Mercator projected map tiles of the world. Each tile is a 256 x 256 pixel image in a JPEG format. The number of tiles in each zoom level is calculated using 2zoomlevel x 2zoomlevel.

The map tiles can be found in the example directory under offline_tiles_provider/tiles/ directory.

Fetching map tiles (Technical Preview)

The example fetches map tiles from an offline external storage.

OfflineTileFetcher class

The OfflineTileFetcher class is a reference implementation of the Qul::MapTileFetcher class. You can find the implementation in the offline_tile_provider/offlinetilefetcher.h and offline_tile_provider/offlinetilefetcher.cpp files.

The Qul::MapTileFetcher class has a Qul::MapTileFetcher::getTileImage function that must be implemented. The OfflineTileFetcher class implements it as follows:

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 = findImage(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 = findImage(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;
}

The OfflineTileFetcher class implements a member function generateTileUrl to compose the URI of the map tile using spec parameter and TILES_BASE_DIR defined in the CMakeLists.txt file of the example.

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;
}

The OfflineTileFetcher class uses FileCache::get to access, decode and cache the decoded map tile image into RAM. FileCache::get returns the Qul::SharedImage object.

Qul::SharedImage OfflineTileFetcher::findImage(const char *url)
{
    if (m_fileCache)
        return m_fileCache->get(url);

    return Qul::Private::findImageForString(url);
}

OfflineTileFetcher implements a cache facility to store an already accessed map tiles.

...
tileCache[url] = newTileImage;
...

Caching the accessed map tiles keeps a reference to Qul::SharedImage and returns the already decoded tile image if it was found in the RAM cache.

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

OfflineTileFetcher uses MAX_CACHE_SIZE defined in CMakeLists.txt to set the maxCacheSize, after which the oldest cache entries are removed.

OfflineTileFetcher::OfflineTileFetcher(bool useFileCache)
    ...
    , maxCacheSize(MAX_CACHE_SIZE)
{}

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);
    }
}

Retrieving position information (Technical Preview)

The example simulates retrieving position information from a dummy position source.

DummyPositionSource class

The DummyPositionSource class is a reference implementation of the Qul::GeoPositionSource class. You can find the implementation in the dummy_position_source/dummypositionsource.h and dummy_position_source/dummypositionsource.cpp files.

The Qul::GeoPositionSource class has a Qul::GeoPositionSource::getCurrentPosition function that must be implemented. The DummyPositionSource class implements it as follows:

Qul::Private::PositionSource::SourceError DummyPositionSource::getCurrentPosition(Qul::GeoPositionInfo &positionInfo)
{
    const size_t lastIndex = dataSource.size() - 1;
    IndexManager &indexManager = IndexManager::instance();
    Qul::GeoPositionInfo posInfo = dataSource[indexManager.getIndex()];

    positionInfo.latitude = posInfo.latitude;
    positionInfo.longitude = posInfo.longitude;
    positionInfo.direction = posInfo.direction;

    if (indexManager.getIndex() == lastIndex)
        return Qul::Private::PositionSource::ClosedError;

    indexManager.incrementIndex();

    return Qul::Private::PositionSource::NoError;
}

DummyPositionSource::getCurrentPosition function accesses a simulated position data from dataSource vector. The definition of dataSource vector is located in dummy_position_source/dummypositiondata.cpp.

extern const std::vector<Qul::GeoPositionInfo, Qul::PlatformInterface::Allocator<Qul::GeoPositionInfo> > dataSource
    = {{65.05877, 25.45545, 270},
       {65.05877, 25.455422549019605, 270},
       {65.05877, 25.455395098039215, 270},
        ...

{65.0542974509804, 25.456510980392157, 248.6951536973353},
{65.05427156862746, 25.45636862745098, 248.6951535146238},
{65.05427078431373, 25.45636431372549, 248.6951535146238},
{65.05427, 25.45636, 248.6951535146238}};

To control which position data entry is accessed in dataSource from C++ and QML, the IndexManager class is implemented in dummy_position_source/indexmanager.h.

class IndexManager : public Qul::Singleton<IndexManager>
{
public:
    IndexManager()
        : m_index(0){};

    void setIndex(size_t i) { m_index = i; }
    size_t getIndex() const { return m_index; }
    void incrementIndex() { m_index++; }

private:
    size_t m_index;
};

DummyPositionSource::getCurrentPosition function returns CloseError when the last position data entry is reached to stop updating the current position. Otherwise, it returns NoError when PositionSource calls it to continue updating the current position according to the value of updateInterval property.

Project structure

Cmake project file

The CMakeLists.txt contains configurations for both desktop and STM32F769i platforms.

CMake compile definitions
  • TILES_BASE_DIR which points to the root directory where the map tiles are.
  • PLACEHOLDER_TILE which points to a placeholder image in case the user pans outside of the generated map area.
  • MAX_CACHE_SIZE which sets the size after which old cache entries are deleted.
Desktop configuration

The desktop configuration depends on the POSIX file system implementation of the fileloading example to access the images of the map tiles and desktop JPEG imagedecoder implementation of the imagedecoder example to decode the images of the map tiles.

target_sources(map PRIVATE
...
                      ../imagedecoder/desktop/desktopimagedecoder.cpp
                      ../fileloading/posix/posixfilesystem.cpp
                  )
STM32F769i configuration

The platform configuration depends on the FATFS file system implementation of the fileloading example to access the images of the map tiles in the SD Card, and the hardware JPEG imagedecoder of the imagedecoder example to decode the images of the map tiles.

# file system sources
target_sources(map PRIVATE
        ../fileloading/3rdparty/FatFs/src/diskio.c
        ../fileloading/3rdparty/FatFs/src/ff.c
        ../fileloading/3rdparty/FatFs/src/ff_gen_drv.c
        ../fileloading/3rdparty/FatFs/src/sd_diskio.c
        ../fileloading/3rdparty/FatFs/src/option/unicode.c
        ${QUL_BOARD_SDK_DIR}/Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_ll_sdmmc.c
        ${QUL_BOARD_SDK_DIR}/Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_sd.c
        ${QUL_BOARD_SDK_DIR}/Drivers/BSP/STM32F769I-Discovery/stm32f769i_discovery_sd.c
    )
# jpeg decoder sources
target_sources(map PRIVATE
        ${QUL_BOARD_SDK_DIR}/Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_jpeg.c
        ../imagedecoder/stm/${STM32XX}/stm32f7xx_hal_msp.c
        ../imagedecoder/stm/${STM32XX}/buffer_config.cpp
        ../imagedecoder/stm/stmimagedecoder.cpp
        ../imagedecoder/common/jpeg.cpp
        ../imagedecoder/3rdparty/stm/Utilities/JPEG/jpeg_utils.c
    )
QmlProject file

The example QmlProject file lists the required QML files, image files, interface files, and Qt Quick Ultralite modules. It also sets MCU.Config.maxResourceCacheSize to a value that works on the target platform.

import QmlProject

Project {
    mainFile: "map.qml"

    QmlFiles {
        files: [
            "MapButton.qml",
            "MapContainer.qml",
            "MapParameters.qml",
            "MapMarker.qml",
            "CopyrightText.qml"
        ]
    }

    ModuleFiles {
        MCU.qulModules: ["Positioning", "Location"]
    }

    InterfaceFiles {
        files: [
            "dummy_position_source/indexmanager.h"
        ]
    }

    MCU.Config {
        maxResourceCacheSize: 4000000
    }

    ImageFiles {
        MCU.base: "icons/24px"
        files: [
            "icons/24px/plus.png",
            "icons/24px/minus.png",
            "icons/24px/rotate-right.png",
            "icons/24px/rotate-left.png",
            "icons/24px/nav-arrow.png",
            "icons/24px/location-marker.png",
            "icons/24px/start-resume.png",
            "icons/24px/pause.png",
            "icons/24px/stop.png"
        ]
    }
}
MapContainer.qml

The main QML file uses the Map item and sets the center of the map to Oulu city, Finland. It sets the initial zoomLevel of the map to 3, the minimumZoomLevel to 0, and the maximumZoomLevel to 3.

    Map {
        id: map

        anchors.fill: parent
        center {
            latitude: mapParameters.latitude
            longitude: mapParameters.longitude
        }
        bearing: mapParameters.bearing
        zoomLevel: root.zoomLevel
        minimumZoomLevel: mapParameters.minimumZoomLevel
        maximumZoomLevel: mapParameters.maximumZoomLevel
...

It defines the MouseArea that is responsible for calculating the deltaX and deltaY parameters of the pan function.

        MouseArea {
            id: mapPan

            anchors.fill: parent

            property real pressPointX: 0
            property real pressPointY: 0
            property real translationX: 0
            property real translationY: 0

            onPressed: {
                pressPointX = mouse.x
                pressPointY = mouse.y
                translationX = 0
                translationY = 0
            }
            onPositionChanged: {
                var x = mouse.x - pressPointX
                var y = mouse.y - pressPointY

                var deltaX = x - translationX
                var deltaY = y - translationY
                translationX = x
                translationY = y

                map.pan(-deltaX, -deltaY)
            }
        }
MapParameters.qml

It defines the Map item default parameters.

...
    readonly property real zoomLevel: 3
    readonly property real minimumZoomLevel: 0
    readonly property real maximumZoomLevel: 3
    readonly property real bearing: 0
    readonly property real latitude: 65.05877
    readonly property real longitude: 25.45545
...
MapMarker.qml

MapMarker.qml is a custom QML type. It wraps MapQuickItem item which represent a point of interest marker on the map.

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

MapQuickItem {
    id: root

    property real latitude
    property real longitude
    property alias markerText: markerText.text
    property alias imageSource: markerImage.source

    sourceItem: Image {
        id: markerImage
        source: ""

        Text {
            id: markerText
            text: ""
            font.pointSize: 7
            font.bold: true
            anchors.bottom: parent.top
            anchors.horizontalCenter: parent.horizontalCenter
        }
    }

    coordinate {
        latitude: root.latitude
        longitude: root.longitude
    }
    anchorPoint: Qt.point(sourceItem.width / 2, sourceItem.height / 2)
}

Files:

Images:

Available under certain Qt licenses.
Find out more.