Sorry about the red box, but we really need you to update your browser. Read this excellent article if you're wondering why we are no longer supporting this browser version. Go to Browse Happy for browser suggestions and how to update.

Visualizing a location

Adding a map to your app

You can add a map to your app by using the MapField class and RichMapField class, which are provided in the net.rim.device.api.lbs.maps.ui package. For example, you can create an app that displays a map showing the BlackBerry device user's current location and points of interest in the surrounding area.

The MapField class extends the net.rim.device.api.ui.Field class. With MapField, you can add the following functionality to your app:

  • Render a map in a UI field.
  • Pan and zoom the map using the keyboard, trackpad, trackball, or touch screen.
  • Set styles that define the visual characteristics of mappable objects.
  • Determine when mappable objects receive focus or when points on a map are selected.

The RichMapField class extends the functionality of MapField. With RichMapField, you can add the following features to your app:

  • Add utility fields, such as a center target, zoom indicator, and a hint field.
  • Overlay fields on a map.
  • Share focus with other UI components on a screen to allow users to navigate through map field components to other components on the screen.

Each MapField or RichMapField instance uses one thread to render a map. For example, if an app has two MapField instances running at the same time, two threads are used. The thread ends when the MapField instance is processed for garbage collection. Make sure the app does not exceed the limit of available threads. To end the thread for the MapField or RichMapField instance, you must invoke close(), which removes the field as a listener from specific classes and starts garbage collection.

Code sample: Adding a map by using the MapField class

MapField map = new MapField();
add(map);

Code sample: Adding a map by using the RichMapField class

RichMapField map = MapFactory.getInstance().generateRichMapField();
add(map);

Add a map to an app

The following code sample shows how to add a map to an app with the RichMapField class, and how to set the center and zoom level of the map. The resulting map is shown in the following image:


This screen shows a RichMapField with a specified center and zoom level.

Code sample: Adding a map to an app

import net.rim.device.api.lbs.maps.*;
import net.rim.device.api.lbs.maps.model.*;
import net.rim.device.api.lbs.maps.ui.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.container.*;

//Create the application framework by extending the UiApplication class. 
//In main(), create an instance of the new class and invoke 
//enterEventDispatcher() to enable the app to receive 
//events. In the application constructor, invoke pushScreen() to display 
//the custom screen for the app. The MapScreen class represents the custom screen.  
public class RichMapFieldDemo extends UiApplication
{
    public static void main(String[] args)
    {
        RichMapFieldDemo theApp = new RichMapFieldDemo();
        theApp.enterEventDispatcher();
    }
    public RichMapFieldDemo()
    {
        pushScreen(new MapScreen());
    }
}

//Create the framework for the custom screen by extending the 
//FullScreen class. In the constructor, invoke super() to create a default menu.  
class MapScreen extends FullScreen
{
    public MapScreen() 
    {
        super( FullScreen.DEFAULT_CLOSE | FullScreen.DEFAULT_MENU );

//In the screen constructor, invoke MapFactory.getInstance() to create 
//an instance of the MapFactory class and then invoke 
//generateRichMapField() to generate the RichMapField.  
        RichMapField map = MapFactory.getInstance().generateRichMapField();

//In the screen constructor, invoke getAction() to create an instance 
//of the MapAction class. Invoke setCentreAndZoom() to specify the center and 
//zoom level of the map. Invoke add() to add the field to the screen.  
        MapAction action = map.getAction();
        action.setCentreAndZoom(new MapPoint(43.47462, -80.53820), 2);   
        add(map);
    }
}

Specifying locations on a map

You can specify a location on a map by using the MapPoint class that is provided in the net.rim.device.api.lbs.maps.model package. MapPoint represents the geographical coordinates (latitude and longitude) for a location. You can use MapPoint to focus the map view to display a specific location (for example, you can set the center location for the map).

Your app can display a marker icon to identify a location on a map by using the MapLocation class. MapLocation is an extension of MapPoint. MapLocation represents the geographical coordinates (latitude and longitude), a label, and a description for the location and generates a marker icon (a red pushpin) for the location. You can use MapLocation to provide a point of interest on a map for a specified latitude and longitude (for example, a city). The map displays a marker icon and a label (for example, "Waterloo") for the location. When the user clicks the location, the description for the location (for example, "This is Waterloo") appears on a separate details screen.


This screen shows the elements described in the following code sample.

Code sample: Specifying a location by using the MapLocation class

MapLocation location = new MapLocation(43.46518, -80.52237, "Waterloo", "This is Waterloo");

Tagging and setting the visibility for locations on a map

You can assign tags to locations that are stored in a MapDataModel class. Each MapField class has an associated MapDataModel instance. The MapDataModel class that is provided in the net.rim.device.api.lbs.maps.model package, represents a container. You can add locations and associated data for the locations to the container by invoking MapDataModel.add(). Any item and its associated data are considered mappable items in the container.

You can group mappable items by assigning tags to the items (for example, all work locations have a "work" tag). You can invoke MapDataModel.add() or MapDataModel.tag()to tag mappable items in a MapDataModel container. The add() method allows you to add a mappable item to the container and it allows you to specify a tag for the item. The tag() method allows you to specify a tag for a single mappable item that is in the container. Multiple locations can have the same tag (for example, all RIM offices can be tagged "RIM"), and a single location can have multiple tags (for example, a residence can have tags for both "Sarah" and "Paul").

You can specify which tagged items that are stored in MapDataModel are visible or hidden on a map. By default, all items in MapDataModel are visible. For example, you can add the tag "park" to several locations and you can specify that only the locations with the tag "park" are displayed on the map. You can specify the items that are visible on the map by first invoking MapDataModel.setVisibleNone() to turn off the visibility for all items, and then invoking MapDataModel.setVisible() to turn on the visibility for the specified items.

Code sample: Tagging locations by using the MapDataModel.add() method

In the following code sample, three locations are defined, and then added and tagged by invoking the MapDataModel.add() method. Only the locations that have a "RIM" tag are visible on the map.

MapDataModel model = map.getModel();
MapLocation office01 = new MapLocation( 43.47550, -80.53900, "Head Office", null );
MapLocation office02 = new MapLocation( 43.48261, -80.54169, "Manufacturing", null );
MapLocation justinHome = new MapLocation( 43.47751, -80.54817, "Justin - Home", null);
model.add( (Mappable) office01, "RIM");
model.add( (Mappable) office02, "RIM");
model.add( (Mappable) justinHome, "home");
model.setVisibleNone();
model.setVisible( "RIM" );

Code sample: Tagging and setting the visibility for locations on a map

The following code sample creates a map, assigns tags to multiple locations, and displays only the locations that have a "work" tag.

import net.rim.device.api.ui.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.lbs.maps.*;
import net.rim.device.api.lbs.maps.model.*;
import net.rim.device.api.lbs.maps.ui.*;

//Create the application framework by extending the UiApplication class. 
//In main(), create an instance of the new class and invoke 
//enterEventDispatcher() to enable the app to receive events. 
//In the application constructor, invoke pushScreen() to display the custom 
//screen for the app. The MapTagScreen represents the custom screen. 
public class MapTaggingDemo extends UiApplication
{
    public static void main(String[] args)
    {
        MapTaggingDemo theApp = new MapTaggingDemo();
        theApp.enterEventDispatcher();
    }
    public MapTaggingDemo()
    {
        pushScreen(new MapTagScreen());
    }
}

//Create the framework for the custom screen by extending the FullScreen 
//class. In the constructor, invoke super() to create a default menu.  
class MapTagScreen extends FullScreen
{
    public MapTagScreen() 
    {
        super(FullScreen.DEFAULT_CLOSE | FullScreen.DEFAULT_MENU | 
                FullScreen.VERTICAL_SCROLL | FullScreen.VERTICAL_SCROLLBAR);

//In the screen constructor, invoke MapFactory.getInstance() to create 
//an instance of the MapFactory class, and then invoke generateRichMapField() 
//to generate the map field. Invoke add() to add the RichMapField 
//instance to the screen.             
        RichMapField map = MapFactory.getInstance().generateRichMapField();
        add(map);

//Invoke getModel() to create an instance of the MapDataModel class.         
        MapDataModel data = map.getModel();

//Create instances of the MapLocation class to define the locations. Pass the 
//latitude, longitude, label, and description of each location to 
//the MapLocation objects.              
        MapLocation julieHome = new MapLocation( 43.47751, -80.54817, 
                "Julie - Home", null );
        MapLocation headOffice = new MapLocation( 43.47550, -80.53900, 
                "Head Office", null );

//Create an integer identifier to represent a mappable item. Assign the 
//mappable item to the identifier by invoking add() to add a location 
//and pass one of the MapLocation objects and a tag for the location to 
//MapDataModel. You can use the identifier to access the item in MapDataModel 
//and assign another tag to a mappable item by invoking tag(), and passing as 
//arguments the identifier and the tag. In the following code sample, two locations 
//are added to MapDataModel, and each location is assigned two tags.         
        int julieHomeId = data.add( (Mappable) julieHome, "julie" );
        data.tag( julieHomeId, "home" );
        int headOfficeId = data.add( (Mappable) headOffice, "julie" );
        data.tag( headOfficeId, "work" );

//Define two more locations, and invoke add() to add the locations to MapDataModel. 
//Invoke tag() to assign the appropriate tags for the locations.         
        MapLocation paulHome = new MapLocation( 43.49487, -80.55335, 
                "Paul - Home", null );
        int paulHomeId = data.add( (Mappable) paulHome, "paul" );
        data.tag( paulHomeId, "home" );
        data.tag( headOfficeId, "paul" );
        
        data.tag( paulHomeId, "sarah" );
        MapLocation manufacturing = new MapLocation( 43.46514, -80.50506, 
                "Manufacturing", null );
        int manufacturingId = data.add( (Mappable) manufacturing, "sarah" );
        data.tag( manufacturingId, "work" );

//Turn on visibility for the locations that have the “work” tag. By 
//default, all the locations are visible on the map. Invoke setVisibleNone() 
//to turn the visibility off for all the locations. Invoke setVisible() and pass 
//the "work" tag as an argument to specify that only the locations with 
//the “work” tag are visible on the map.          
        data.setVisibleNone();
        data.setVisible( "work" );

//Invoke getMapField().update() to update the map view. Pass the Boolean value 
//true to the update method to recalculate the center and zoom level of 
//the map with the visible locations on the map. 
        map.getMapField().update( true );          
    }
}

Controlling the behavior of a map

The MapAction class contains the methods and constants that control and modify a map. For example, you can set the initial zoom level of a map, or prevent BlackBerry device users from changing the zoom level.

You can use the methods and constants in the MapAction class to add the following functionality to a map:

  • Initiate actions on a map.
  • Define the behavior for actions that users are allowed to perform on a map.
  • Identify and respond to the events that occur on a map.

Initiating actions on a map

The MapAction class provides a set of methods, each called set<Action>, that you can invoke to perform actions on a map (for example, setRotation). The set methods determine if the action is allowed, and if it is allowed, how the action is performed.

Before you can invoke one of these methods on a map, you must retrieve the MapAction object from the map by calling getAction(). For example, when the app opens and displays the map, you can invoke myMap.getAction().setCenterAndZoom(MapPoint center, int zoom) to set the center and the zoom level of the map.

MapField map = new MapField();
map.getAction().setCenterLatLon( 45.0000, -75.0000 );
map.getAction().setZoom( 0 );

Defining custom behaviors

Each set<Action> method has a corresponding perform<Action> and allow<Action> method. The perform methods customize the behavior that occurs when an action is performed. The allow methods define whether actions are allowed to occur. To use the perform and allow methods, you must extend your class with MapAction and override the appropriate methods.

The following code sample demonstrates how to override the performNavigateNext() and performNavigatePrev() to customize their behavior when a user presses the N (next) or P (previous) keys. The methods invoke super to ensure that the default actions for these methods are still invoked, in addition to the custom actions that are added. This code sample also demonstrates how to prevent the user from zooming by overriding allowSetZoom(int zoom) and returning false.

public class NewMapActions extends MapAction
{
    protected boolean performNavigateNext()
    {
        super.performNavigateNext();
        // Add custom behavior here.

        return true;	
    }
    		
    protected boolean performNavigatePrev()
    {
        super.performNavigatePrev();
        // Add custom behavior here.

        return true;	
    }
    		
    protected boolean allowSetZoom(int zoom)
    {
        return false;	
    }
}

Adding a custom MapAction object to a map

After you create a new MapAction object, you can invoke setAction() on your map to apply the customized MapAction object.

NewMapActions action = new NewMapActions();
map.getMapField().setAction(action);

Responding to changes in a MapField

If you want to identify and respond to the actions that the user performs in a map field, you can invoke addChangeListener() to register the map field as a listener, and then use the constants that are defined in the MapAction class. For example, MapAction.ACTION_ZOOM_CHANGE indicates that the zoom level was changed.

class MapFieldListener implements FieldChangeListener
{
    public MapFieldListener ()
    {
        MapField map = new MapField();
        map.addChangeListener(this);
    }

    public void fieldChanged( Field field, int actionId )
    {
        switch ( actionId )
        {
            case MapAction.ACTION_CENTRE_CHANGE:
                break;
            case MapAction.ACTION_ZOOM_CHANGE:
                break;
        }
    }
}

Updating and re-rendering objects on a map

You often need to update information about a mappable object and re-render the object on a map. For example, the name, description, or coordinates of a MapLocation object might change, or you might want to change the appearance of an item on a map in response to a BlackBerry device user's action. You might also want to display the location of a user's BlackBerry Messenger contact, and update the map as the location changes. By creating a class that implements the DynamicMappable interface, you can create mappable objects that are updated dynamically on a map when they change. In addition to the DynamicMappable class, the net.rim.device.api.lbs.maps.model package contains the following set of classes that allow you to create dynamic mappable objects.

Class or interface

Description

DynamicMappable

By implementing this interface on a class that you create, you can create mappable objects with changeable properties that can be updated dynamically on a map.

MappableEventManager

When you create a class that implements DynamicMappable, it must implement the getMappableEventManager method, which returns an object of this class. This class manages the events and listeners for dynamic mappable objects, and initiates updates to maps.

MappableEventListener

You can use this interface to listen for changes to a dynamic mappable object.

MappableChangeEvent

When you want to push updates to a map, you can create an instance of this class which captures the contents of a change event that has occurred with a dynamic mappable object.

To create a dynamic mappable object, you must create a class that implements the DynamicMappable interface. This class stores the old state and the new state for all information that can change. For example, if your app displays the prices of gas at various gas stations, your dynamic mappable class stores the old and new gas price.

Your dynamic mappable class must implement the getEventManager method. When you invoke getEventManager from your main application, the method returns a MappableEventManager object. After you add a dynamic mappable object to a map, the MappableEventManager object is the component that notifies the map when a change occurs, forcing the map to re-render the object. To initiate the update, you must create and trigger a MappableChangeEvent that contains the old state and the new state for the content that is changing.

Creating a class that implements DynamicMappable

The following code sample demonstrates how to create a class that can update the location of an object on a map. The class is called UpdatableMappable, and it extends the MapLocation class and implements the DynamicMappable. In the UpdatableMappable constructor, super() is called to initialize the object in the same way as a MapLocation object is initialized. The MappableEventManager property that is required to implement the DynamicMappable interface, is initialized. It is this instance of the MappableEventManager that enables the UpdatableMappable class to push updates about itself to the map that you add it to.

This class also overrides setLon() and setLat() . When you call one of these methods from your app, the method takes the existing latitude or longitude and stores it as an old value before the class invokes super to set the new value.

public class UpdatableMappable extends MapLocation implements DynamicMappable 
{    
    public MappableEventManager eventManager; 
    public double oldLat;
    public double oldLon;
    
    public UpdatableMappable(double lat, double lon, String name, String description) 
    {
        super(lat, lon, name, description);
        eventManager = new MappableEventManager();
    }
    
    public void setLon(final double lon) 
    {
        oldLon = getLon();
        super.setLon(lon);
    }
    
    public void setLat(final double lat) 
    {
        oldLat = getLat();
        super.setLat(lat);
    }
    
    public MappableEventManager getEventManager() 
    {
        return eventManager;
    }
}

Adding a dynamic mappable object to a map

The following code sample demonstrates how to create a RichMapField object and add a dynamic mappable object to the map's data model.

RichMapField map = MapFactory.getInstance().generateRichMapField();
map.getMapField().getAction().setCenter(new MapPoint( 45.0, -75.0));
add(map);
            
UpdatableMappable mappableObject = new UpdatableMappable(45.0, -75.0, 
        "Map Location", "Dynamic Updates"); 
    
MapDataModel model = map.getModel();
model.add(mappableObject, "dynamic", true);

Updating the dynamic mappable object

The following code sample demonstrates how to set the new values for the latitude and longitude of the dynamic mappable object and initiate an update to re-render the object on a map. You can set the new values for the latitude and longitude by calling the UpdatableMappable.setLat() and UpdatableMappable.setLon() . To push the updates to the map, you can call UpdatableMappable.getEventManager() and trigger a MappableChangeEvent, which contains both the new and old state of the object.

In this example, the old state is represented by a MapPoint that contains the old coordinates. The new state is a reference to an updated mappable object. In cases where dynamic mappable objects are frequently updated with new information (such as when you're tracking a location in near real-time), a dynamic mappable object might be overwritten with new values before the event manager can trigger an event. In these cases, you might want to create and send a copy of the object to ensure that all updates are re-rendered on the map.

The newLat and newLon variables represent the new coordinates for the dynamic mappable object, which are obtained through a separate operation (for example, a request that you send for a user's location, or a web service that pushes location information).

mappableObject.setLat(newLat);
mappableObject.setLon(newLon);

MappableChangeEvent event = new MappableChangeEvent();
event.setOldState(new MapPoint(mappableObject.oldLat, mappableObject.oldLon));
event.setNewState(mappableObject);
mappableObject.getEventManager().triggerEvent(event);

Code sample: Updating and re-rendering objects on a map

The following code sample illustrates how to create a dynamic mappable object, add the object to a map, and dynamically update the location of the object. In this code sample, the app generates new coordinates for the dynamic mappable object each time a user presses the button on the screen. Typically this data would be obtained by requesting a user's current location, or by obtaining location information from a web service.

/*
 * DynamicMappableApp.java
 */

import net.rim.device.api.lbs.maps.*;
import net.rim.device.api.lbs.maps.model.*;
import net.rim.device.api.lbs.maps.ui.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.system.*;
import java.util.Random;

public class DynamicMappableApp extends UiApplication 
{
    public static final void main( String[] args ) 
    {
        new DynamicMappableApp().enterEventDispatcher();
    }
    
    public DynamicMappableApp() 
    {
        pushScreen( new DynamicMappableScreen() );
    }
   
    private class DynamicMappableScreen extends FullScreen implements 
            FieldChangeListener
    {
        
        private RichMapField map;
        private MapDataModel model;
        private UpdatableMappable mappableObject;
        private ButtonField button;
         
        private DynamicMappableScreen() 
        {
            super(FullScreen.DEFAULT_CLOSE | FullScreen.DEFAULT_MENU);
            
            // Create the map.
            map = MapFactory.getInstance().generateRichMapField();
            map.getAction().enableOperationMode(MapConstants.MODE_SHARED_FOCUS); 
            map.getMapField().setDimensions(new MapDimensions(Display.getWidth(), 
                    Display.getHeight()-100));
            map.getMapField().getAction().setCenter(new MapPoint( 45.0, -75.0));
            map.getMapField().getAction().setZoom(4);
            add(map);
            
            // Create an instance of the dynamic mappable class and add it to
            // the data model for the map.
            mappableObject = new UpdatableMappable(45.0, -75.0, "Map Location", 
                    "Dynamic Updates");     
            model = map.getModel();
            model.add(mappableObject, "dynamic", true);
            
            // Create and add a button that initiates an update.
            button = new ButtonField("Update Location");
            button.setChangeListener(this);
            add(button);            
        }
        
        public void fieldChanged (Field field, int context)
        {
            // Create a random value between 0 and 0.03 in steps of 0.0001
            // to update the lat and lon with.
            Random randomizer = new Random();
            double dlat = randomizer.nextInt(300) / 10000.0;
            double dlon = randomizer.nextInt(300) / 10000.0;           
            mappableObject.setLat(44.985 + dlat);
            mappableObject.setLon(-74.985 + dlon);

            // Create and trigger a MappableChangeEvent that captures the old 
            // and new values.
            MappableChangeEvent event = new MappableChangeEvent();
            event.setOldState((Mappable) new MapPoint(mappableObject.oldLat, 
                    mappableObject.oldLon));
            event.setNewState(mappableObject);
            mappableObject.getEventManager().triggerEvent(event);
        }
    }      
}

/*
 * UpdatableMappable.java
 */

import net.rim.device.api.lbs.maps.model.*;

public class UpdatableMappable extends MapLocation implements DynamicMappable 
{   
    public MappableEventManager eventManager;
    
    // Stores the old latitude and longitude.
    public double oldLat;
    public double oldLon;
    
    public UpdatableMappable(double lat, double lon, String name, String description) 
    {
        super(lat, lon, name, description);
        eventManager = new MappableEventManager();
    }
    
    public void setLon(final double lon) 
    {
        // Record the old value before it's changed.
        oldLon = getLon();
        super.setLon(lon);
    }
    
    public void setLat(final double lat) 
    {
        // Record the old value before it's changed.
        oldLat = getLat();
        super.setLat(lat);
    }

    public MappableEventManager getEventManager() 
    {
        return eventManager;
    }
}