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:
- map/+t2g/+4m/MapContainer.qml
- map/+t2g/+4m/map.qml
- map/+t2g/+6m/MapContainer.qml
- map/+t2g/+6m/map.qml
- map/+t2g/MapParameters.qml
- map/CMakeLists.txt
- map/CopyrightText.qml
- map/MapButton.qml
- map/MapContainer.qml
- map/MapMarker.qml
- map/MapParameters.qml
- map/board_config.h
- map/desktop/board_config.cpp
- map/dummy_position_source/dummypositiondata.cpp
- map/dummy_position_source/dummypositionsource.cpp
- map/dummy_position_source/dummypositionsource.h
- map/dummy_position_source/indexmanager.h
- map/map.qml
- map/mcu_map.qmlproject
- map/mcu_map_t2g_4m.qmlproject
- map/mcu_map_t2g_6m.qmlproject
- map/offline_tiles_provider/offlinetilefetcher.cpp
- map/offline_tiles_provider/offlinetilefetcher.h
- map/os/baremetal/main.cpp
- map/stm/stm32f7/board_config.cpp
- map/stm/stm32u5/board_config.cpp
- map/t2g-traveo/board_config.cpp
Images:
Available under certain Qt licenses.
Find out more.