summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2022-11-15 20:32:32 +0100
committerShawn Rutledge <shawn.rutledge@qt.io>2023-01-28 22:56:11 +0100
commiteaab46307c04004649eaa481ff9ba35972d9b967 (patch)
tree0ec21072cd833af628066893016d7486385df226 /examples
parent4b81f1a34c6936b7b5a752ed4555f65847b13337 (diff)
downloadqtlocation-eaab46307c04004649eaa481ff9ba35972d9b967.tar.gz
Add MapView
This has nearly complete functionality now. A couple of approaches were possible: one way is to have the Map larger than the MapView, so that the MapView defines a viewport. This allows the handlers to function more "normally": DragHandler already knows how to move the Map in the viewport, etc. But then you can pan off the edge of the Map; so we needed a recenter() function to calculate and set the map.center property, and call that at the right times. This needs to be done when the MapView is resized too, and that turned out to be tricky to get right. Another advantage though would be that we could ensure Map doesn't re-generate geometry any more often than necessary: small changes to the center and scale of the map would often merely change one QSGTransformNode. We could still try this approach again later on, but perhaps Map should be doing more of the work to make it possible; and the new ItemObservesViewport flag ought to be useful. But for now, we do it the existing way: Map does its own viewporting. Thus, we assume that Map is optimized to limit geometry re-generation internally. In practice, redrawing while executing a pinch gesture feels fast enough. One of the main reasons we needed the recent changes in handlers is to get deltas. We cannot use bindings directly from handler properties to Map properties, because there are multiple ways to modify each property (you can zoom by pinch or wheel in MapView, and probably via a keyboard shortcut in the application), so we need to increment the zoomLevel and bearing properties rather than binding them. When it comes to panning: instead of a property, Map has a pan(dx, dy) invokable function; so we call that in response to the DragHandler.translationChanged signal, which now carries a delta-vector argument. The alignCoordinateToPoint() function turned out to be ideal to make the pinch gesture zoom into and rotate the map around the centroid (the point between the touchpoints). Since we call that function when either the rotation or scale changes, we do not need an onTranslationChanged(), because you can't do a pinch gesture that only pans without also changing scale and rotation slightly. All three signals are firing constantly, so handling two of them is enough. The Vector3dAnimation turned out to be a good fit to get flicking momentum (let the panning continue a little while after the finger is released); needing to use the pan() function here is a little clumsy, but we can live with it. Handlers and Animations would both prefer to set properties directly. But if there were a property, it would tend to have type QVector2D or QPointF, and the Vector3dAnimation wouldn't know how to set it anyway (but that could be hacked in, or we could write a Vector2dAnimation). Calculating the limits for zooming seems to be tricky: Map.minimumZoomLevel is zero, but in practice the minimum zoom depends on the size (because we cannot zoom out so far that the map no longer fills the viewport, but if the viewport is smaller, then you can zoom out further). So PinchHandler currently limits the max zoom fairly well, but when you try to zoom out too far, it is Map rather than PinchHandler that applies its own runtime limit. That makes PinchHandler.persistentScale useless; but now PinchHandler applies only incremental zoom deltas, so it doesn't matter. But WheelHandler cannot apply limits on its own, so currently it lets you zoom in too far. Map stops you from zooming past level 30, which is strange, since it already knows that OSM maps are limited to level 18. So either we need to figure out how to calculate both the min and max accurately so that we can apply BoundaryRule (which will also replace the use of PinchHandler's own limits, and will depend on a fix for PinchHandler to work with BoundaryRule), or we can get Map to enforce the lower limit: 18 instead of 30. A little bit of zooming beyond 18 is ok (for example to 20), but if you go even further, the rendering suddenly disappears. This could be done in a followup patch, and a couple of autotests need fixing then. The incremental zooming is treated as base-2-logarithmmic, although that's an approximation: https://wiki.openstreetmap.org/wiki/Zoom_levels tells us that sometimes one zoom level corresponds exactly to zooming 2X, but going from zoom level 16 to 15 is an 8:15 ratio. It's close enough to feel smooth anyway; and it turns out that Map is rendering fractional zoom levels well enough already. If that were not the case, we'd need to bring the Item.scale property into play. Now that the Map is the same size as the MapView, we have a choice whether the root of MapView should be a wrapper Item or not. The root could be Map itself, with handlers inside; the upside would be that all Map properties are left exposed. The downsides would be losing the opportunity to go back to the other architecture later on (with the root defining a viewport, and rendering a larger map inside, but re-rendering less often), and losing the opportunity to make the view's minimumZoomLevel and maximumZoomLevel different than those in Map itself. As explained above, minimumZoomLevel should depend on viewport size. So perhaps it's better to keep it like this: we have control over which Map properties to expose directly, and for the rest, the user can bind to things like mapview.map.center instead of mapview.center. The geojson example is updated to use MapView, whereas minimal_map applies its own handlers to the Map. Other examples still need updating. [ChangeLog][MapView] MapView is the interactive replacement for the Map item in earlier releases. It wraps a Map instance with pointer handlers to make it interactive. Pick-to: 6.5 Fixes: QTBUG-68108 Change-Id: Ibf6bcf71fa7588fcf8cf117e213f35cebd105639 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Diffstat (limited to 'examples')
-rw-r--r--examples/location/geojson_viewer/main.qml30
-rw-r--r--examples/location/minimal_map/main.qml42
2 files changed, 59 insertions, 13 deletions
diff --git a/examples/location/geojson_viewer/main.qml b/examples/location/geojson_viewer/main.qml
index 85d0e07d..2aab296e 100644
--- a/examples/location/geojson_viewer/main.qml
+++ b/examples/location/geojson_viewer/main.qml
@@ -63,7 +63,10 @@ ApplicationWindow {
width: 1024
height: 1024
menuBar: mainMenu
- title: qsTr("GeoJSON Viewer")
+ title: qsTr("GeoJSON Viewer: ") + view.map.center +
+ " zoom " + view.map.zoomLevel.toFixed(3)
+ + " min " + view.map.minimumZoomLevel +
+ " max " + view.map.maximumZoomLevel
FileDialog {
visible: false
@@ -147,23 +150,26 @@ ApplicationWindow {
}
Shortcut {
- sequence: "Ctrl+P"
- onActivated: {
-
- console.log("Center : QtPositioning.coordinate(",map.center.latitude,",",map.center.longitude,")")
- console.log("Zoom : ",map.zoomLevel)
- }
+ enabled: view.map.zoomLevel < view.map.maximumZoomLevel
+ sequence: StandardKey.ZoomIn
+ onActivated: view.map.zoomLevel = Math.round(view.map.zoomLevel + 1)
+ }
+ Shortcut {
+ enabled: view.map.zoomLevel > view.map.minimumZoomLevel
+ sequence: StandardKey.ZoomOut
+ onActivated: view.map.zoomLevel = Math.round(view.map.zoomLevel - 1)
}
- Map {
- id: map
+ MapView {
+ id: view
anchors.fill: parent
- center: QtPositioning.coordinate(43.59, 13.50) // Starting coordinates: Ancona, the city where I am studying :)
- plugin: Plugin { name: "osm" }
- zoomLevel: 4
+ map.center: QtPositioning.coordinate(43.59, 13.50) // Ancona, Italy
+ map.plugin: Plugin { name: "osm" }
+ map.zoomLevel: 4
MapItemView {
id: miv
+ parent: view.map
model: geoJsoner.model
delegate: GeoJsonDelegate {
}
diff --git a/examples/location/minimal_map/main.qml b/examples/location/minimal_map/main.qml
index 92230f76..be78f00d 100644
--- a/examples/location/minimal_map/main.qml
+++ b/examples/location/minimal_map/main.qml
@@ -49,7 +49,6 @@
****************************************************************************/
import QtQuick
-import QtQuick.Window
import QtLocation
import QtPositioning
@@ -57,6 +56,8 @@ Window {
width: Qt.platform.os == "android" ? Screen.width : 512
height: Qt.platform.os == "android" ? Screen.height : 512
visible: true
+ title: map.center + " zoom " + map.zoomLevel.toFixed(3)
+ + " min " + map.minimumZoomLevel + " max " + map.maximumZoomLevel
Plugin {
id: mapPlugin
@@ -69,9 +70,48 @@ Window {
}
Map {
+ id: map
anchors.fill: parent
plugin: mapPlugin
center: QtPositioning.coordinate(59.91, 10.75) // Oslo
zoomLevel: 14
+ property geoCoordinate startCentroid
+
+ PinchHandler {
+ id: pinch
+ target: null
+ onActiveChanged: if (active) {
+ map.startCentroid = map.toCoordinate(pinch.centroid.position, false)
+ }
+ onScaleChanged: (delta) => {
+ map.zoomLevel += Math.log2(delta)
+ map.alignCoordinateToPoint(map.startCentroid, pinch.centroid.position)
+ }
+ onRotationChanged: (delta) => {
+ map.bearing -= delta
+ map.alignCoordinateToPoint(map.startCentroid, pinch.centroid.position)
+ }
+ grabPermissions: PointerHandler.TakeOverForbidden
+ }
+ WheelHandler {
+ id: wheel
+ rotationScale: 1/120
+ property: "zoomLevel"
+ }
+ DragHandler {
+ id: drag
+ target: null
+ onTranslationChanged: (delta) => map.pan(-delta.x, -delta.y)
+ }
+ Shortcut {
+ enabled: map.zoomLevel < map.maximumZoomLevel
+ sequence: StandardKey.ZoomIn
+ onActivated: map.zoomLevel = Math.round(map.zoomLevel + 1)
+ }
+ Shortcut {
+ enabled: map.zoomLevel > map.minimumZoomLevel
+ sequence: StandardKey.ZoomOut
+ onActivated: map.zoomLevel = Math.round(map.zoomLevel - 1)
+ }
}
}