Qt-based BB10 API Examples Documentation

Contents

Custom Map View Example

Files:

Description

The Custom Map View example displays a city map overlayed with three sliders that allow you to control altitude, tilt and heading. On top of this it demonstrates its main feature, which is the ability to put an overlay on another control and draw Controls on top of that.

Overview

In this example we'll learn how to use the MapView class of the BB10 framework to change altitude, tilt and heading as well as dropping push pins using an overlay.

The UI

The UI of this sample application consists of a page with a MapView, three slidders and several actions such as "Drop Pin", "Remove Pins", and some geographical location choices.

    ActionItem {
        title: qsTr("Drop Pin")
        imageSource: "asset:///images/pin.png"
        ActionBar.placement: ActionBarPlacement.OnBar
        onTriggered: {
            pinContainer.addPin(mapview.latitude, mapview.longitude);
        }
    },
    ActionItem {
        title: qsTr("Remove Pins")
        imageSource: "asset:///images/clearpin.png"
        ActionBar.placement: ActionBarPlacement.OnBar
        onTriggered: {
            pinContainer.removeAll();
        }
    },
    ActionItem {
        title: qsTr("Center URL")
        imageSource: "asset:///images/url.png"
        ActionBar.placement: ActionBarPlacement.InOverflow
        onTriggered: {
            status.setText(mapview.url());
        }
    },
    ActionItem {
        title: qsTr("Waterloo")
        imageSource: "asset:///images/pin.png"
        ActionBar.placement: ActionBarPlacement.InOverflow
        onTriggered: {
            mapview.latitude = 43.468245;
            mapview.longitude = -80.519603;
        }
    },
    ActionItem {
        title: qsTr("Manhattan")
        imageSource: "asset:///images/pin.png"
        ActionBar.placement: ActionBarPlacement.InOverflow
        onTriggered: {
            mapview.latitude = 40.791556;
            mapview.longitude = -73.967394;
        }
    }

These actions provide ways to manipulate the map view by dropping pins, clearing them or going to a specific location such as Manhattan. These manipulations are done through longitude, latitude and x,y calculations to drop them in the user tapped area. The current way might seem complicated, but its only a temporary workaround for the pin functionality that will be available as part of the MapView element coming soon.

    MapView {
        id: mapview
        altitude: 3000
        latitude: 43.449488
        longitude: -80.406777
        preferredWidth: 768
        preferredHeight: 1280
        onAltitudeChanged: {
            status.setText(qsTr("altitude changed: %1").arg(newAlt));
        }
        onHeadingChanged: {
            status.setText(qsTr("heading changed: %1").arg(newHeading));
        }
        onLatitudeChanged: {
            status.setText(qsTr("latitude changed: %1").arg(newLat));
        }
        onLongitudeChanged: {
            status.setText(qsTr("longitude changed: %1").arg(newLon));
        }
        onTiltChanged: {
            status.setText(qsTr("tilt changed: %1").arg(newTilt));
        }
        onMapLongPressed: {
            status.setText(qsTr("map long pressed"));
        }
        onRequestRender: {
            pinContainer.updateMarkers();
        }
    }

The MapView element is set to the Waterloo, Ontario latitude,longitude location by default. The markers (Pin locations) x,y positions are updated by invoking the updateMarkers javascript method, whenever the onRequestRender() signal is emitted.

    Label {
        id: status
        multiline: true
        textStyle {
            base: SystemDefaults.TextStyles.SmallText
            color: Color.Black
            fontWeight: FontWeight.Bold
        }
    }

Displays the status of the MapView element, such as current atlitude, tilt or latitude, longitude updates and some user actions.

    Container {
        leftPadding: 20
        rightPadding: 20
        bottomPadding: 20
        topPadding: 20
        horizontalAlignment: HorizontalAlignment.Right
        verticalAlignment: VerticalAlignment.Bottom
        ImageView {
            id: compassImage
            imageSource: "asset:///images/compass.png"
            horizontalAlignment: HorizontalAlignment.Center
            attachedObjects: [
                ImplicitAnimationController {
                    // Disable animations to avoid jumps between 0 and 360 degree
                    enabled: false
                }
            ]
        }
        ToggleButton {
            id: sensorToggle
            horizontalAlignment: HorizontalAlignment.Center
            checked: true
            onCheckedChanged: {
                if (checked) {
                    pinContainer.showMe();
                    pinContainer.me.visible = false;
                } else {
                    pinContainer.me.visible = false;
                }
            }
            onCreationCompleted: {
                pinContainer.showMe();
                pinContainer.me.visible = false;
            }
        }
    }
    Compass {
        property double azimuth: 0
        active: sensorToggle.checked
        axesOrientationMode: Compass.UserOrientation
        alwaysOn: false
        onReadingChanged: { // Called when a new compass reading is available
            mapview.setHeading(reading.azimuth);
            compassImage.rotationZ = 360 - reading.azimuth;
        }
    },
    PositionSource {
        id: positionSource
        updateInterval: 1000
        active: sensorToggle.checked
        onPositionChanged: {
            mapview.latitude = positionSource.position.coordinate.latitude;
            mapview.longitude = positionSource.position.coordinate.longitude;
            pinContainer.me.lat = positionSource.position.coordinate.latitude;
            pinContainer.me.lon = positionSource.position.coordinate.longitude;
            var xy = _mapViewTest.worldToPixelInvokable(mapview, pinContainer.me.lat, pinContainer.me.lon);
            pinContainer.me.x = xy[0];
            pinContainer.me.y = xy[1];
            pinContainer.me.visible = true;
        }
    }

The Container that encapslates a compass image and ToggleButton. This allows us to show the compass positioning and the current map latitude/longitude of your gps location.

    ComponentDefinition {
        id: pin
        source: "pin.qml"
    },
    ComponentDefinition {
        id: bubble
        source: "bubble.qml"
    },

These are component definitions of qml assets that allows for dynamic QML creation. In this case, it allows us to created predefined pin and bubble elements which represent the pin, bubble image assets respectively with scale transition for the pin custom element.

    RotationSensor {
        id: rotation
        property real x: 0
        active: sensorToggle.checked
        alwaysOn: false
        skipDuplicates: true
        onReadingChanged: {
            x = reading.x - 30
            if (x <= 40 && x > 0) {
                mapview.setTilt(x);
            }
        }
    },

The RotationSensor is used to determine the map tilt based on your handhelds rotation around the x-axis.

    Container {
        id: pinContainer
        // Must match the mapview width and height and position
        preferredHeight: 1280
        preferredWidth: 768
        //touchPropagationMode: TouchPropagationMode.PassThrough
        overlapTouchPolicy: OverlapTouchPolicy.Allow
        property variant currentBubble
        property variant me
        layout: AbsoluteLayout {
        }
        function addPin(lat, lon) {
            var marker = pin.createObject();
            marker.lat = lat;
            marker.lon = lon;
            var xy = _mapViewTest.worldToPixelInvokable(mapview, marker.lat, marker.lon);
            marker.x = xy[0];
            marker.y = xy[1];
            pinContainer.add(marker);
            marker.animDrop.play();
        }
        function showBubble(pin) {
            pinContainer.remove(currentBubble);
            var details = bubble.createObject();
            details.lat = pin.lat;
            details.lon = pin.lon;
            var xy = _mapViewTest.worldToPixelInvokable(mapview, details.lat, details.lon);
            details.x = xy[0];
            details.y = xy[1];
            pinContainer.add(details);
            details.play();
            currentBubble = details;
        }
        function showMe() {
            var marker = pin.createObject();
            marker.pinImageSource = "asset:///images/me.png"
            marker.pointerOffsetX = 30
            marker.pointerOffsetY = 30
            pinContainer.insert(-1, marker);
            marker.visible = false;
            me = marker;
        }
        function updateMarkers() {
            _mapViewTest.updateMarkers(mapview, pinContainer);
        }
        function removeBubble() {
            pinContainer.remove(currentBubble);
        }
        onTouch: {
            if (event.isDown()) {
                if ((event.localX <= currentBubble.actualX) || (event.localX >= currentBubble.actualX + currentBubble.contentWidth) || (event.localY <= currentBubble.actualY) || (event.localY >= currentBubble.actualY + currentBubble.contentHeight)) {
                    removeBubble();
                }
            }
        }
    }

The pinContainer is the overlay, which is responsible for displaying the Controls such as pins and bubbles on top of the MapView. It has various javascript functions defined in order to calculate the controls x,y screen coordinates based on longitude/latitude values from MapView, as well as updating by invoking updateMarkers() when the map is being moved. This container also provides an additional method, showMe(), to draw a marker for your current GPS location.

    Container {
        // File path of the pin image
        property string pinImageSource: "asset:///images/on_map_pin.png"
        // pointerOffsetX, pointerOffsetY is the position of the pixel in pin image that should point to the location. Change these to match your pin image.
        property int pointerOffsetX: 20
        property int pointerOffsetY: 58
        /////////////////////////////////////////////////////////
        id: root
        property int x: 0
        property int y: 0
        property double lat
        property double lon
        property alias animDrop: animDrop
        clipContentToBounds: false
        overlapTouchPolicy: OverlapTouchPolicy.Allow
        layoutProperties: AbsoluteLayoutProperties {
            id: position
            positionX: x - pointerOffsetX
            positionY: y - pointerOffsetY
        }
        ImageView {
            id: pinImage
            scaleX: .8
            scaleY: .8
            imageSource: pinImageSource
            focusPolicy: FocusPolicy.Touch
            overlapTouchPolicy: OverlapTouchPolicy.Allow
            onFocusedChanged: {
                if (focused) {
                    animFocus.play();
                    root.parent.showBubble(root);
                }
                if (! focused) {
                    animUnfocus.play();
                }
            }
            animations: [
                ScaleTransition {
                    id: animFocus
                    fromX: .8
                    toX: 1
                    fromY: .8
                    toY: 1
                    duration: 300
                    easingCurve: StockCurve.BounceOut
                },
                ScaleTransition {
                    id: animUnfocus
                    fromX: 1
                    toX: .8
                    fromY: 1
                    toY: .8
                    duration: 300
                    easingCurve: StockCurve.BounceOut
                }
            ]
        }
        animations: [
            TranslateTransition {
                id: animDrop
                fromY: - position.positionY
                toY: 0
                duration: 600
                easingCurve: StockCurve.BounceOut
            }
        ]
    }

This Container represents a pin, providing properties to set is x,y coordinates with offsets and various animations to deal with its creation and focus transitions.