MapView.qml 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // Copyright (C) 2023 The Qt Company Ltd.
  2. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
  3. import QtQuick
  4. import QtLocation as QL
  5. import QtPositioning as QP
  6. import Qt.labs.animation
  7. /*!
  8. \qmltype MapView
  9. \inqmlmodule QtLocation
  10. \brief An interactive map viewer component.
  11. MapView wraps a Map and adds the typical interactive features:
  12. changing the zoom level, panning and tilting the map.
  13. The implementation is a QML assembly of smaller building blocks that are
  14. available separately. In case you want to make changes in your own version
  15. of this component, you can copy the QML, which is installed into the
  16. \c qml/QtLocation module directory, and modify it as needed.
  17. \sa Map
  18. */
  19. Item {
  20. /*!
  21. \qmlproperty Map MapView::map
  22. This property provides access to the underlying Map instance.
  23. */
  24. property alias map: map
  25. /*!
  26. \qmlproperty real minimumZoomLevel
  27. The minimum zoom level according to the size of the view.
  28. \sa Map::minimumZoomLevel
  29. */
  30. property real minimumZoomLevel: map.minimumZoomLevel
  31. /*!
  32. \qmlproperty real maximumZoomLevel
  33. The maximum valid zoom level for the map.
  34. \sa Map::maximumZoomLevel
  35. */
  36. property real maximumZoomLevel: map.maximumZoomLevel
  37. // --------------------------------
  38. // implementation
  39. id: root
  40. Component.onCompleted: map.resetPinchMinMax()
  41. QL.Map {
  42. id: map
  43. width: parent.width
  44. height: parent.height
  45. tilt: tiltHandler.persistentTranslation.y / -5
  46. property bool pinchAdjustingZoom: false
  47. BoundaryRule on zoomLevel {
  48. id: br
  49. minimum: map.minimumZoomLevel
  50. maximum: map.maximumZoomLevel
  51. }
  52. onZoomLevelChanged: {
  53. br.returnToBounds();
  54. if (!pinchAdjustingZoom) resetPinchMinMax()
  55. }
  56. function resetPinchMinMax() {
  57. pinch.persistentScale = 1
  58. pinch.scaleAxis.minimum = Math.pow(2, root.minimumZoomLevel - map.zoomLevel + 1)
  59. pinch.scaleAxis.maximum = Math.pow(2, root.maximumZoomLevel - map.zoomLevel - 1)
  60. }
  61. PinchHandler {
  62. id: pinch
  63. target: null
  64. property real rawBearing: 0
  65. property QP.geoCoordinate startCentroid
  66. onActiveChanged: if (active) {
  67. flickAnimation.stop()
  68. pinch.startCentroid = map.toCoordinate(pinch.centroid.position, false)
  69. } else {
  70. flickAnimation.restart(centroid.velocity)
  71. map.resetPinchMinMax()
  72. }
  73. onScaleChanged: (delta) => {
  74. map.pinchAdjustingZoom = true
  75. map.zoomLevel += Math.log2(delta)
  76. map.alignCoordinateToPoint(pinch.startCentroid, pinch.centroid.position)
  77. map.pinchAdjustingZoom = false
  78. }
  79. onRotationChanged: (delta) => {
  80. pinch.rawBearing -= delta
  81. // snap to 0° if we're close enough
  82. map.bearing = (Math.abs(pinch.rawBearing) < 5) ? 0 : pinch.rawBearing
  83. map.alignCoordinateToPoint(pinch.startCentroid, pinch.centroid.position)
  84. }
  85. grabPermissions: PointerHandler.TakeOverForbidden
  86. }
  87. WheelHandler {
  88. id: wheel
  89. // workaround for QTBUG-87646 / QTBUG-112394 / QTBUG-112432:
  90. // Magic Mouse pretends to be a trackpad but doesn't work with PinchHandler
  91. // and we don't yet distinguish mice and trackpads on Wayland either
  92. acceptedDevices: Qt.platform.pluginName === "cocoa" || Qt.platform.pluginName === "wayland"
  93. ? PointerDevice.Mouse | PointerDevice.TouchPad
  94. : PointerDevice.Mouse
  95. onWheel: (event) => {
  96. const loc = map.toCoordinate(wheel.point.position)
  97. switch (event.modifiers) {
  98. case Qt.NoModifier:
  99. map.zoomLevel += event.angleDelta.y / 120
  100. break
  101. case Qt.ShiftModifier:
  102. map.bearing += event.angleDelta.y / 15
  103. break
  104. case Qt.ControlModifier:
  105. map.tilt += event.angleDelta.y / 15
  106. break
  107. }
  108. map.alignCoordinateToPoint(loc, wheel.point.position)
  109. }
  110. }
  111. DragHandler {
  112. id: drag
  113. signal flickStarted // for autotests only
  114. signal flickEnded
  115. target: null
  116. onTranslationChanged: (delta) => map.pan(-delta.x, -delta.y)
  117. onActiveChanged: if (active) {
  118. flickAnimation.stop()
  119. } else {
  120. flickAnimation.restart(centroid.velocity)
  121. }
  122. }
  123. property vector3d animDest
  124. onAnimDestChanged: if (flickAnimation.running) {
  125. const delta = Qt.vector2d(animDest.x - flickAnimation.animDestLast.x, animDest.y - flickAnimation.animDestLast.y)
  126. map.pan(-delta.x, -delta.y)
  127. flickAnimation.animDestLast = animDest
  128. }
  129. Vector3dAnimation on animDest {
  130. id: flickAnimation
  131. property vector3d animDestLast
  132. from: Qt.vector3d(0, 0, 0)
  133. duration: 500
  134. easing.type: Easing.OutQuad
  135. onStarted: drag.flickStarted()
  136. onStopped: drag.flickEnded()
  137. function restart(vel) {
  138. stop()
  139. map.animDest = Qt.vector3d(0, 0, 0)
  140. animDestLast = Qt.vector3d(0, 0, 0)
  141. to = Qt.vector3d(vel.x / duration * 100, vel.y / duration * 100, 0)
  142. start()
  143. }
  144. }
  145. DragHandler {
  146. id: tiltHandler
  147. minimumPointCount: 2
  148. maximumPointCount: 2
  149. target: null
  150. xAxis.enabled: false
  151. grabPermissions: PointerHandler.TakeOverForbidden
  152. onActiveChanged: if (active) flickAnimation.stop()
  153. }
  154. }
  155. }