C
Qt Quick Ultralite map example
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial
import QtQuick
import QtPositioning
import QtLocation
Item {
id: root
property real zoomLevel: mapParameters.zoomLevel
property real nextZoomLevel: root.zoomLevel + 1
property real prevZoomLevel: root.zoomLevel - 1
property string speedKmh: posSource.active && posSource.position.speedValid
? (posSource.position.speed * 3.6).toFixed(1)
: "0.0"
signal startDemoMode(bool paused)
signal endDemoMode
MapParameters {
id: mapParameters
}
function zoom(zoomType: string) {
if (zoomType === "In" && root.zoomLevel < map.maximumZoomLevel) {
zoomInAnimation.stop()
zoomInAnimation.start()
} else if (zoomType === "Out"
&& root.zoomLevel > map.minimumZoomLevel) {
zoomOutAnimation.stop()
zoomOutAnimation.start()
}
}
function incrementZoomLevel() {
if (root.zoomLevel < map.maximumZoomLevel)
root.zoomLevel += 1
}
function decrementZoomLevel() {
if (root.zoomLevel > map.minimumZoomLevel)
root.zoomLevel -= 1
}
function startDemo(paused: bool) {
if (paused) {
posSource.active = false
demoStartPause.imageSource = "start-resume.png"
demoStartPause.demoPaused = true
coordAnimation.stop()
return
}
// start/resume
posSource.active = true
demoStartPause.imageSource = "pause.png"
demoStartPause.demoPaused = false
demoStop.visible = true
}
function endDemo() {
posSource.active = false
IndexManager.setIndex(0)
demoStartPause.imageSource = "start-resume.png"
demoStartPause.demoPaused = true
demoStop.visible = false
}
onStartDemoMode: startDemo(paused)
onEndDemoMode: endDemo()
PositionSource {
id: posSource
active: false
updateInterval: 1000
onPositionChanged: {
map.bearing = posSource.position.direction
currentLocation.coordinate = posSource.position.coordinate
}
onSourceErrorChanged: {
if (sourceError === PositionSource.ClosedError)
endDemo()
}
}
Map {
id: map
property bool panning: false
anchors.fill: parent
center: mapParameters.startPosition
bearing: mapParameters.bearing
zoomLevel: root.zoomLevel
minimumZoomLevel: mapParameters.minimumZoomLevel
maximumZoomLevel: mapParameters.maximumZoomLevel
Behavior on bearing {
RotationAnimation {
duration: 600
direction: RotationAnimation.Shortest
easing.type: Easing.InOutCubic
}
}
MapMarker {
id: currentLocation
imageSource: "nav-arrow.png"
coordinate: map.center
visible: !mapParameters.showButtons
Behavior on coordinate {
CoordinateAnimation {
id: coordAnimation
duration: posSource.updateInterval
}
}
onCoordinateChanged: {
if (posSource.active && !map.panning) {
map.center = currentLocation.coordinate
}
}
}
MapMarker {
id: poiQt
imageSource: "location-marker.png"
markerText: "The Qt Company"
coordinate: QtPositioning.coordinate(65.05877, 25.45545)
visible: !mapParameters.showButtons
}
MapMarker {
id: poiMarket
imageSource: "location-marker.png"
markerText: "Supermarket"
coordinate: QtPositioning.coordinate(65.054274, 25.456213)
visible: !mapParameters.showButtons
}
MapMarker {
id: poiStation
imageSource: "location-marker.png"
markerText: "Gas Station"
coordinate: QtPositioning.coordinate(65.055253, 25.456561)
visible: !mapParameters.showButtons
}
MouseArea {
id: mapPan
anchors.fill: parent
property real pressPointX: 0
property real pressPointY: 0
property real translationX: 0
property real translationY: 0
onPressed: {
map.panning = true
pressPointX = mouse.x
pressPointY = mouse.y
translationX = 0
translationY = 0
}
onReleased: {
map.panning = false
}
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)
}
}
MapButton {
id: zoomIn
visible: mapParameters.showButtons
width: root.width / 10
height: width
anchors.top: parent.top
anchors.right: parent.right
anchors.topMargin: 10
anchors.rightMargin: 10
imageSource: "plus.png"
backgroundColor: "#e0e0e0"
onButtonClicked: {
if (zoomOutAnimation.running) {
zoomOutAnimation.stop()
}
zoom("In")
}
}
MapButton {
id: zoomOut
visible: mapParameters.showButtons
width: root.width / 10
height: width
anchors.top: zoomIn.bottom
anchors.right: parent.right
anchors.topMargin: 5
anchors.rightMargin: 10
imageSource: "minus.png"
backgroundColor: "#e0e0e0"
onButtonClicked: {
if (zoomInAnimation.running) {
zoomInAnimation.stop()
}
zoom("Out")
}
}
MapButton {
id: rotateRight
visible: mapParameters.showButtons
width: root.width / 10
height: width
anchors.top: zoomOut.bottom
anchors.right: parent.right
anchors.topMargin: 5
anchors.rightMargin: 10
imageSource: "rotate-right.png"
backgroundColor: "#e0e0e0"
onButtonClicked: {
map.bearing -= 5
}
}
MapButton {
id: rotateLeft
visible: mapParameters.showButtons
width: root.width / 10
height: width
anchors.top: zoomOut.bottom
anchors.right: rotateRight.left
anchors.topMargin: 5
anchors.rightMargin: 10
imageSource: "rotate-left.png"
backgroundColor: "#e0e0e0"
onButtonClicked: {
map.bearing += 5
}
}
MapButton {
id: demoStartPause
property bool demoPaused: true
width: root.width / 10
height: width
anchors.top: parent.top
anchors.right: zoomIn.left
anchors.topMargin: 10
anchors.rightMargin: 10
imageSource: "start-resume.png"
backgroundColor: "#e0e0e0"
onButtonClicked: {
startDemoMode(!demoPaused)
}
}
MapButton {
id: demoStop
visible: false
width: root.width / 10
height: width
anchors.top: parent.top
anchors.left: demoStartPause.right
anchors.topMargin: 10
anchors.leftMargin: 10
imageSource: "stop.png"
backgroundColor: "#e0e0e0"
onButtonClicked: {
endDemoMode()
}
onVisibleChanged: {
if (visible) {
// only on demo start not on resume
mapParameters.showButtons = false
root.zoomLevel = 17
map.minimumZoomLevel = 17
map.maximumZoomLevel = 17
} else {
mapParameters.showButtons = true
root.zoomLevel = mapParameters.zoomLevel
map.minimumZoomLevel = mapParameters.minimumZoomLevel
map.maximumZoomLevel = mapParameters.maximumZoomLevel
map.bearing = mapParameters.bearing
}
}
}
CopyrightText {
id: copyrighttxt
anchors.bottom: parent.bottom
anchors.right: parent.right
copyrightText: "© OpenStreetMap contributors"
backgroundColor: "#e0e0e0"
}
}
PropertyAnimation {
id: zoomInAnimation
target: map
property: "zoomLevel"
from: root.zoomLevel
to: root.nextZoomLevel
running: false
alwaysRunToEnd: false
duration: 500
onStopped: incrementZoomLevel()
}
PropertyAnimation {
id: zoomOutAnimation
target: map
property: "zoomLevel"
from: root.zoomLevel
to: root.prevZoomLevel
running: false
alwaysRunToEnd: false
duration: 500
onStopped: decrementZoomLevel()
}
}