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.

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.
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 imagedecoder example.
Target platforms
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 four zoom levels of Web Mercator projected map tiles of the world. Each tile is a 256 x 256 pixels 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 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 = 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; }
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 newTileImage = m_fileCache->get(url.c_str());
...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() ... , 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); } }
Project structure
Cmake project file
The CMakeLists.txt contains configurations for both desktop and STM32F769i platforms.
CMake compile definitions
TILES_BASE_DIRwhich points to the root directory where the map tiles are.PLACEHOLDER_TILEwhich points to a placeholder image in case the user pans outside of the generated map area.MAX_CACHE_SIZEwhich 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 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",
"CopyrightText.qml"
]
}
ModuleFiles {
MCU.qulModules: ["Positioning", "Location"]
}
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"
]
}
}map.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: 65.044334 longitude: 25.692558 } zoomLevel: root.zoomLevel minimumZoomLevel: 0 maximumZoomLevel: 3 ...
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) } }
Files:
- map/CMakeLists.txt
- map/CopyrightText.qml
- map/MapButton.qml
- map/board_config.h
- map/desktop/board_config.cpp
- map/map.qml
- map/mcu_map.qmlproject
- map/offline_tiles_provider/offlinetilefetcher.cpp
- map/offline_tiles_provider/offlinetilefetcher.h
- map/os/baremetal/main.cpp
- map/stm/stm32f7/board_config.cpp
Images:
Available under certain Qt licenses.
Find out more.