×
Namespaces

Variants
Actions
(Difference between revisions)

How to display an Address or a Point of Interest on a Map with Java ME

From Nokia Developer Wiki
Jump to: navigation, search
skalogir (Talk | contribs)
(Skalogir -)
 
Oskar Bukolt (Talk | contribs)
m (Oskar Bukolt - Update Keywords + API Link + java version + minor text alteration)
(32 intermediate revisions by 3 users not shown)
Line 1: Line 1:
[[Category:Draft]]
+
[[Category:Java ME]][[Category:Code Examples]][[Category:Symbian]][[Category:Series 40]][[Category:Nokia Maps]]
This article describes how to search for an address or a point of interest and display it on the Map. It uses [http://projects.developer.nokia.com/LBSPJME/wiki Nokia's Map API for Java ME] for the translation of the search string into valid coordinates and for the retrieval of the relevant map image.  
+
{{Abstract|This article describes how to search for an address or a point of interest and display it on the Map. It uses the [http://www.developer.nokia.com/Develop/Maps/Maps_API_for_Java_ME/ Map API for Java ME] for the translation of the search string into valid coordinates and for the retrieval of the relevant map image.}}
  
 
[[Image:sydneypoi.png]][[Image:sydneyshow.png]]
 
[[Image:sydneypoi.png]][[Image:sydneyshow.png]]
  
 
==Introduction==
 
==Introduction==
The example described in this article includes the following features:
+
As of January 2012, Nokia Maps API for Java ME is provided as a separate library. It uses the device's network connection in order to download and display map data. The library can be downloaded from [http://www.developer.nokia.com/Develop/Maps/Maps_API_for_Java_ME/ here]. It needs to be added to the working project, before the code in this example can be compiled.
  
* Retrieval of the geographic coordinates for a given address or a place
+
The Nokis Maps API for Java ME uses the concept of tiles. The screen is divided into several tiles. When scrolling the map, if the new screen contains tiles that have already been downloaded, these will be used and only the new tiles will be requested via the network. This reduces the amount of generated data traffic and makes the interaction with the user faster. In some platforms, such as Series 40 6th Edition, the default security access level for the underlying network operations by default is set to "Always ask", meaning that many prompts will be generated each time the map needs to be updated. Information on how to change the security access after the application has been installed, and also how to change the default security level via signing the application can be found here: [[Java_Security_Domains]]
* Retrieval of static images with given custom size, format and zoom level
+
* Zooming in and out functionality
+
  
<!-- The line bellow has been commented out because the demo throws an Exception after selecting "Search"-->
+
==Interaction modes for different types of devices==
<!-- To see a live sample of this API, you can check here: [http://www.jappit.com/blog/j2me-online-emulator?midlet=google_maps Java ME Google Maps API sample MIDlet] -->
+
  
==Terms of Service==
+
The device requirements for running this example are MIDP2.0 and CLDC 1.1. This covers a very large group of devices from Symbian to Series 40 and from non-touch to Full Touch and Touch and Type. [http://www.developer.nokia.com/Develop/Maps/Maps_API_for_Java_ME/ Nokia's Map API for Java ME] masks away the differences between various devices and provides different interaction modes for each device, without the developer having to worry about the details of the implementation. With respect to zooming, the following interaction modes are supported:
{{Note|Google's Static Maps API no longer requires a Google Maps API key. However if you are not a Google Maps API Premier user, you should not use the code on this page outside a web-based application according to the Google Maps API Terms of Service [http://code.google.com/apis/maps/terms.html Terms and Conditions] (section 10.1.1(h)), unless
+
  
(i) your platform does not have a web browser or
+
* On non-touch devices, the zoom functionality is provided by the star (*) and hash (#) keys for zooming in and out respectively. No zoom controls are displayed on the screen.
<br />
+
* On full-touch devices, the zoom functionality is provided by the zoom controls displayed on the screen.
(ii) your platform does have a web browser, but you provide a link that shows the location concerned either in the Google Maps native application (if your platform has one) or on the Google Maps website.
+
* On Touch and Type and full-touch with keyboard devices, the zoom functionality is provided by both the star (*) and hash (#) keys for zooming in and out respectively and the zoom controls displayed on the screen.
 +
  
Also for non Premier users there is a limitation of 1000 unique (different) image requests per viewer per day.}}
+
With respect to panning (i.e. scrolling or moving the screen around the target location), the following interaction modes are supported:
  
==Disabling the On Screen Keypad==
+
* On non-touch devices, the navigation keypad is used to move the center of the map up, down, left and right from its current position
 +
* On full-touch and Touch and Type devices, panning is touch driven.
 +
* On full-touch devices with keyboard, such as Nokia E7-00, panning is also supported by the keyboard's arrow keys.
  
In order to take advantage of the entire screen, the following line should be added to the application's file descriptor (jad file):
 
  
{| class="wikitable"
+
{{Warning| On S60 5th Edition devices, this application requires that the On Screen Keypad (OSK) is explicitly defined as disabled. In order to disable OSK and use the entire screen for displaying the Map, the following line should be added to the application's file descriptor (jad file):
|-
+
'''Nokia-MIDlet-On-Screen-Keypad: no'''
| '''Nokia-MIDlet-On-Screen-Keypad: no'''
+
}}
|}
+
  
== Comparison of Static Maps Rendering Services ==
+
==The search screen==
 +
The search screen consists of a {{Icode|Form}}, that contains the {{Icode|TextField}} where the user can type the requested address or point of interest, a hidden info message item that is only used for the display of any potential errors and two {{Icode|Commands}}, an exit and a search {{Icode|Command}}.
 +
<code java>
 +
//Main Search Screen
 +
display=Display.getDisplay(this);
 +
mainform=new Form("Address or POI");
 +
mainform.append(text);
 +
mainform.append(infomessage);
 +
mainform.addCommand(searchCmd);
 +
mainform.addCommand(exitCmd);
 +
mainform.setCommandListener(this);
 +
display.setCurrent(mainform);
 +
</code>
  
There are several consideration when using a Static Maps Rendering Service, including how detailed the map coverage is for the location in question, how large the generated image will be, and therefore how much data traffic and delay will be generated when contacting the Service and downloading the image. The balance between keeping the image's quality high while maintaining the image's size low is also something to be considered.
+
The search {{Icode|Command}} triggers the connection to the Nokia Maps servers. An additional control has been added, so that no operation takes place if the user hasn't typed any text, after selecting the Search option:
  
The table below compares three different Maps Rendering Services:
+
<code java>
 +
if(c==searchCmd)
 +
{
 +
String search=text.getString();
 +
infomessage.setText("");
 +
if(!search.equals(""))
 +
{
 +
showOnMap(search);
 +
}
 +
}
 +
</code>
  
{| class="wikitable"
+
==The map screen==
|-
+
! Google's Static Maps API !!  Nokia's Map Image API !! Open Street Maps
+
|-
+
|  [[File:Googlesgreenwich.jpg‎]] ||  [[File:greenwich20.jpg]]|| [[File:Openstreetmap_greenwich.png‎]]
+
|-
+
| file size = 8 KB  ||  file size = 4KB|| file size: 47 KB
+
|}
+
  
{{Note|If you want to do anything more than just display a '''single  map image''' (e.g adding panning to your app), you will be better off using  an integrated "dynamic" mapping Java ME library such as
+
It is quite easy to create a scrollable, dynamic map with zoom functionality with Nokia Maps API for Java ME. We can simply instantiate a new {{Icode|MapCanvas}} object and set it as current:
[http://projects.developer.nokia.com/LBSPJME/wiki Nokia's Map API for Java ME], at the cost of including an extra library jar file (approx 151 KB) as part of your
+
app download. There is a significant reduction in required network traffic within the first couple of 200x200 pixel images due to the use of map tiling and caching.}}
+
  
==Source code: GoogleMaps class==
 
 
<code java>
 
<code java>
import java.io.ByteArrayOutputStream;
+
mapcanvas = new MapCanvas(display);
import java.io.DataOutputStream;
+
                         display.setCurrent(mapcanvas);
import java.io.IOException;
+
import java.io.InputStream;
+
import java.util.Vector;
+
import javax.microedition.io.Connector;
+
import javax.microedition.io.HttpConnection;
+
import javax.microedition.lcdui.Image;
+
+
public class GoogleMaps {
+
    private static final String URL_UNRESERVED =
+
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + 
+
        "abcdefghijklmnopqrstuvwxyz" +
+
        "0123456789-_.~";
+
    private static final char[] HEX = "0123456789ABCDEF".toCharArray();
+
+
    // these 2 properties will be used with map scrolling methods. You can remove them if not needed
+
    public static final int offset = 268435456;
+
    public static final double radius = offset / Math.PI;
+
+
    public GoogleMaps() { 
+
    }
+
+
    public double[] geocodeAddress(String address) throws Exception {
+
        byte[] res = loadHttpFile(getGeocodeUrl(address));
+
        String[] data = split(new String(res), ',');  
+
+
        if (!data[0].equals("200")) {
+
            int errorCode = Integer.parseInt(data[0]);
+
            throw new Exception("Google Maps Exception: " + getGeocodeError(errorCode));
+
        }
+
+
        return new double[] {
+
                Double.parseDouble(data[2]), Double.parseDouble(data[3])
+
        };
+
    }
+
+
    public Image retrieveStaticImage(int width, int height, double lat, double lng, int zoom,
+
            String format) throws IOException {
+
        byte[] imageData = loadHttpFile(getMapUrl(width, height, lng, lat, zoom, format));
+
+
        return Image.createImage(imageData, 0, imageData.length);
+
    }
+
+
    private static String getGeocodeError(int errorCode) {
+
        switch (errorCode) {
+
        case 400:
+
            return "Bad request";
+
        case 500:
+
            return "Server error";
+
        case 601:
+
            return "Missing query";
+
        case 602:
+
            return "Unknown address";
+
        case 603:
+
            return "Unavailable address";
+
        case 604:
+
            return "Unknown directions";
+
        case 620:
+
            return "Too many queries";
+
        default:
+
            return "Generic error";
+
        }
+
    }
+
+
    private String getGeocodeUrl(String address) {
+
        return "http://maps.google.com/maps/geo?q=" + urlEncode(address) + "&output=csv";
+
    }
+
+
    private String getMapUrl(int width, int height, double lng, double lat, int zoom, String format) {
+
        return "http://maps.google.com/staticmap?center=" + lat + "," + lng + "&format="
+
                + format + "&zoom=" + zoom + "&size=" + width + "x" + height ;
+
    }
+
+
    private static String urlEncode(String str) {
+
        StringBuffer buf = new StringBuffer();
+
        byte[] bytes = null;
+
        try {
+
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
            DataOutputStream dos = new DataOutputStream(bos);
+
            dos.writeUTF(str);
+
            bytes = bos.toByteArray();
+
        } catch (IOException e) {
+
            // ignore
+
        }
+
        for (int i = 2; i < bytes.length; i++) {
+
            byte b = bytes[i];
+
            if (URL_UNRESERVED.indexOf(b) >= 0) {
+
                buf.append((char) b);
+
            } else {
+
                buf.append('%').append(HEX[(b >> 4) & 0x0f]).append(HEX[b & 0x0f]);
+
            }
+
        }
+
        return buf.toString();
+
    }
+
+
    private static byte[] loadHttpFile(String url) throws IOException {
+
        byte[] byteBuffer;
+
+
        HttpConnection hc = (HttpConnection) Connector.open(url);
+
        try {
+
            hc.setRequestMethod(HttpConnection.GET);
+
            InputStream is = hc.openInputStream();
+
            try {
+
                int len = (int) hc.getLength();
+
                if (len > 0) {
+
                    byteBuffer = new byte[len];
+
                    int done = 0;
+
                    while (done < len) {
+
                         done += is.read(byteBuffer, done, len - done);  
+
                    }
+
                } else {
+
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
                    byte[] buffer = new byte[512];
+
                    int count;
+
                    while ( (count = is.read(buffer)) >= 0 ) {
+
                        bos.write(buffer, 0, count);
+
                    }
+
                    byteBuffer = bos.toByteArray();
+
                }
+
            } finally {
+
                is.close();
+
            }
+
        } finally {
+
            hc.close();
+
        }
+
+
        return byteBuffer;
+
    }
+
+
    private static String[] split(String s, int chr) {
+
        Vector res = new Vector();
+
+
        int curr;
+
        int prev = 0;
+
+
        while ( (curr = s.indexOf(chr, prev)) >= 0 ) {
+
            res.addElement(s.substring(prev, curr));
+
            prev = curr + 1;
+
        }
+
        res.addElement(s.substring(prev));
+
+
        String[] splitted = new String[res.size()];
+
        res.copyInto(splitted);
+
+
        return splitted;
+
    }
+
}
+
 
</code>
 
</code>
  
==Addition of a map scrolling functionality==
+
The next problem, is to '''geolocate''' the search string and instantiate the {{Icode|geo}} object, which is the {{Icode|GeoCoordinate}} of the target location. We can also create a standard balloon marker and attach it to that location. Note that a list of results is retrieved from Nokia's servers as an array of {{Icode|Location}} objects. In this example, we use by default the first available result.
  
This example can be extended to include scrolling, i.e. the ability to move around the current map point, by catching the user's input and by retrieving new images. For that, the new center for the static image should be calculated. The following '''adjust()''' method returns the latitude and longitude values for the new map's center. The method requires the following arguments:
+
<code java>
 +
//The search Manager for this search
 +
GeocodeRequest geocodeRequest = searchFactory.createGeocodeRequest();
 +
//Retrieves the Point of Interest
 +
geocodeRequest.geocode(poi, null, this); // this refers to a GeocodeRequestListener
 +
</code>
 +
The search has been made '''asynchronously''', therefore the {{Icode|GeocodeRequestListener}} interface must be implemented,  and the app will be  informed once the results are supplied.
 +
<code java>
 +
public void onRequestComplete(GeocodeRequest request, Location[] result) {
  
* the current center's '''latitude''' and '''longitude''' coordinates
+
        //Stores the results in an array of Location objects
* the '''deltaX''' and '''deltaY''' values, which represent the deviation in pixels, of the new map center from the current one
+
        locationresults = result;
* the map's '''zoom''' level
+
        //The first result is taken
<br/>
+
        geo = locationresults[0].getDisplayPosition();
Original code, in JavaScript, can also be found [http://home.provide.net/~bratliff/adjust.js here]<br/><br/>
+
        //Centers the Map at the Point of Interest
{{Note| In order to use the methods described below, the '''MicroFloat''' library should be included in the project. The library is available here: [http://www.dclausen.net/projects/microfloat/ MicroFloat website]}}
+
        map.setCenter(geo);
 +
        //Displays the marker
 +
        marker = getMapFactory().createStandardMarker(geo);
 +
        map.addMapObject(marker);
 +
        //Sets the zoom level
 +
        map.setZoomLevel(14, 0, 0);
 +
        //Displays the Map on the Screen
 +
     
 +
        addCommand(exitCmd);
 +
        addCommand(backCmd);
 +
        setCommandListener(this);
 +
        display.setCurrent(this);
  
{{Note|Instead of coding the scrolling functionality, as an alternative solution one could use '''Nokia's Maps API for Java ME''', where this is provided by default as a MapCanvas. The API is provided as external library and it should therefore be included in the project. The library is available [http://projects.developer.nokia.com/LBSPJME/wiki here].}}
+
    }
  
<code java>
+
public void onRequestError(GeocodeRequest request, Throwable error) {
public double[] adjust(double lat, double lng, int deltaX, int deltaY, int z)
+
... etc ...
{
+
return new double[]{
+
XToL(LToX(lng) + (deltaX<<(21-z))),
+
YToL(LToY(lat) + (deltaY<<(21-z)))
+
};
+
}
+
double LToX(double x)
+
{
+
return round(offset + radius * x * Math.PI / 180);
+
 
}
 
}
 +
</code>
  
double LToY(double y)
+
If we need on top of that, our {{Icode|MapDisplay}} to be displayed with a specific zoom level and to be centered around a given coordinate point, we can write the following:
{
+
<code java>
return round(
+
offset - radius *
+
Double.longBitsToDouble(MicroDouble.log(
+
Double.doubleToLongBits(
+
(1 + Math.sin(y * Math.PI / 180))
+
/
+
(1 - Math.sin(y * Math.PI / 180))
+
)
+
)) / 2);
+
}
+
  
double XToL(double x)
+
//Centers the Map at the Point of Interest
{
+
mapdisplay.setCenter(geo);
return ((round(x) - offset) / radius) * 180 / Math.PI;
+
//Sets the zoom level
}
+
        mapdisplay.setZoomLevel(14,0,0);
  
double YToL(double y)
 
{
 
return (Math.PI / 2 - 2 * Double.longBitsToDouble(
 
MicroDouble.atan(
 
MicroDouble.exp(Double.doubleToLongBits((round(y)-offset)/radius))
 
)
 
)) * 180 / Math.PI;
 
}
 
double round(double num)
 
{
 
double floor = Math.floor(num);
 
 
if(num - floor >= 0.5)
 
return Math.ceil(num);
 
else
 
return floor;
 
}
 
 
</code>
 
</code>
  
==Description of example's main calls==
+
Finally, we need to set the {{Icode|MapCanvas}} we have just created as current:
  
The GoogleMaps class, contains the methods for geocoding addresses and map image retrievals. It can be instantiated as follows:
 
 
<code java>
 
<code java>
GoogleMaps gMap = new GoogleMaps();
+
        display.setCurrent(this);
</code>
+
  
In order to geocode an address or a point of interest, the geocodeAddress() method of the GoogleMaps class should be called as follows:
 
<code java>
 
double[] lanLng = gMap.geocodeAddress("Eiffel tower");
 
 
</code>
 
</code>
  
In order to retrieve a map image that corresponds to a given point, we have to specify the image's width and height, the point's latitude and longitude values, as well as the zoom level and the image type:
+
==The MIDlet source==
 
<code java>
 
<code java>
Image map = gMap.retrieveStaticImage(width, height, lanLng[0], lanLng[1], 14, "png32");
+
 
 +
import com.nokia.maps.common.ApplicationContext;
 +
import javax.microedition.lcdui.Display;
 +
import javax.microedition.midlet.MIDlet;
 +
import javax.microedition.midlet.MIDletStateChangeException;
 +
 
 +
public class ShowPOIonMapMidlet extends MIDlet {
 +
 
 +
    protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {}
 +
 
 +
    protected void pauseApp() {}
 +
 
 +
    protected void startApp() throws MIDletStateChangeException {
 +
 
 +
        // Please initialise the appId and token first
 +
        ApplicationContext.getInstance().setAppID("...");
 +
        ApplicationContext.getInstance().setToken("...");
 +
 
 +
        Display display = Display.getDisplay(this);
 +
        ShowPOIonMap md = new ShowPOIonMap(display, this);
 +
    }
 +
}
 
</code>
 
</code>
 +
<code java>
 +
import com.nokia.maps.common.GeoCoordinate;
 +
import com.nokia.maps.common.Location;
 +
import com.nokia.maps.map.MapCanvas;
 +
import com.nokia.maps.map.MapStandardMarker;
  
Note that the zoom level can be any value from 0 to 21.
+
import com.nokia.maps.search.GeocodeRequest;
 +
import com.nokia.maps.search.GeocodeRequestListener;
 +
import com.nokia.maps.search.SearchFactory;
 +
import javax.microedition.lcdui.Alert;
 +
import javax.microedition.lcdui.AlertType;
 +
import javax.microedition.lcdui.Command;
 +
import javax.microedition.lcdui.CommandListener;
 +
import javax.microedition.lcdui.Display;
 +
import javax.microedition.lcdui.Displayable;
 +
import javax.microedition.lcdui.Form;
 +
import javax.microedition.lcdui.StringItem;
 +
import javax.microedition.lcdui.TextField;
 +
import javax.microedition.midlet.MIDlet;
  
==Resources==
+
public class ShowPOIonMap extends MapCanvas implements CommandListener, GeocodeRequestListener {
  
The example's source code can be downloaded from here: [[File:GoogleMapsMIDletSource.zip]]
+
    MIDlet midlet;
 +
    Form mainform;
  
The example's installation binary files can be downloaded from here: [[File:GoogleMapsMIDletBinaries.zip]]
+
    GeoCoordinate geo;
 +
    TextField text = new TextField("Type Address or POI", "", 100, TextField.ANY);
 +
    StringItem infomessage = new StringItem("", "");
 +
    Command exitCmd = new Command("Exit", Command.EXIT, 0);
 +
    Command backCmd = new Command("Back", Command.BACK, 0);
 +
    Command searchCmd = new Command("Search", Command.OK, 1);
 +
    Location[] locationresults;
 +
    MapStandardMarker marker;
 +
    private SearchFactory searchFactory = SearchFactory.getInstance();
 +
 
 +
    public ShowPOIonMap(Display display, MIDlet midlet) {
 +
        super(display);
 +
        this.midlet = midlet;
 +
        //Main Search Screen
 +
 
 +
        mainform = new Form("Address or POI");
 +
        mainform.append(text);
 +
        mainform.append(infomessage);
 +
        mainform.addCommand(searchCmd);
 +
        mainform.addCommand(exitCmd);
 +
        mainform.setCommandListener(this);
 +
        display.setCurrent(mainform);
 +
    }
 +
 
 +
    public void onRequestComplete(GeocodeRequest request, Location[] result) {
 +
 
 +
        //Stores the results in an array of Location objects
 +
        locationresults = result;
 +
        //The first result is taken
 +
        geo = locationresults[0].getDisplayPosition();
 +
        //Centers the Map at the Point of Interest
 +
        map.setCenter(geo);
 +
        //Displays the marker
 +
        marker = getMapFactory().createStandardMarker(geo);
 +
        map.addMapObject(marker);
 +
        //Sets the zoom level
 +
        map.setZoomLevel(14, 0, 0);
 +
        //Displays the Map on the Screen
 +
     
 +
        addCommand(exitCmd);
 +
        addCommand(backCmd);
 +
        setCommandListener(this);
 +
        display.setCurrent(this);
 +
 
 +
    }
 +
 
 +
    public void onRequestError(GeocodeRequest request, Throwable error) {
 +
        locationresults = null;
 +
        setTitle("");
 +
        Alert a = new Alert("ERROR", error.toString(), null, AlertType.INFO);
 +
 
 +
        a.setTimeout(Alert.FOREVER);
 +
        display.setCurrent(a, this);
 +
 
 +
    }
 +
 
 +
    public void showOnMap(String poi) {
 +
        try {
 +
            //The search Manager for this search
 +
            GeocodeRequest geocodeRequest = searchFactory.createGeocodeRequest();
 +
            //Retrieves the Point of Interest
 +
            geocodeRequest.geocode(poi, null, this);
 +
            setTitle(poi);
 +
 
 +
        } catch (RuntimeException e) {
 +
            infomessage.setText("Error:" + e.getMessage());
 +
        }
 +
    }
 +
 
 +
    public void commandAction(Command c, Displayable d) {
 +
        if (c == searchCmd) {
 +
            String search = text.getString();
 +
            infomessage.setText("");
 +
            if (!search.equals("")) {
 +
                showOnMap(search);
 +
            }
 +
        }
 +
        if (c == exitCmd) {
 +
            midlet.notifyDestroyed();
 +
        }
 +
        if (c == backCmd) {
 +
            display.setCurrent(mainform);
 +
        }
 +
    }
 +
 
 +
    public void onMapUpdateError(String description, Throwable detail, boolean critical) {
 +
    }
 +
 
 +
    public void onMapContentComplete() {
 +
    }
 +
}
 +
</code>
 +
 
 +
==Resources==
 +
 
 +
* The example's source code can be downloaded from here: [[File:NokiaMapsShowPOIonMapSource.zip]]
 +
* The example's installation binary files can be downloaded from here: [[File:NokiaMapsShowPOIonMapBinaries.zip]]
  
 
==See also==
 
==See also==
* [http://projects.developer.nokia.com/LBSPJME Nokia's Maps API for Java ME]
+
* [http://www.developer.nokia.com/Develop/Maps/Maps_API_for_Java_ME/ Map API for Java ME]
 
* [[Using a JSR-179 Location Provider and displaying the result on a Map]]
 
* [[Using a JSR-179 Location Provider and displaying the result on a Map]]
 
* [[How to Create a Custom Map View|How to Create a Custom Map View with Nokia's Map API for Java ME]]
 
* [[How to Create a Custom Map View|How to Create a Custom Map View with Nokia's Map API for Java ME]]
 
{{SeeAlso|
 
{{SeeAlso|
* [http://projects.developer.nokia.com/LBSPJME Nokia's Maps API for Java ME]
+
* [http://www.developer.nokia.com/Develop/Maps/Maps_API_for_Java_ME/ Map API for Java ME]
 +
* [http://www.developer.nokia.com/Develop/Maps/Maps_API_for_Java_ME/Code_examples/#search Search Demo]
 
* [[Using a JSR-179 Location Provider and displaying the result on a Map]]
 
* [[Using a JSR-179 Location Provider and displaying the result on a Map]]
 
* [[How to Create a Custom Map View|How to Create a Custom Map View with Nokia's Map API for Java ME]]
 
* [[How to Create a Custom Map View|How to Create a Custom Map View with Nokia's Map API for Java ME]]
Line 302: Line 280:
  
 
{{ArticleMetaData
 
{{ArticleMetaData
|sourcecode=[[Media:GoogleMapsMIDletSource.zip]]
+
|sourcecode=[[Media:NokiaMapsShowPOIonMapSource.zip]]
[[Media:GoogleMapsMIDletBinaries.zip]]
+
[[Media:NokiaMapsShowPOIonMapBinaries.zip]]
 
|installfile=
 
|installfile=
|devices=Nokia X3-02, Nokia 6210N, Nokia 6233, Nokia 701
+
|devices=Nokia X3-02, Nokia 6210N, Nokia 6700classic, Nokia 701
 
|sdk=[http://www.developer.nokia.com/Develop/Java/ Nokia SDK 1.0 for Java]
 
|sdk=[http://www.developer.nokia.com/Develop/Java/ Nokia SDK 1.0 for Java]
 
|platform=Series 40, Symbian
 
|platform=Series 40, Symbian
|devicecompatability= since Series 40 6th Edition, since Symbian S60 3rd Edition
+
|devicecompatability= MIDP2.0/CLDC 1.1
 +
|dependencies=[http://www.developer.nokia.com/Develop/Maps/Maps_API_for_Java_ME/ Map API for Java ME] version 1.1
 
|signing=
 
|signing=
 
|capabilities=<!-- Capabilities required by the article/code example (e.g. Location, NetworkServices. -->
 
|capabilities=<!-- Capabilities required by the article/code example (e.g. Location, NetworkServices. -->
|keywords=Google Static Maps API, Image, Coordinates, Search Address or POI on a Map, GeoLocation
+
|keywords=Nokia Maps, Java ME, Nokia Maps API for Java ME, Image, Coordinates, Search Address or Point of Interest on a Map, GeoLocation
 
|id= <!-- Article Id (Knowledge base articles only) -->
 
|id= <!-- Article Id (Knowledge base articles only) -->
|creationdate=20111128
+
|creationdate=20120113
|author=[[User:jappit]]
+
|author=[[User:skalogir]]
 
|review-by=[[User:skalogir]]
 
|review-by=[[User:skalogir]]
|review-timestamp=20120111
+
|review-timestamp=20120113
 
}}
 
}}
 
 
<!-- translations -->
 
[[ja:Google Maps API in Java ME(日本語)]]
 

Revision as of 18:10, 17 July 2012

This article describes how to search for an address or a point of interest and display it on the Map. It uses the Map API for Java ME for the translation of the search string into valid coordinates and for the retrieval of the relevant map image.

Sydneypoi.pngSydneyshow.png

Contents

Introduction

As of January 2012, Nokia Maps API for Java ME is provided as a separate library. It uses the device's network connection in order to download and display map data. The library can be downloaded from here. It needs to be added to the working project, before the code in this example can be compiled.

The Nokis Maps API for Java ME uses the concept of tiles. The screen is divided into several tiles. When scrolling the map, if the new screen contains tiles that have already been downloaded, these will be used and only the new tiles will be requested via the network. This reduces the amount of generated data traffic and makes the interaction with the user faster. In some platforms, such as Series 40 6th Edition, the default security access level for the underlying network operations by default is set to "Always ask", meaning that many prompts will be generated each time the map needs to be updated. Information on how to change the security access after the application has been installed, and also how to change the default security level via signing the application can be found here: Java_Security_Domains

Interaction modes for different types of devices

The device requirements for running this example are MIDP2.0 and CLDC 1.1. This covers a very large group of devices from Symbian to Series 40 and from non-touch to Full Touch and Touch and Type. Nokia's Map API for Java ME masks away the differences between various devices and provides different interaction modes for each device, without the developer having to worry about the details of the implementation. With respect to zooming, the following interaction modes are supported:

  • On non-touch devices, the zoom functionality is provided by the star (*) and hash (#) keys for zooming in and out respectively. No zoom controls are displayed on the screen.
  • On full-touch devices, the zoom functionality is provided by the zoom controls displayed on the screen.
  • On Touch and Type and full-touch with keyboard devices, the zoom functionality is provided by both the star (*) and hash (#) keys for zooming in and out respectively and the zoom controls displayed on the screen.


With respect to panning (i.e. scrolling or moving the screen around the target location), the following interaction modes are supported:

  • On non-touch devices, the navigation keypad is used to move the center of the map up, down, left and right from its current position
  • On full-touch and Touch and Type devices, panning is touch driven.
  • On full-touch devices with keyboard, such as Nokia E7-00, panning is also supported by the keyboard's arrow keys.


Warning.pngWarning: On S60 5th Edition devices, this application requires that the On Screen Keypad (OSK) is explicitly defined as disabled. In order to disable OSK and use the entire screen for displaying the Map, the following line should be added to the application's file descriptor (jad file): Nokia-MIDlet-On-Screen-Keypad: no

The search screen

The search screen consists of a Form, that contains the TextField where the user can type the requested address or point of interest, a hidden info message item that is only used for the display of any potential errors and two Commands, an exit and a search Command.

		//Main Search Screen
display=Display.getDisplay(this);
mainform=new Form("Address or POI");
mainform.append(text);
mainform.append(infomessage);
mainform.addCommand(searchCmd);
mainform.addCommand(exitCmd);
mainform.setCommandListener(this);
display.setCurrent(mainform);

The search Command triggers the connection to the Nokia Maps servers. An additional control has been added, so that no operation takes place if the user hasn't typed any text, after selecting the Search option:

		if(c==searchCmd)
{
String search=text.getString();
infomessage.setText("");
if(!search.equals(""))
{
showOnMap(search);
}
}

The map screen

It is quite easy to create a scrollable, dynamic map with zoom functionality with Nokia Maps API for Java ME. We can simply instantiate a new MapCanvas object and set it as current:

			mapcanvas = new MapCanvas(display);
display.setCurrent(mapcanvas);

The next problem, is to geolocate the search string and instantiate the geo object, which is the GeoCoordinate of the target location. We can also create a standard balloon marker and attach it to that location. Note that a list of results is retrieved from Nokia's servers as an array of Location objects. In this example, we use by default the first available result.

 //The search Manager for this search
GeocodeRequest geocodeRequest = searchFactory.createGeocodeRequest();
//Retrieves the Point of Interest
geocodeRequest.geocode(poi, null, this); // this refers to a GeocodeRequestListener

The search has been made asynchronously, therefore the GeocodeRequestListener interface must be implemented, and the app will be informed once the results are supplied.

public void onRequestComplete(GeocodeRequest request, Location[] result) {
 
//Stores the results in an array of Location objects
locationresults = result;
//The first result is taken
geo = locationresults[0].getDisplayPosition();
//Centers the Map at the Point of Interest
map.setCenter(geo);
//Displays the marker
marker = getMapFactory().createStandardMarker(geo);
map.addMapObject(marker);
//Sets the zoom level
map.setZoomLevel(14, 0, 0);
//Displays the Map on the Screen
 
addCommand(exitCmd);
addCommand(backCmd);
setCommandListener(this);
display.setCurrent(this);
 
}
 
public void onRequestError(GeocodeRequest request, Throwable error) {
... etc ...
}

If we need on top of that, our MapDisplay to be displayed with a specific zoom level and to be centered around a given coordinate point, we can write the following:

			//Centers the Map at the Point of Interest
mapdisplay.setCenter(geo);
//Sets the zoom level
mapdisplay.setZoomLevel(14,0,0);

Finally, we need to set the MapCanvas we have just created as current:

		        display.setCurrent(this);

The MIDlet source

import com.nokia.maps.common.ApplicationContext;
import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
 
public class ShowPOIonMapMidlet extends MIDlet {
 
protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {}
 
protected void pauseApp() {}
 
protected void startApp() throws MIDletStateChangeException {
 
// Please initialise the appId and token first
ApplicationContext.getInstance().setAppID("...");
ApplicationContext.getInstance().setToken("...");
 
Display display = Display.getDisplay(this);
ShowPOIonMap md = new ShowPOIonMap(display, this);
}
}
import com.nokia.maps.common.GeoCoordinate;
import com.nokia.maps.common.Location;
import com.nokia.maps.map.MapCanvas;
import com.nokia.maps.map.MapStandardMarker;
 
import com.nokia.maps.search.GeocodeRequest;
import com.nokia.maps.search.GeocodeRequestListener;
import com.nokia.maps.search.SearchFactory;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.StringItem;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
 
public class ShowPOIonMap extends MapCanvas implements CommandListener, GeocodeRequestListener {
 
MIDlet midlet;
Form mainform;
 
GeoCoordinate geo;
TextField text = new TextField("Type Address or POI", "", 100, TextField.ANY);
StringItem infomessage = new StringItem("", "");
Command exitCmd = new Command("Exit", Command.EXIT, 0);
Command backCmd = new Command("Back", Command.BACK, 0);
Command searchCmd = new Command("Search", Command.OK, 1);
Location[] locationresults;
MapStandardMarker marker;
private SearchFactory searchFactory = SearchFactory.getInstance();
 
public ShowPOIonMap(Display display, MIDlet midlet) {
super(display);
this.midlet = midlet;
//Main Search Screen
 
mainform = new Form("Address or POI");
mainform.append(text);
mainform.append(infomessage);
mainform.addCommand(searchCmd);
mainform.addCommand(exitCmd);
mainform.setCommandListener(this);
display.setCurrent(mainform);
}
 
public void onRequestComplete(GeocodeRequest request, Location[] result) {
 
//Stores the results in an array of Location objects
locationresults = result;
//The first result is taken
geo = locationresults[0].getDisplayPosition();
//Centers the Map at the Point of Interest
map.setCenter(geo);
//Displays the marker
marker = getMapFactory().createStandardMarker(geo);
map.addMapObject(marker);
//Sets the zoom level
map.setZoomLevel(14, 0, 0);
//Displays the Map on the Screen
 
addCommand(exitCmd);
addCommand(backCmd);
setCommandListener(this);
display.setCurrent(this);
 
}
 
public void onRequestError(GeocodeRequest request, Throwable error) {
locationresults = null;
setTitle("");
Alert a = new Alert("ERROR", error.toString(), null, AlertType.INFO);
 
a.setTimeout(Alert.FOREVER);
display.setCurrent(a, this);
 
}
 
public void showOnMap(String poi) {
try {
//The search Manager for this search
GeocodeRequest geocodeRequest = searchFactory.createGeocodeRequest();
//Retrieves the Point of Interest
geocodeRequest.geocode(poi, null, this);
setTitle(poi);
 
} catch (RuntimeException e) {
infomessage.setText("Error:" + e.getMessage());
}
}
 
public void commandAction(Command c, Displayable d) {
if (c == searchCmd) {
String search = text.getString();
infomessage.setText("");
if (!search.equals("")) {
showOnMap(search);
}
}
if (c == exitCmd) {
midlet.notifyDestroyed();
}
if (c == backCmd) {
display.setCurrent(mainform);
}
}
 
public void onMapUpdateError(String description, Throwable detail, boolean critical) {
}
 
public void onMapContentComplete() {
}
}

Resources

See also

Article Metadata
Code ExampleTested with
Devices(s): Nokia X3-02, Nokia 6210N, Nokia 6700classic, Nokia 701
Compatibility
Device(s): MIDP2.0/CLDC 1.1
Dependencies: Map API for Java ME version 1.1
Article
Keywords: Nokia Maps, Java ME, Nokia Maps API for Java ME, Image, Coordinates, Search Address or Point of Interest on a Map, GeoLocation
Created: skalogir (13 Jan 2012)
Reviewed: skalogir (13 Jan 2012)
Last edited: Oskar Bukolt (17 Jul 2012)
221 page views in the last 30 days.
×