While working on one of my current projects I came across the need for a map/geo browser.

From start, Qt Quick’s QML Map Element (using the nokia plugin) looked like the best option by a large margin and for all the good and sane reasons.

The problem is that the map element itself is quite simple/raw. At first I was expecting the Map Element to provide all the panning/pinch-zooming logic/functionality built in but it doesn’t.

Looking around for people with the same problem, I found this example from mlong (Huge thanks, that’s what got me started!). Seemed pretty good at first glance but I soon started missing some features. That example gives you the minimal shell around the Map QML element and is a great starting point.

The essential features I was still looking for were:

Pinch-zooming centred at the pinch centre, not at the screen/map centre.

While panning around, any minimal panning will trigger tile loading for the newly uncovered map regions (i.e. tiles adjacent to the on-screen map section are not pre-loaded as they could be)

Zooming (on the original QML Map) is discrete (I confess this one was the one that bothered me more!). Your all human pinch gesture is translated to a dumb “Zoom In/Out” action just like old-style “+/-” buttons would..

Flicking. The original QML Map’s panning feels static and non-natural

two-finger panning! Ok, this is a big one… As far as my world model goes, if while pinch-zooming(in/out) the user moves both fingers together, that means he’s panning/scrolling too! This is specially handy when you are looking for a place and you started the pinch gesture in a screen corner, it makes sense to bring that map section to the centre of the screen while zooming doesn’t it? It is a fact though that this is not a common feature in most mapping apps I tried (e.g. N8 and n950 Maps).

Adapted to Portrait/Landscape nicely.

In short, the goal is that same pinch zooming behaviour that you get on the n950’s web browser.

As one can see it’s not just a “quick fix”. I kept thinking about this for a while and then it occurred to me that I could throw the Map element inside a Flickable and making the actual Map Element bigger than the screen. With this approach you’d solve the buffering/loading problems when panning and even get flickering for free. Then it also occurred me that I could use QML’s standard “scale” property to fill the gaps in between the different discrete zoom levels..

The “continuous” zoom works by actually scaling the Map element (using transform: Scale{}..) until the next level of discrete zoom (Map.zoomLevel) is reached. The map would then switch to that new zoomLevel (upper or lower) and fix the Map scale accordingly.

If you look at the source you’ll notice that I’m not using the usual “Map.scale” property. I had to use the transform: Scale{..} . The problem is that when the screen orientation changes the whole map+flickable+content are changed too to fit it but then the scale transformOrigin (the point that stays in place when you scale something) wouldn’t change accordingly no matter how hard I tried; it would stay fixed to the first Item.Centre point where the scale was first changed.

The two finger zooming uses the pinch.center point and makes sure that point stays still in relation to the screen; for all pinchUpdates, the map is panned around to offset two types of drifts. The first is that the pinch.center coordinate will normally drift away due to the scaling point not being at the pinch.center, the second is due to the user actually moving the “pinch” around and thus moving the centre (this is what mekes the two-finger scrolling/panning work).

To use it, all you need is to include that Item in your page, like this:

Page {
id: mapPage
//considering you saved the previous item in a file named: "ContinuousMap.qml"
ContinuousMap {
id: mapFrame
//onMap landmarks can be added from cpp or directly here.
}
//You can layer your own buttons/UI on top of the map by adding them here.
}
//....

1 Comment:

Thanks for sharing this great map example!
I’ve been wondering why Nokia did not bother to provide a map widget which supports continuous zooming (vs. the discrete one they support now). The zoom property already is of type qreal so the interface suggests it does but ah what a disappointment that brings to us 😉

There are however some problems still that arise from this. E.g. when you add items to the map using the landmark support of the map widget, I assume they are zoomed also now. This happens when scaling inside one discreet zoom level. Of course for most of the items you want them to use a predefined amount of space (pixels) to look good on the UI. Now they are zoomed back-and forth when you use the pinch zoom.