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_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 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
- map/stm/stm32u5/board_config.cpp
Images:
Available under certain Qt licenses.
Find out more.