Mapping a location

With the Cascades Maps APIs, you can add maps to your app and integrate them with other Location APIs, such as the Places APIs and positioning and geocoding services.

You can use the MapView class to display a map that the user can interact with in your app. You can customize your app with the following features:

  • Display points of interest or other location-aware elements on a map using the MapData class.
  • Add pins to mark points of interest using the GeoLocation class and display bubbles to provide details about a point of interest using the MapView::captionContent property.
  • Track the current location of the device using the GeoDeviceLocation class.
  • Center a specific Geographic object on a map even when the Geographic object's coordinates change.
  • Display routes between locations using the GeoPolyline class and regions, buildings, and arbitrary shapes using the GeoPolygon class.
  • Change the visual characteristics of displayed data by using custom icons ( Marker), styles ( Style and StyleSheet) and custom captions ( MapView::setCaptionContent()).
  • Connect your map to the gyroscope and compass sensors to tilt and rotate your map in 3-D space by using the attachedObjects property.

You can see these techniques in action in the MapView sample app. You can also follow a tutorial that teaches you how to create a maps app.

Adding a map

You can use the MapView class to add a map to your app.

Data on the map is provided by the MapData class, and rendering of the map defaults to the most appropriate engine based on the mapping data that is available.

To use the MapView class, you must add the following line to the .pro file in your project:

LIBS += -lbbcascadesmaps

You also need to import the bb.cascades.maps library in your .qml file.

Here's a QML example that displays a MapView of a location at specific longitude and latitude coordinates. The image to the right shows the MapView on a device with a physical keyboard.

Screen showing a MapView on a device with a physical keyboard.
import bb.cascades 1.2
import bb.cascades.maps 1.0

Page {
    Container {
        id: root
        
        MapView {
            id: mapview
            altitude: 3000
            latitude: 45.342614
            longitude: -75.914991
        }
    }
}

You can capture any of the following MapView signals with the built-in signal handlers:

  • The altitudeModeChanged() signal is emitted when the map's altitude mode has changed. This signal uses the AltitudeMode enumeration to capture how the altitude is being interpreted relative to the ground.

  • The captionLabelTapped() signal is emitted when the text label of an existing GeoLocation object on a map is tapped. A GeoLocation object is a location in geographic space that represents a latitude and longitude coordinate, and is visually represented on the map using its marker property.

  • The locationTapped() signal is emitted when an existing GeoLocation object on a map is tapped. You can listen for this signal and provide additional details about the location when it's emitted. For example, if you're creating a restaurant finder app, you can create a GeoLocation object for each restaurant in a geographic area in a MapView. When the user presses a location in your app, you can provide a URL for the restaurant or provide contact details for making a reservation at the restaurant.

  • The locationLongPressed() signal is emitted when a user presses and holds an existing GeoLocation object on a map. You can listen for this signal and provide actions for the user when it's emitted. For example, you can provide a context menu to save the location to the Places database, so that it can be accessed later. For more information about context menus, see Menus. To learn about the Places database, see Sharing a location.

  • The mapLongPressed() signal is emitted when a user presses and holds a location that is not associated with a GeoLocation in your MapView (which is also known as an empty map space). You can use MapLongPressToPinDrop to drop a Marker at this GeoLocation on the map.

Here's a simple example of how to listen for MapView signals and reflect the changes on a Label in your UI:

import bb.cascades 1.2
import bb.cascades.maps 1.0

Page {
    Container {
        id: root
        
        Label {
            id: status
        }
        
        MapView {
            id: mapview
            altitude: 3000
            latitude: 45.342614
            longitude: -75.914991        
            
            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));
            }
            onFocusedIdChanged: {
                status.setText(qsTr("focused id changed to %1")
                .arg(idWithFocus));
            }
            onFollowedIdChanged: {
                status.setText(qsTr("followed id changed to %1")
                .arg(idOfFollowed));
            }
            onLocationTapped: {
                status.setText(qsTr("location tapped"));
            }
            onLocationLongPressed: {
                status.setText(qsTr("location long pressed"));
            }
            onMapLongPressed: {
                status.setText(qsTr("map long pressed"));
            }
            onCaptionButtonClicked: {
                status.setText(qsTr("caption button clicked"));
            }
            onCaptionLabelTapped: {
                status.setText(qsTr("caption label tapped"));
            }
            onCreationCompleted: {
                status.setText(qsTr("Map displayed"));
                setCaptionGoButtonVisible( false );
            }
        } // End of MapView
    } // End of Container
} // End of Page

You can use the MapView class to add a map to your app in C++. To use the MapView class in your C++ code, you must include the following line in your C++ file:

#include <bb/cascades/maps/MapView>

Here's a C++ example that displays a MapView of a location at specific longitude and latitude coordinates.

#include "applicationui.hpp"

#include <bb/cascades/Application>
#include <bb/cascades/Page>
#include <bb/cascades/maps/MapView>

using namespace bb::cascades;
using namespace bb::cascades::maps;

ApplicationUI::ApplicationUI(bb::cascades::Application *app) :
        QObject(app)
{
    // Create a MapView for your location
    MapView* mv = new MapView();
    mv -> setAltitude(3000);
    mv -> setLatitude(45.342614);
    mv -> setLongitude(-75.914991);

    // Add the MapView to the root Page
    Page *page = new Page();
    page->setContent(mv);
    app->setScene(page);
}

Adding pins and bubbles to a map

A GeoLocation object is a visual representation of a Point on a map. Every GeoLocation has a marker, which is represented by a Marker object. This Marker is the image used to represent the GeoLocation. For example, you can use pins, arrows, or bulls-eyes as markers. When a location is in focus, the user is presented with a caption dialog box that they can use to create and manage markers and custom icons for the location. The caption dialog box contains the title and description for the location that you specify, and an action button. The title and description of a Marker support HTML tags. You can use HTML tags to format the text or even insert hyperlinks to open web pages.

Signals are emitted when the user taps the text or taps the action button of a GeoLocation caption dialog box. You can capture these signals and provide more details about a location when these signals are emitted. For example, your app can provide a URL for the business at the location or start a turn-by-turn navigation to that location.

Screen showing a marker on a map.The triangle icon on a GeoLocation caption dialog box is the action button. You can capture the captionButtonClicked() signal of MapView to handle the button's action. You can also capture the captionLabelTapped() signal to react when the user taps the caption's text. You can hide the action button by using the setCaptionGoButtonVisible() function.

To add custom pins and bubbles to a map, you create a GeoLocation instance for the point on the map and then create a Marker instance with the custom pin information, such as the file path, image size, and so on. Next, you assign the Marker to the GeoLocation and add the GeoLocation object to a MapView.

Here's an example that creates a Marker at a Geolocation labeled "Ottawa" at the coordinates 45.342614N, 75.914991W. The example uses an image for a purple pin found at the file path /usr/hmi/lbs/purple_pin.png to create a custom icon for the Marker.

The image to the right shows the sample running on a device.

Screen showing a MapView with a custom pin and marker.

#include "applicationui.hpp"

#include <bb/cascades/Application>
#include <bb/cascades/Page>
#include <bb/cascades/maps/MapView>
#include <bb/cascades/maps/MapData>
#include <bb/platform/geo/Marker>
#include <bb/platform/geo/GeoLocation>


using namespace bb::cascades;
using namespace bb::cascades::maps;
using namespace bb::platform::geo;

ApplicationUI::ApplicationUI(bb::cascades::Application *app) :
        QObject(app)
{
    // Create a MapView for your location
    MapView* mv = new MapView();
    mv -> setAltitude(3000);
    mv -> setLatitude(45.342614);
    mv -> setLongitude(-75.914991);


    // Create an instance of Marker to represent the pin
    Marker purpleMarker;
    purpleMarker.setIconUri( "/usr/hmi/lbs/purple_pin.png" );
    purpleMarker.setIconSize( QSize( 64, 64 ) );

    // Offset the location coordinates so that it's near the bottom
    // and center of the icon (that is, the pin's point)
    purpleMarker.setLocationCoordinate( QPoint( 24, 64 ) );

    // Set the position on the icon so that the caption bubble's
    // tail points to the top center of the pin
    purpleMarker.setCaptionTailCoordinate( QPoint( 24, 3 ) );

    // Create the GeoLocation object and set the marker
    GeoLocation* ottawa = new GeoLocation(
            "id-ottawa",
            "Ottawa",
            Point( 45.342614, -75.914991 ) );

    ottawa->setMarker( purpleMarker );

    // Add the location to your map
    mv->mapData()->add( ottawa );

    // When your location is within the map's viewport,
    // you will see the custom icon
    mv->setLocationOnVisible();

    // Add the MapView to the root Page
    Page *page = new Page();
    page->setContent(mv);
    app->setScene(page);
}

You can use the setCaptionContent() function to create a custom caption dialog box using Cascades components. You can add items such as icons, text, or buttons to the caption dialog box. The height of this caption dialog box is limited in size.

Following locations on a map

You can use the GeoDeviceLocation class to monitor the device's location on a map. This is useful if you want to keep the map centered on the user's device location. For example, adding a GeoDeviceLocation instance to the map’s data adds a marker to the map that show the current device’s location. If that location is being “followed”, then that marker is kept in the center of the map, even as the device moves. When the user interacts with the map (for example, if the user pans), then the location of the current device is no longer followed.

Here's a code sample that places a specific coordinates on the map, and then tells the map to follow that location:

#include <bb/cascades/maps/MapData>
#include <bb/cascades/maps/MapView>
#include <bb/cascades/Page>
#include <bb/cascades/Container>
#include <bb/cascades/Application>
#include <bb/platform/geo/GeoDeviceLocation>

using namespace bb::platform::geo;
using namespace bb::cascades;
using namespace bb::cascades::maps;

MappingApp::MappingApp( bb::cascades::Application* app ) {
    Page* root = new Page;

    Container* topContainer = Container::create();

    MapView* mapview = new MapView;
    mapview->setAltitude( 8000 );
    mapview->setLatitude( 35.417 );
    mapview->setLongitude( -75.7 );
    topContainer->add( mapview );

    // Add a specific location to the map.
    mapview->mapData()
        ->add( new GeoDeviceLocation( "device-location-id" ) );

    // Tell the map to follow the location 
    // (to keep it in the center of the map).
    mapview->setFollowedId( "device-location-id" );

    root->setContent( topContainer );
    app->setScene( root );
}

You can also set up a MapView to follow a specific location on your map. You can update the coordinates of the followed location and watch the map move automatically with it. This technique is useful to track friends on a map, or keep the map centered on a specific set of coordinates.

The following example creates a MapView at specific coordinates and adds a ToggleButton to the UI to allow the user to decide whether to display the followed location on a map:

import bb.cascades 1.2
import bb.cascades.maps 1.0
import QtMobilitySubset.location 1.1

Page {
    Container {
        id: root
        layout: DockLayout {
        }
        MapView {
            id: mapview
            objectName: "mapViewObj"
            altitude: 3000
            latitude: 43.449488
            longitude: -80.406777
            preferredWidth: 768
            preferredHeight: 1280
        }
        Container {
            horizontalAlignment: HorizontalAlignment.Fill
            verticalAlignment: VerticalAlignment.Top
            topPadding: 5
            leftPadding: 5
            bottomPadding: 5
            background: Color.create("#ddffffff")

        }
        Container {
            leftPadding: 20
            rightPadding: 20
            bottomPadding: 20
            topPadding: 20
            horizontalAlignment: HorizontalAlignment.Right
            verticalAlignment: VerticalAlignment.Bottom
            overlapTouchPolicy: OverlapTouchPolicy.Allow
            
            ToggleButton {
                id: sensorToggle
                horizontalAlignment: HorizontalAlignment.Center
                checked: true
                onCheckedChanged: {
                    if (checked) {
                        mapview.setFollowedId("device-location-id");
                    } else {
                        mapview.setFollowedId("");
                    }
                }
                onCreationCompleted: {
                    mapview.setFollowedId("device-location-id");
                }
            }
        }
    }
}

The device-location-id is created in the applicationui.cpp file.

    
    QObject* mapViewAsQObject = 
        root->findChild<QObject*>(QString("mapViewObj"));

    if (mapViewAsQObject) {
        mapView = 
            qobject_cast<bb::cascades::maps::MapView*>(mapViewAsQObject);
        mapView->setCaptionGoButtonVisible(true);

        if (mapView) {
            // Create a data provider just for the device 
            // location object. When the clear function is 
            // called, this object is not removed.
            DataProvider* deviceLocDataProv = 
                new DataProvider("device-location-data-provider");
            mapView->mapData()->addProvider(deviceLocDataProv);

            // Create a geolocation just for the device's location.
            deviceLocation = new GeoLocation("device-location-id");
            deviceLocation->setName("Current Device Location");


            // For this location, replace the standard default pin 
            // with a special bulls-eye image.
            Marker bullseye = 
                Marker(UIToolkitSupport::absolutePathFromUrl(
                       QUrl("asset:///images/me.png")), 
                       QSize(60, 60),
                       QPoint(29, 29), 
                       QPoint(29, 1));
            deviceLocation->setMarker(bullseye);

            deviceLocDataProv->add(deviceLocation);
        }
    }

You can see the complete source code for this technique in the MapView sample.

Defining two-dimensional areas on a map

You can use the following classes to define a two-dimensional area on your map. You can use these areas to define the limits of a Geographic element:

  • Geographic: An entity that can be placed on a map
  • GeoList: A container of Geographic elements
  • BoundingBox: A two-dimensional, axis-aligned bounding box (rectangle)

You can also use the GeoLocation class to define a radius of interest. This radius could be an accuracy circle or a cell tower reach for a location on a map.

You can set limits to what the user can view by using setViewBoundaries().

Adding shapes and lines to a map

You can use the following classes to add shapes and lines to maps:

  • GeoPolyline: A line based on geographic data that is represented by a set of ordered Point objects
  • GeoPolygon: A polygon based on geographic data

For example, the following C++ sample creates a UI with a map of BlackBerry locations in Ottawa.

Screen showing polylines and polygons added to a map on a device.
#include "applicationui.hpp"

#include <bb/cascades/Page>
#include <bb/cascades/Container>
#include <bb/cascades/StackLayout>
#include <bb/cascades/DockLayout>
#include <bb/cascades/Color>
#include <bb/cascades/Button>
#include <bb/cascades/Application>
#include <bb/cascades/maps/MapView>
#include <bb/cascades/maps/MapData>
#include <bb/platform/geo/Coordinate>
#include <bb/platform/geo/Polyline>
#include <bb/platform/geo/GeoPolyline>
#include <bb/platform/geo/GeoPolygon>
#include <bb/platform/geo/Geographic>
#include <bb/platform/geo/Style>
#include <bb/platform/geo/StyleSheet>
#include <bb/platform/geo/EdgeSize>
#include <bb/platform/geo/EdgeStyle>
#include <QDebug>


using namespace bb::cascades;
using namespace bb::cascades::maps;
using namespace bb::platform::geo;

ApplicationUI::ApplicationUI( bb::cascades::Application* app ) :
    QObject( app ), mapControl( 0 ) {
    Page* root = new Page;

    StackLayout* topContLayout = new StackLayout;
    topContLayout->setOrientation( LayoutOrientation::TopToBottom);
    Container* topContainer = Container::create()
              .layout( topContLayout )
              .horizontal( HorizontalAlignment::Center)
              .vertical( VerticalAlignment::Center);

    Container* buttonCont = Container::create()
              .background( Color::Blue )
              .horizontal( HorizontalAlignment::Center)
              .vertical( VerticalAlignment::Center)
              .layout( new StackLayout);
    topContainer->add( buttonCont );

    buttonCont->add( Button::create().text( "March" ).connect(
    		SIGNAL( clicked() ),
    		this,
    		SLOT( onBBMarchClicked() ) ) );
    buttonCont->add( Button::create().text( "Innov 5050" ).connect(
    		SIGNAL( clicked() ),
    		this,
    		SLOT( onBBInnovationClicked() ) ) );
    buttonCont->add( Button::create().text( "Farrar" ).connect(
    		SIGNAL( clicked() ),
    		this,
    		SLOT( onBBFarrarClicked() ) ) );
    buttonCont->add( Button::create().text( "All" ).connect(
    		SIGNAL( clicked() ),
    		this,
    		SLOT( onAllClicked() ) ) );

    Container* mapCont = Container::create()
                         .background( Color::LightGray )
                         .layout( new DockLayout );
    topContainer->add( mapCont );

    mapControl = new MapView;
    mapControl->setAltitude( 10000 );
    mapControl->setLatitude( 45.5 );
    mapControl->setLongitude( -75.5 );
    mapControl->setTilt( 2 );
    mapControl->setVerticalAlignment (VerticalAlignment::Center);
    mapControl->setHorizontalAlignment (HorizontalAlignment::Center);
    mapCont->add( mapControl );

    root->setContent( topContainer );
    app->setScene( root );
}

Then, you can define lines, rectangles, and other shapes on your map. You can use these shapes to outline buildings and illustrate routes between locations.

For example, you can define each BlackBerry location in Ottawa as a GeoPolygon. Then, you can define a road between two buildings as a GeoPolyline.

    // March building
    Polyline marchPoly;
    marchPoly.append( Coordinate( 45.34160960994857,
    							 -75.91488771580505 ) );
    marchPoly.append( Coordinate( 45.34187180699856,
    							 -75.91444157785021 ) );
    marchPoly.append( Coordinate( 45.34213741045421,
    							 -75.9147878149265 ) );
    marchPoly.append( Coordinate( 45.3418633664853,
    							 -75.91522259607544 ) );
    GeoPolygon* march = new GeoPolygon( "bb march" );
    march->setName( "BB March" );
    march->setOuterBoundary( marchPoly );
    mapControl->mapData()->add( march );

    // Innovation building
    Polyline innovPoly;
    innovPoly.append( Coordinate( 45.342190763965,
    							 -75.92918483922576 ) );
    innovPoly.append( Coordinate( 45.3422030727039,
    							 -75.92842960530541 ) );
    innovPoly.append( Coordinate( 45.34292044800614,
    							 -75.92845653710081 ) );
    innovPoly.append( Coordinate( 45.34291079293224,
    							 -75.92921466365985 ) );
    GeoPolygon* innov = new GeoPolygon( "bb innovation" );
    innov->setName( "BB Innovation 5050" );
    innov->setStyleFamily( "new building style" );
    innov->setOuterBoundary( innovPoly );
    mapControl->mapData()->add( innov );

    // Farrar building
    Polyline farrarPoly;
    farrarPoly.append( Coordinate( 45.34325993310162,
    							  -75.91001547191871 ) );
    farrarPoly.append( Coordinate( 45.3433709788002,
    							  -75.90990080533155 ) );
    farrarPoly.append( Coordinate( 45.34331196304391,
    							  -75.90979119465503 ) );
    farrarPoly.append( Coordinate( 45.3438603204231,
    							  -75.90917235749987 ) );
    farrarPoly.append( Coordinate( 45.34393161240791,
    							  -75.90925552556062 ) );
    farrarPoly.append( Coordinate( 45.34401809926613,
    							  -75.90912780918529 ) );
    farrarPoly.append( Coordinate( 45.34423872787728,
    							  -75.90948711302949 ) );
    farrarPoly.append( Coordinate( 45.34348760002223,
    							  -75.91037151832083 ) );
    farrarPoly.append( Coordinate( 45.34325993310162,
    							  -75.91001547191871 ) );
    GeoPolygon* farrar = new GeoPolygon( "bb farrar" );
    farrar->setName( "BB Farrar" );
    farrar->setOuterBoundary( farrarPoly );
    mapControl->mapData()->add( farrar );

    // Road from Innovation to Farrar
    Polyline innovFarrarPoly;
    innovFarrarPoly.append( Coordinate( 45.34254984806693,
    								   -75.92945946719877 ) );
    innovFarrarPoly.append( Coordinate( 45.3425414457549,
    								   -75.93035697294239 ) );
    innovFarrarPoly.append( Coordinate( 45.34315026720957,
    								   -75.93028220153305 ) );
    innovFarrarPoly.append( Coordinate( 45.34353751590488,
    								   -75.93010563421292 ) );
    innovFarrarPoly.append( Coordinate( 45.34415384365244,
    								   -75.93010941154165 ) );
    innovFarrarPoly.append( Coordinate( 45.3446852579663,
    								   -75.93045412888773 ) );
    innovFarrarPoly.append( Coordinate( 45.34510219586706,
    								   -75.93091086896648 ) );
    innovFarrarPoly.append( Coordinate( 45.3478399457104,
    								   -75.92664696330979 ) );
    innovFarrarPoly.append( Coordinate( 45.34892905492961,
    								   -75.92470264219908 ) );
    innovFarrarPoly.append( Coordinate( 45.34940283817473,
    								   -75.92394023369711 ) );
    innovFarrarPoly.append( Coordinate( 45.34964903911299,
    								   -75.92286006616197 ) );
    innovFarrarPoly.append( Coordinate( 45.34957471608524,
    								   -75.92186111620949 ) );
    innovFarrarPoly.append( Coordinate( 45.34942614506517,
    								   -75.92095675975592 ) );
    innovFarrarPoly.append( Coordinate( 45.34914680663247,
    								   -75.92094662004 ) );
    innovFarrarPoly.append( Coordinate( 45.34876501569315,
    								   -75.92071910835911 ) );
    innovFarrarPoly.append( Coordinate( 45.34814675067516,
    								   -75.92025552400592 ) );
    innovFarrarPoly.append( Coordinate( 45.34472217000616,
    								   -75.9158069805362 ) );
    innovFarrarPoly.append( Coordinate( 45.34448272728834,
    								   -75.91535604586107 ) );
    innovFarrarPoly.append( Coordinate( 45.3441660679114,
    								   -75.91436625488032 ) );
    innovFarrarPoly.append( Coordinate( 45.3439733396328,
    								   -75.91389463948262 ) );
    innovFarrarPoly.append( Coordinate( 45.34355028143146,
    		    					   -75.91328243932992 ) );
    innovFarrarPoly.append( Coordinate( 45.34259459771675,
    								   -75.91208641090967 ) );
    innovFarrarPoly.append( Coordinate( 45.34182514673778,
    								   -75.9109443388713 ) );
    innovFarrarPoly.append( Coordinate( 45.343281998413,
    								   -75.90920432927531 ) );
    innovFarrarPoly.append( Coordinate( 45.34340746859033,
    								   -75.90895093003152 ) );
    innovFarrarPoly.append( Coordinate( 45.34357846477555,
    								   -75.90902807032643 ) );
    innovFarrarPoly.append( Coordinate( 45.34358883511185,
    								   -75.90919302761208 ) );
    innovFarrarPoly.append( Coordinate( 45.34349362982461,
    								   -75.9094334832255 ) );
    GeoPolyline* innovFarrar = new GeoPolyline( "innovation to farrar");
    innovFarrar->setName( "Innovation to Farrar" );
    innovFarrar->setLine( innovFarrarPoly );
    mapControl->mapData()->add( innovFarrar );

You can use the following classes to define styles to highlight each geographic element on your map:

For example, you can define a Style to apply to a Polygon that represents a building, or you can define a Style for a Polyline that represents a route between buildings.

    
    // Styles
    StyleSheet styles;

    // All new buildings have solid blue fill
    Style newBuildings;
    newBuildings.setFillColor( 0xFF0000FF );
    styles.addStyleForFamily( "new building style", newBuildings );

    // All polygons have no edge
    Style polygons;
    polygons.setEdgeSize( EdgeSize::None );
    styles.addStyleForClass( march, polygons );

    // All polylines have a solid, medium edge
    Style polylines;
    polylines.setEdgeStyle( EdgeStyle::Solid );
    polylines.setEdgeSize( EdgeSize::Medium );
    polylines.setEdgeColor( 0x8F00FF00 );
    styles.addStyleForClass( innovFarrar, polylines );

    // Innovation 5050 is special because it
    // is where the author of this sample works
    Style myWorkStyle;
    myWorkStyle.setEdgeSize( EdgeSize::Small );
    myWorkStyle.setEdgeStyle( EdgeStyle::Dot );
    myWorkStyle.setEdgeColor( 0xFFFF0000 );
    styles.addStyleForId( innov->geoId(), myWorkStyle );

    mapControl->mapData()->setStyles( styles );
    mapControl->setCaptionGoButtonVisible( false );

You can view the applicationui.hpp and applicationui.cpp files for the complete code sample below.

#ifndef ApplicationUI_HPP_
#define ApplicationUI_HPP_

#include <QObject>
#include <bb/cascades/maps/MapView>

namespace bb
{
    namespace cascades
    {
        class Application;
    }
}

class ApplicationUI: public QObject
{
    Q_OBJECT
    public:
        ApplicationUI( bb::cascades::Application* app );
        virtual ~ApplicationUI() { }

    private slots:
        void onBBMarchClicked();
        void onBBInnovationClicked();
        void onBBFarrarClicked();
        void onAllClicked();
        void setLocationOn( const QString& id ) const;

    private:
        bb::cascades::maps::MapView* mapControl;
};

#endif /* ApplicationUI_HPP_ */
#include "applicationui.hpp"

#include <bb/cascades/Page>
#include <bb/cascades/Container>
#include <bb/cascades/StackLayout>
#include <bb/cascades/DockLayout>
#include <bb/cascades/Color>
#include <bb/cascades/Button>
#include <bb/cascades/Application>
#include <bb/cascades/maps/MapView>
#include <bb/cascades/maps/MapData>
#include <bb/platform/geo/Coordinate>
#include <bb/platform/geo/Polyline>
#include <bb/platform/geo/GeoPolyline>
#include <bb/platform/geo/GeoPolygon>
#include <bb/platform/geo/Geographic>
#include <bb/platform/geo/Style>
#include <bb/platform/geo/StyleSheet>
#include <bb/platform/geo/EdgeSize>
#include <bb/platform/geo/EdgeStyle>
#include <QDebug>


using namespace bb::cascades;
using namespace bb::cascades::maps;
using namespace bb::platform::geo;

ApplicationUI::ApplicationUI( bb::cascades::Application* app ) :
    QObject( app ), mapControl( 0 ) {
    Page* root = new Page;

    StackLayout* topContLayout = new StackLayout;
    topContLayout->setOrientation( LayoutOrientation::TopToBottom);
    Container* topContainer = Container::create()
                              .layout( topContLayout )
                              .horizontal( HorizontalAlignment::Center)
                              .vertical( VerticalAlignment::Center);

    Container* buttonCont = Container::create()
                            .background( Color::Blue )
                            .horizontal( HorizontalAlignment::Center)
                            .vertical( VerticalAlignment::Center)
                            .layout( new StackLayout);
    topContainer->add( buttonCont );

    buttonCont->add( Button::create().text( "March" ).connect(
    		SIGNAL( clicked() ),
    		this,
    		SLOT( onBBMarchClicked() ) ) );
    buttonCont->add( Button::create().text( "Innov 5050" ).connect(
    		SIGNAL( clicked() ),
    		this,
    		SLOT( onBBInnovationClicked() ) ) );
    buttonCont->add( Button::create().text( "Farrar" ).connect(
    		SIGNAL( clicked() ),
    		this,
    		SLOT( onBBFarrarClicked() ) ) );
    buttonCont->add( Button::create().text( "All" ).connect(
    		SIGNAL( clicked() ),
    		this,
    		SLOT( onAllClicked() ) ) );

    Container* mapCont = Container::create()
                         .background( Color::LightGray )
                         .layout( new DockLayout );
    topContainer->add( mapCont );

    mapControl = new MapView;
    mapControl->setAltitude( 10000 );
    mapControl->setLatitude( 45.5 );
    mapControl->setLongitude( -75.5 );
    mapControl->setTilt( 2 );
    mapControl->setVerticalAlignment (VerticalAlignment::Center);
    mapControl->setHorizontalAlignment (HorizontalAlignment::Center);
    mapCont->add( mapControl );

    root->setContent( topContainer );
    app->setScene( root );


    // March building
    Polyline marchPoly;
    marchPoly.append( Coordinate( 45.34160960994857,
    							 -75.91488771580505 ) );
    marchPoly.append( Coordinate( 45.34187180699856,
    							 -75.91444157785021 ) );
    marchPoly.append( Coordinate( 45.34213741045421,
    							 -75.9147878149265 ) );
    marchPoly.append( Coordinate( 45.3418633664853,
    							 -75.91522259607544 ) );
    GeoPolygon* march = new GeoPolygon( "bb march" );
    march->setName( "BB March" );
    march->setOuterBoundary( marchPoly );
    mapControl->mapData()->add( march );

    // Innovation building
    Polyline innovPoly;
    innovPoly.append( Coordinate( 45.342190763965,
    							 -75.92918483922576 ) );
    innovPoly.append( Coordinate( 45.3422030727039,
    							 -75.92842960530541 ) );
    innovPoly.append( Coordinate( 45.34292044800614,
    							 -75.92845653710081 ) );
    innovPoly.append( Coordinate( 45.34291079293224,
    							 -75.92921466365985 ) );
    GeoPolygon* innov = new GeoPolygon( "bb innovation" );
    innov->setName( "BB Innovation 5050" );
    innov->setStyleFamily( "new building style" );
    innov->setOuterBoundary( innovPoly );
    mapControl->mapData()->add( innov );

    // Farrar building
    Polyline farrarPoly;
    farrarPoly.append( Coordinate( 45.34325993310162,
    							  -75.91001547191871 ) );
    farrarPoly.append( Coordinate( 45.3433709788002,
    							  -75.90990080533155 ) );
    farrarPoly.append( Coordinate( 45.34331196304391,
    							  -75.90979119465503 ) );
    farrarPoly.append( Coordinate( 45.3438603204231,
    							  -75.90917235749987 ) );
    farrarPoly.append( Coordinate( 45.34393161240791,
    							  -75.90925552556062 ) );
    farrarPoly.append( Coordinate( 45.34401809926613,
    							  -75.90912780918529 ) );
    farrarPoly.append( Coordinate( 45.34423872787728,
    							  -75.90948711302949 ) );
    farrarPoly.append( Coordinate( 45.34348760002223,
    							  -75.91037151832083 ) );
    farrarPoly.append( Coordinate( 45.34325993310162,
    							  -75.91001547191871 ) );
    GeoPolygon* farrar = new GeoPolygon( "bb farrar" );
    farrar->setName( "BB Farrar" );
    farrar->setOuterBoundary( farrarPoly );
    mapControl->mapData()->add( farrar );

    // Road from Innovation to Farrar
    Polyline innovFarrarPoly;
    innovFarrarPoly.append( Coordinate( 45.34254984806693,
    								   -75.92945946719877 ) );
    innovFarrarPoly.append( Coordinate( 45.3425414457549,
    								   -75.93035697294239 ) );
    innovFarrarPoly.append( Coordinate( 45.34315026720957,
    								   -75.93028220153305 ) );
    innovFarrarPoly.append( Coordinate( 45.34353751590488,
    								   -75.93010563421292 ) );
    innovFarrarPoly.append( Coordinate( 45.34415384365244,
    								   -75.93010941154165 ) );
    innovFarrarPoly.append( Coordinate( 45.3446852579663,
    								   -75.93045412888773 ) );
    innovFarrarPoly.append( Coordinate( 45.34510219586706,
    								   -75.93091086896648 ) );
    innovFarrarPoly.append( Coordinate( 45.3478399457104,
    								   -75.92664696330979 ) );
    innovFarrarPoly.append( Coordinate( 45.34892905492961,
    								   -75.92470264219908 ) );
    innovFarrarPoly.append( Coordinate( 45.34940283817473,
    								   -75.92394023369711 ) );
    innovFarrarPoly.append( Coordinate( 45.34964903911299,
    								   -75.92286006616197 ) );
    innovFarrarPoly.append( Coordinate( 45.34957471608524,
    								   -75.92186111620949 ) );
    innovFarrarPoly.append( Coordinate( 45.34942614506517,
    								   -75.92095675975592 ) );
    innovFarrarPoly.append( Coordinate( 45.34914680663247,
    								   -75.92094662004 ) );
    innovFarrarPoly.append( Coordinate( 45.34876501569315,
    								   -75.92071910835911 ) );
    innovFarrarPoly.append( Coordinate( 45.34814675067516,
    								   -75.92025552400592 ) );
    innovFarrarPoly.append( Coordinate( 45.34472217000616,
    								   -75.9158069805362 ) );
    innovFarrarPoly.append( Coordinate( 45.34448272728834,
    								   -75.91535604586107 ) );
    innovFarrarPoly.append( Coordinate( 45.3441660679114,
    								   -75.91436625488032 ) );
    innovFarrarPoly.append( Coordinate( 45.3439733396328,
    								   -75.91389463948262 ) );
    innovFarrarPoly.append( Coordinate( 45.34355028143146,
    		    					   -75.91328243932992 ) );
    innovFarrarPoly.append( Coordinate( 45.34259459771675,
    								   -75.91208641090967 ) );
    innovFarrarPoly.append( Coordinate( 45.34182514673778,
    								   -75.9109443388713 ) );
    innovFarrarPoly.append( Coordinate( 45.343281998413,
    								   -75.90920432927531 ) );
    innovFarrarPoly.append( Coordinate( 45.34340746859033,
    								   -75.90895093003152 ) );
    innovFarrarPoly.append( Coordinate( 45.34357846477555,
    								   -75.90902807032643 ) );
    innovFarrarPoly.append( Coordinate( 45.34358883511185,
    								   -75.90919302761208 ) );
    innovFarrarPoly.append( Coordinate( 45.34349362982461,
    								   -75.9094334832255 ) );
    GeoPolyline* innovFarrar = new GeoPolyline( "innovation to farrar" );
    innovFarrar->setName( "Innovation to Farrar" );
    innovFarrar->setLine( innovFarrarPoly );
    mapControl->mapData()->add( innovFarrar );


    // Styles
    StyleSheet styles;

    // All new buildings have solid blue fill
    Style newBuildings;
    newBuildings.setFillColor( 0xFF0000FF );
    styles.addStyleForFamily( "new building style", newBuildings );

    // All polygons have no edge
    Style polygons;
    polygons.setEdgeSize( EdgeSize::None );
    styles.addStyleForClass( march, polygons );

    // All polylines have a solid, medium edge with XX opacity
    Style polylines;
    polylines.setEdgeStyle( EdgeStyle::Solid );
    polylines.setEdgeSize( EdgeSize::Medium );
    polylines.setEdgeColor( 0x8F00FF00 );
    styles.addStyleForClass( innovFarrar, polylines );

    // Innovation 5050 is special because it
    // is where the author of this sample works
    Style myWorkStyle;
    myWorkStyle.setEdgeSize( EdgeSize::Small );
    myWorkStyle.setEdgeStyle( EdgeStyle::Dot );
    myWorkStyle.setEdgeColor( 0xFFFF0000 );
 //   styles.addStyleForId( innov->id(), myWorkStyle );
    styles.addStyleForId( innov->geoId(), myWorkStyle );

    mapControl->mapData()->setStyles( styles );
    mapControl->setCaptionGoButtonVisible( false );
}

void ApplicationUI::onBBMarchClicked() {
    setLocationOn( "bb march" );
}

void ApplicationUI::onBBInnovationClicked() {
    setLocationOn( "bb innovation" );
}

void ApplicationUI::onBBFarrarClicked() {
    setLocationOn( "bb farrar" );
}

void ApplicationUI::onAllClicked() {
    qDebug() << "ApplicationUI::onAllClicked()";
    if ( mapControl ) {
        mapControl->setLocationOnVisible();
    }
}

void ApplicationUI::setLocationOn( const QString& id ) const {
    qDebug() << "ApplicationUI::setLocationOn( " << id << " )";
    if ( mapControl ) {
        mapControl->setFocusedId( id );
        mapControl->setLocationOnFocused();
    }
}

Connecting your map to the gyroscope and compass sensors

You can connect a MapView to the gyroscope and compass sensors to rotate and tilt your map.

import bb.cascades 1.0
import QtMobility.sensors 1.2
import bb.cascades.maps 1.0
import QtMobilitySubset.location 1.1

Page {
    Container {
        id: root
        layout: DockLayout {}
        MapView {
            id: mapview
            objectName: "mapViewObj"
            altitude: 3000
            latitude: 43.449488
            longitude: -80.406777
            preferredWidth: 768
            preferredHeight: 1280
        }
        Container {
            horizontalAlignment: HorizontalAlignment.Fill
            verticalAlignment: VerticalAlignment.Top
            topPadding: 5
            leftPadding: 5
            bottomPadding: 5
            background: Color.create("#ddffffff")
        }
        Container {
            leftPadding: 20
            rightPadding: 20
            bottomPadding: 20
            topPadding: 20
            horizontalAlignment: HorizontalAlignment.Right
            verticalAlignment: VerticalAlignment.Bottom
            overlapTouchPolicy: OverlapTouchPolicy.Allow
            
            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
                    }
                ]
            }
        }
    }
    attachedObjects: [
        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);
                }
            }
        },
        Compass {
            property double azimuth: 0
            active: sensorToggle.checked
            axesOrientationMode: Compass.UserOrientation
            alwaysOn: false
            // Adjust the map when a new compass reading is available
            onReadingChanged: { 
                mapview.setHeading(reading.azimuth);
                compassImage.rotationZ = 360 - reading.azimuth;
            }
        }
    ]
}

For more information about supported sensors, see Sensors.

Updating and rendering a map

You can use the following classes to render a map on the device. You shouldn't really need to use these classes unless you have a very special case where you to want to extend the MapView class.

Last modified: 2013-12-21

comments powered by Disqus