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.

Customizing the appearance of a map

Customizing the appearance of objects on a map

The net.rim.device.api.lbs.maps.view package contains the Style and StyleSet classes, that you can use to customize the appearance of any mappable objects that are displayed on a map. By using styles, you can customize your maps and give them a uniform appearance in a way that is analagous to applying CSS styles.

The Style class provides the following properties that define the visual characteristics of mappable objects.

  • Edge: Specifies properties for the edge around a mappable object (color, opacity, and size).
  • Fill: Specifies properties for the internal area of a mappable object (color and opacity).
  • Label: Specifies properties for the label of a mappable object (color and opacity of the fill, and the alignment, color, family name, opacity, size, and style of the font).

The Style class also contains constants for the edge size and text alignment (for example, EDGE_SIZE_LARGE, and FONT_ALIGNMENT_RIGHT). You can find constants related to the font and color in the Font and Color classes, respectively. Opacity is represented by an integer between 0 and 255, where 0 is transparent and 255 is opaque.

The StyleSet class represents a set of individual styles that a map uses to render mappable objects on a screen. Maps have a default style set that you can retrieve and override with your own styles.

A StyleSet object can include three different types of styles, a base style, a class style, and an ID style. The base style defines a style for all mappable objects. The class style defines a style for all mappable objects of a particular class. The ID style defines a style for all mappable objects of a particular ID. To assign an ID to a mappable object, you must invoke AbstractMappable.setStyleId() on the object.

You can also create styles that inherit properties from parent styles (for example, an ID style can inherit from a class or base style). To inherit a property from a parent style, you can specify one of the INHERIT constants (for example, COLOR_INHERIT or EDGE_SIZE_INHERIT ).

Creating a map

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

Retrieving the style set for the map

StyleSet styles = map.getMapField().getDrawingStyles();

Defining styles and applying them to a style set

// Base style for the entire map.
Style baseStyle = new Style();
baseStyle.setLabelFontFamilyName("arial");
styles.setBaseStyle( baseStyle );

// Style for the MapLocation class.
Style classStyle = new Style();
classStyle.setLabelFillColor( Color.BLACK );
classStyle.setLabelFontColor( Color.WHITE );
classStyle.setEdgeColor( Color.WHITE );
styles.addClassBasedStyle( MapLocation.class, classStyle );

// Style for a specific ID.
Style idStyle = new Style();
classStyle.setEdgeColor( Color.RED );
styles.addIdBasedStyle("ID_1", idStyle);

Creating a mappable object that uses an ID style

MapLocation locationTwo = new MapLocation( 4500100, -7499900, "Loc. 2", "Uses an ID style" );
locationTwo.setStyleId("ID_1");

Code sample: Customizing the appearance of mappable objects

The following code sample demonstrates how to define a base style, class style, and an ID style, and apply the styles to the style set for a map.

import net.rim.device.api.ui.*;
import net.rim.device.api.lbs.maps.MapConstants;
import net.rim.device.api.lbs.maps.MapFactory;
import net.rim.device.api.lbs.maps.model.MapDataModel;
import net.rim.device.api.lbs.maps.model.MapLocation;
import net.rim.device.api.lbs.maps.ui.RichMapField;
import net.rim.device.api.lbs.maps.view.Style;
import net.rim.device.api.lbs.maps.view.StyleSet;
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.container.FullScreen;

public class StyleSetApp extends UiApplication 
{
    public StyleSetApp() 
    {
        pushScreen(new StyleSetScreen());
    }
    
    public static void main(String[] args) 
    {
        new StyleSetApp().enterEventDispatcher();
    }
    
    public class StyleSetScreen extends FullScreen 
    {
        private RichMapField map;
        private MapDataModel model;
        private StyleSet styles;
        
        public StyleSetScreen() 
        {
            // Invoke the constructor for FullScreen to define the behavior of the
            // screen.
            super(FullScreen.DEFAULT_CLOSE | FullScreen.DEFAULT_MENU);
            
            // Create and add a map.
            map = MapFactory.getInstance().generateRichMapField();
            map.getAction().disableOperationMode( MapConstants.MODE_SHARED_FOCUS );
            map.getAction().setCenterLatLon( 44.9500, -75.0000 );
            map.getAction().setZoom( 4 );
            add(map);
            
            // Retrieve the data model and style set for the map.
            model = map.getModel();
            styles = map.getMapField().getDrawingStyles();
            
            // Define a base style for the entire map.
            Style baseStyle = new Style();
            baseStyle.setLabelFontFamilyName("arial");
            styles.setBaseStyle( baseStyle );

            // Define a style for the MapLocation class.
            Style classStyle = new Style();
            classStyle.setLabelFillColor( Color.BLACK );
            classStyle.setLabelFontColor( Color.WHITE );
            classStyle.setEdgeColor( Color.WHITE );
            styles.addClassBasedStyle( MapLocation.class, classStyle );

            // Define a style for an ID.
            Style idStyle = new Style();
            idStyle.setEdgeOpacity(15);
            idStyle.setFillOpacity(15);
            idStyle.setLabelFillOpacity(50);
            styles.addIdBasedStyle("ID_1", idStyle);
        
            // Create objects that demonstrate the MapLocation class style.
            MapLocation wes = new MapLocation( 45.0000, -75.0100, "Wes", 
                    "Displays a class style" );
            model.add( wes );
            
            MapLocation jenn = new MapLocation( 45.0000, -74.9900, "Jenn", 
                    "Displays a class style" );
            model.add( jenn );

            // Create objects that demonstrate the ID style.
            MapLocation mike = new MapLocation( 44.9900, -75.0100, "Mike", 
                    "Displays an ID style" );
            model.add( mike );
            mike.setStyleId( "ID_1" );
  
            MapLocation jamie = new MapLocation( 44.9900, -74.9900, "Jamie", 
                    "Displays an ID style" );
            model.add( jamie );
            jamie.setStyleId( "ID_1" );       
        }  
    }       
}

Adding fields on top of a map

You can use the RichMapField class to display a map, which can be part of a screen that includes one or more UI components. You can add fields that are not part of the map by invoking RichMapField.add(). The fields are overlays that are not directly rendered on the map.

When a RichMapField instance is in a container that contains other UI components (for example, in a dialog box), by default, RichMapField shares focus with the other UI components, which allows a BlackBerry device user to move from RichMapField to the other UI components on the screen. If you want to make RichMapField the exclusive field on the screen (for example, the map is the full screen), you must disable the shared focus by invoking disableOperationMode(MapConstants.MODE_SHARED_FOCUS).

You can use MapConstants.MODE_FOCUS_ACTIVE to specify that RichMapField has focus, and still shares focus with the other UI components. When RichMapField has focus, RichMapField actively consumes all input events, such as a user clicking the trackpad. Giving focus to RichMapField allows a user to pan and zoom the map without inadvertently exiting the map and moving to other components on the screen. When RichMapField does not have focus, RichMapField does not consume any input events and a user can pan the map and move focus to another field on the screen.

For more information about UI components, see the BlackBerry Java SDK UI Component Quick Reference Guide.

Code sample: Adding a field on top of a map

RichMapField map = MapFactory.getInstance().generateRichMapField();
ButtonField button = new ButtonField( "Click Here", Field.FOCUSABLE);
        
button.setChangeListener( new FieldChangeListener()
{
    public void fieldChanged( Field field, int context )
    {
        Dialog.alert( "Button clicked." );
    }
} );
map.add( button, 50, 50 );

Code sample: Setting the RichMapField as the exclusive field on the screen

RichMapField richMapField = MapFactory.getInstance().generateRichMapField();
MapField map = richMapField.getMapField();
map.getAction().disableOperationMode( MapConstants.MODE_SHARED_FOCUS );

Creating custom shapes and objects

Creating custom shapes and objects on top of maps is crucial to building integrated BlackBerry apps. For example, you could create an app that integrates with BlackBerry Messenger, and displays a BlackBerry device user's profile picture on a map when the user is near a location. You could also create an app and display a polygon that marks a perimeter for a geofenced area that your app is monitoring. Like any other mappable object, you can customize the appearance of your custom shapes and objects with styles.

The net.rim.device.api.lbs.maps.model package contains the following classes that correspond to basic shapes and objects that you can use as building blocks to create your own objects.

Class

Description

MapPoint

This class represents the coordinates for a location on a map. Sets of MapPoint objects are required for building lines, polylines, and polygons.

MapMarker

This class represents a marker point on the map, similar to a MapPoint object. You can use an image as a MapMarker object.

PeerPoint

This class represents a static location on a map, with accuracy. For example, you can use a PeerPoint object to mark a BlackBerry device user's last location with the accuracy in meters.

MapLine

This class represents a line segment on the map. To create a MapLine object, you must specify MapPoint objects for the start and the end of the line.

MapPolyLine

This class represents a continuous line that is composed of mulitple line segments between an ordered set of MapPoint objects.

MapSimplePolygon

This class represents a simple polygon that is defined by an ordered set of MapPoint objects. The last MapPoint object in the array joins the first object to close the polygon. Simple polygons render faster than complex polygons.

MapComplexPolygon

This class represents a polygon that is composed of an outer boundary (defined as an ordered series of MapPoints), and an inner boundary (zero or more MapSimplePolygons).

MapImage

This class represents an image for a location on a map. You can specify an image, a thumbnail, or a URI for the location. When you specify a thumbnail, the thumbnail displays directly on the map. When the user clicks on the thumbnail and requests more details, the larger image displays.

For some of the classes mentioned in the previous table, a set of corresponding shapes are available in the net.rim.device.api.lbs.maps.model.geospatial package. The difference between the shapes in the net.rim.device.api.lbs.maps.model.geospatial package (called with Gs<shape>) and the basic shapes (called Map<shape>) is that the geospatial shapes can be organized in a tree structure using the GsFolder and GsRoot classes. The geospatial objects can also be assigned names and descriptions.

Creating a MapSimplePolygon object

MapPoint[] points = new MapPoint[6];
points[0] = new MapPoint(45.05, -75.0);
points[1] = new MapPoint(45.0, -74.95);
points[2] = new MapPoint(44.95, -74.975);
points[3] = new MapPoint(44.95, -75.025);
points[4] = new MapPoint(45.0, -75.05);
points[5] = new MapPoint(45.05, -75.0);
MapSimplePolygon poly = new MapSimplePolygon(points);

Creating a GsImage object

GsImage gsImg = new GsImage();
img.setIconUri("http://www.rim.com/products/appworld_3col.jpg");
img.setLat(44.5);
img.setLon(-75.0);
img.setName("App World!");
img.setDescription("BlackBerry App World icon");

Code sample: Creating custom shapes and objects

The following code sample demonstrates how you can create a MapSimplePolygon object and a GsImage object. The MapSimplePolygon has a transparent style applied to it that allows BlackBerry device users to see the map behind the object.

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

public class CustomObjectsDemo extends UiApplication 
{
    public CustomObjectsDemo() 
    {
        pushScreen(new GSScreen());
    }
    
    public static final void main( String[] args ) 
    {
        new CustomObjectsDemo().enterEventDispatcher();
    }

    public class CustomObjectsDemoScreen extends FullScreen 
    {       
        private RichMapField map;

        public CustomObjectsDemoScreen() 
        {
            // Invokes the constructor for FullScreen to set the behavior
            // of the screen.
            super(FullScreen.DEFAULT_CLOSE | 
                    FullScreen.DEFAULT_MENU | 
                    FullScreen.VERTICAL_SCROLL | 
                    FullScreen.VERTICAL_SCROLLBAR);
            
            // Creates a map and sets the center of the map.
            map = MapFactory.getInstance().generateRichMapField();                     
            map.getAction().setCenterAndZoom(new MapPoint(45.0, -75.0), 4);
            add(map);
            
            // Adds the geospatial objects to the map.
            addData();
        }

        private void addData() 
        {
            // Creates and adds a MapSimplePolygon object.
            MapPoint[] points = new MapPoint[6];
            points[0] = new MapPoint(45.05, -75.0);
            points[1] = new MapPoint(45.0, -74.95);
            points[2] = new MapPoint(44.95, -74.975);
            points[3] = new MapPoint(44.95, -75.025);
            points[4] = new MapPoint(45.0, -75.05);
            points[5] = new MapPoint(45.05, -75.0);
            MapSimplePolygon poly = new MapSimplePolygon(points);
            map.getModel().add((Mappable) poly, "Polygon", true);
            
            // Creates and adds a GsImage object.
            GsImage gsImg = new GsImage();
            gsImg.setIconUri("
                    http://www.rim.com/products/appworld_3col.jpg");
            gsImg.setLat(44.5);
            gsImg.setLon(-75.0);
            gsImg.setName("App World!");
            gsImg.setDescription("BlackBerry App World icon");
            map.getModel().add(gsImg, "Image", true);
            
            // Creates a style for the MapSimplePolygon class that
            // allows the user to see the map behind the object.
            StyleSet styles = map.getMapField().getDrawingStyles();
            Style transStyle = new Style();
            transStyle.setEdgeColor(Color.RED);
            transStyle.setEdgeOpacity(255);
            transStyle.setFillColor(Color.RED);
            transStyle.setFillOpacity(50);
            styles.addClassBasedStyle(MapSimplePolygon.class, transStyle);                                  
        }     
    }
}

Creating a hierarchy of geospatial objects

Geospatial objects are a convenient way to either import a KML document into your app, or to group objects together for organizational purposes. If your app needs to convert information from a KML document into mappable objects, you can retain the hierarchy of the elements by using the GsFolder and GsRoot classes. However, if you want to add the objects from the hierarchy to a map, you must add the objects individually. If you attempt to add the root or a folder of a tree to a map's MapDataModel, the objects are not rendered on the map.

The net.rim.device.api.lbs.maps.model.geospatial package contains the following classes that are required for creating a hierarchy of mappable elements.

Class or interface

Description

GsRoot

Represents the top-level container for a hierarchy of GsElement objects and StyleSet objects that are used to render the content.

GsFolder

Represents a folder where you can store GsElement objects, as well as GsRoot objects that contain groups of GsElement objects.

GsElement

Defines the interface that is implemented by all geospatial classes.

GsElementEnumeration

Allows you to enumerate through a collection of GsElements objects.

Organizing geospatial objects in a folder

The following code sample demonstrates how to create a hierarchy of geospatial objects. The sample has two different GsRoot objects, each of which contains an array of geospatial objects. The GsRoot objects are contained within a folder.

GsPoint[] gsPoints = new GsPoint[3];
gsPoints[0] = new GsPoint(45.05, -75.0, "Point 1", "Description.");
gsPoints[1] = new GsPoint(45.0, -74.95, "Point 2", "Description.");
gsPoints[2] = new GsPoint(44.95, -74.975, "Point 3", "Description.");

GsRoot pointRoot = new GsRoot("Root 1", "This root contains points.", 
        gsPoints);
            
GsMarker[] gsMarkers = new GsMarker[3];
gsMarkers[0] = new GsMarker(44.95, -75.025, "Marker 1", "Description.", 
        "http://www.rim.com/products/appworld_3col.jpg");
gsMarkers[1] = new GsMarker(45.0, -75.05, "Marker 2", "Description.", 
        "http://www.rim.com/products/appworld_3col.jpg");
gsMarkers[2] = new GsMarker(45.05, -75.0, "Marker 3", "Description.", 
        "http://www.rim.com/products/appworld_3col.jpg");
            
GsRoot markerRoot = new GsRoot("Root 2", "This root contains markers", 
        gsMarkers);
            
GsFolder folder = new GsFolder("Folder", "The folder holds the root elements.");
folder.addElement(pointRoot);
folder.addElement(markerRoot);

Displaying KML overlays on a map

KML documents are XML-based documents that you can use to store geographic data about places, buildings, points of interest, cycle paths, pictures, and so on. You can create and publish a KML document to a web site. You can display the KML data in a map field by specifying the URL of the KML document in the MapFactory.populateDataModelFromKmlUrl(MapDataModel model, String url, String tag) method.

Your app can display KML data in a map field only if the KML document is retrieved through the BlackBerry Internet Service or the BlackBerry Enterprise Server (for example, a KML document that is stored on a web site or on an intranet site). A KML document that is stored on a BlackBerry device (for example, on a media card) cannot be displayed.

Code sample: Displaying a KML overlay

String officeTag = "RIM offices";
String officeUrl = "http://www.example.com/rim_offices.kml";

//create the map field
RichMapField view = MapFactory.getInstance().generateRichMapField();

// retrieve the KML document and populate the data model
MapDataModel model = view.getModel();
MapFactory.getInstance().populateDataModelFromKmlUrl( model, officeUrl, officeTag );

// display only the RIM offices and center the view on the visible locations
model.setVisibleNone();
model.setVisible( officeTag );
view.getMapField().update( true );

Creating a static image of a map

You can create a static image of a map by invoking MapField.getImage() , which is provided in the net.rim.device.api.lbs.maps.ui package, or MapFactory.generateStaticImage() , which is provided in the net.rim.device.api.lbs.maps package.

You can invoke MapField.getImage() to capture an image of the current map view on the screen, including any data that is visible on the map. MapField.getImage() is used to capture images in UI apps, where the map is a field on the screen. For example, in an app that displays a map of points of interest, you can provide a button that BlackBerry device users can click, to save an image of the current map.

You can invoke MapFactory.generateStaticImage() to create an image of a map in the following situations:
  • To create an image of the current map view on the screen in a non-UI app (for example, in an app that sends periodic updates of a person’s location to an email address)
  • To create an image of a map that does not allow user interaction such as panning and zooming (for example, in a contacts application to provide an image of a map for the contact’s home address)

The generateStaticImage() methods in the MapFactory class provide control over the coordinates for the center and the zoom level of the image, and calculate the coordinates based on the mappable items that are provided.

Method

Description

generateStaticMapImage(MapDimensions mapProperties, MappableVector data)

This method uses a MappableVector class and provides the app with control over the coordinates for the center and the zoom level of the map image.

generateStaticMapImage(MapDimensions mapProperties, MapDataModel data)

This method uses a MapDataModel class and provides the app with control over the coordinates for the center and the zoom level of the map image.

generateStaticMapImage(XYDimension imageSize, MappableVector data)

This method calculates the center and zoom level of the image based on the mappable data in the MappableVector.

Code sample: Creating a static image of a map in a UI app

// Add the data to a collection.
MapDataModel data = new MapDataModel();
data.add( (Mappable) new MapLocation( 43.47550, -80.53900, "Andrew", null ) );
data.add( (Mappable) new MapLocation( 43.48261, -80.54169, "Blake", null ) );
data.add( (Mappable) new MapLocation( 43.47751, -80.54817, "Christine", null ) );

// Create the map and specify the map size.
MapField map = new MapField(data, 200, 200);

// Create the image.
Bitmap image = map.getImage();

Code sample: Creating a static image of a map (map center and zoom level are calculated)

// Add the data to a collection.
MappableVector data = new MappableVector();
data.addElement( new MapLocation( 43.47550, -80.53900, "Andrew", null ) );
data.addElement( new MapLocation( 43.48261, -80.54169, "Blake", null ) );
data.addElement( new MapLocation( 43.47751, -80.54817, "Christine", null ) );

// Specify the size of the resulting image.
XYDimension imageSize = new XYDimension( 200, 100 );
        
// Create the image.
Bitmap map = MapFactory.getInstance().generateStaticMapImage( imageSize, data );

Code sample: Creating a static image of a map (map center and zoom level are specified)

// Add the data to a collection.
MapDataModel data = new MapDataModel();
MapLocation andrew = new MapLocation(43.47550, -80.53900, "Andrew", null );
data.add( (Mappable) andrew );
data.add( (Mappable) new MapLocation( 43.48261, -80.54169, "Blake", null ) );
data.add( (Mappable) new MapLocation( 43.47751, -80.54817, "Christine", null ) );
// visibility for this location is false and it will not display on the map
data.add( (Mappable) new MapLocation( 43.49487, -80.55335, "Dustin", null), null, false );
        
// Specify the image size, center and zoom level.
MapDimensions dim = new MapDimensions( 200, 100 );
dim.setCentre( andrew );
dim.setZoom( 3 );
        
// Create the image.
Bitmap map = MapFactory.getInstance().generateStaticMapImage( dim, data );