Files:
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.
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 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.