×
Namespaces

Variants
Actions
(Difference between revisions)

Mapping points of interest using Java ME

From Nokia Developer Wiki
Jump to: navigation, search
hamishwillee (Talk | contribs)
m (Hamishwillee -)
jasfox (Talk | contribs)
m (Jasfox - recat)
Line 1: Line 1:
[[Category:Series 40]][[Category:Code Examples]][[Category:Code Snippet]][[Category:Networking]][[Category:HERE Maps]][[Category:Files/Data]][[Category:Location API (JSR-179)]][[Category:Nokia Asha]][[Category:Nokia Asha Platform 1.0]][[Category:Series 40 Developer Platform 1.1]][[Category:Series 40 Developer Platform 2.0]][[Category:LCDUI]]
+
[[Category:Series 40]][[Category:Code Examples]][[Category:Networking]][[Category:Files/Data]][[Category:Location API (JSR-179)]][[Category:Nokia Asha]][[Category:Nokia Asha Platform 1.0]][[Category:Series 40 Developer Platform 1.1]][[Category:Series 40 Developer Platform 2.0]][[Category:LCDUI]]
 
{{Abstract|This article explains how to use Nokia SDK 2.0 to create application for mapping points of interest for a Series 40 full touch device. The app also gets weather and news information from web services.}}  
 
{{Abstract|This article explains how to use Nokia SDK 2.0 to create application for mapping points of interest for a Series 40 full touch device. The app also gets weather and news information from web services.}}  
 
{{Note|This is an entry in the [[Asha Touch Competition 2012Q3]]}}
 
{{Note|This is an entry in the [[Asha Touch Competition 2012Q3]]}}

Revision as of 10:25, 12 July 2013

This article explains how to use Nokia SDK 2.0 to create application for mapping points of interest for a Series 40 full touch device. The app also gets weather and news information from web services.

Note.pngNote: This is an entry in the Asha Touch Competition 2012Q3

Article Metadata
Code ExampleTested with
Devices(s): X3-02, Asha 311, Asha 501
Compatibility
Platform(s): Asha, Series 40, Symbian^1, Symbian^3
Nokia Asha
Nokia Asha Platform 1.0
Series 40
Series 40 DP 2.0
Series 40 DP 1.1
Article
Created: adarsha_saraff (08 Jul 2012)
Last edited: jasfox (12 Jul 2013)


Contents

Prerequisites

Quick Walk through

The code example uses the content of a Refreshment app. The app provides information about current weather, contains news, and includes mapping features to help you locate refreshment.

In future the app may be extended to include navigation information with transits, walk and drive, or to additionally help users locate ATM's, bus stops, metro stations, nearby taxi stand and many more.

Quick intro to Location API

We need the current location to request Weather data to the server. I will explain how to request to the server to obtain weather data later in the coming topics. Current location is also used with maps to find out the nearest chat center and hangouts.

Creating object of location

LocationProvider myloc;

Current location through Cellular ID

Series 40 supports cellular ID and WLAN positioning. Series 40 extends the Location API with the com.nokia.mid.location.LocationUtil class. Therefore, I have used LocationUtil.getLocationProvider(type, null). However, support for this in specific devices vary. So, I will be using MTE_CELLID, MTY_NETWORKBASED and MTA_ASSISTED.

Use the following statements in startApp() method.

int[] type = {Location.MTE_CELLID|Location.MTY_NETWORKBASED|Location.MTA_ASSISTED};
try {
myloc = LocationUtil.getLocationProvider(type, null);
javax.microedition.location.Location Loc = myloc.getLocation(300);
if(Loc != null){
QualifiedCoordinates cor = Loc.getQualifiedCoordinates();
lat = cor.getLatitude();
lon = cor.getLongitude();
}
} catch (Exception ex) {}

Quick intro to Nokia Map API

Creating object of MapCanvas and setting up the things

MapCanvas mapCanvas = new MapCanvas(Display.getDisplay(this)) {
 
public void onMapUpdateError(String arg0, Throwable arg1, boolean arg2) {
// TODO Auto-generated method stub
 
}
 
public void onMapContentComplete() {
// TODO Auto-generated method stub
 
}
};

In startApp() method add the following lines to setup initial things.

ApplicationContext.getInstance().setAppID("Your_App_ID");
ApplicationContext.getInstance().setToken("Your_App_Token");

Positioning the map to current location and zooming

We will start with positioning to current location on map and extend this location to find out refreshment places near.

mapCanvas.getMapDisplay().zoomTo(new GeoBoundingBox(new GeoCoordinate(lat, lon, 0)), false);
mapCanvas.getMapDisplay().addMapObject(mapCanvas.getMapFactory().createStandardMarker(new GeoCoordinate(lat, lon, 0), 1, "It's You", 2));

Tip.pngTip: It is good practice to create a marker to indicate current position. Use createStandardMarker() method of MapFactory class as shown above.

Refreshment map find nearby.png

Nokia UI Components used in here

Apart from using pure LCDUI components, I will make use of two new components provided and supported on Series 40 full touch devices. These are Category Bar and Icon Command.

Tip.pngTip: If you are interested in designing an app UI with Category Bar see this article.

CategoryBar

Creating Category Bar and making it visible.

CategoryBar cbar;
try {
Image[] unsel = { Image.createImage("/home.png"),
Image.createImage("/news.png"),
Image.createImage("/refreshment.png") };
Image[] sel = { Image.createImage("/home.png"),
Image.createImage("/news.png"),
Image.createImage("/refreshment.png") };
String[] lab = { "Home", "News", "Refreshments" };
cbar = new CategoryBar(unsel, sel, lab);
cbar.setElementListener(this);
cbar.setTransitionSupport(true);
cbar.setVisibility(true);
 
} catch (Exception ex) {}

Note.pngNote: App can have only one Category Bar set visible at a time. However You can create multiple Category Bars but only one can be made visible. If the app requires multiple Category Bars, create as many as needed and change visibility as required. Ex: Form f1 has CategoryBar cbar1 with some items and Form f2 has CategoryBar cbar2 with some items. For Form f1 as current display, use: cbar1.setVisibility(true); cbar2.setVisibility(false); and so on.

IconCommand

Creating and using Icon Command is like normal command. More on Icon Command

try {
refeshCommand = new IconCommand("Refresh",Image.createImage("/refresh.png"),Image.createImage("/refresh.png"), Command.OK, 0);
currentposCommand = new IconCommand("Current pos",Image.createImage("/curloc.png"), Image.createImage("/curloc.png"), Command.OK, 0);
} catch (Exception ex) {
}

Weather feed

Weather feed and rss feed data will be in XML format and we need to parse these xml to extract required information. There are number of XML parsers available: About XML Parser. kXML is one of such parser I would like to use. kXML 1.2 have been included in source program.

public void run() {
try {
//Url format for http request is: http://free.worldweatheronline.com/feed/weather.ashx?q=<latitude>,<longitude>&format=xml&num_of_days=1&key=XXXXXXXXXXXXXXXXXXXXXX
// or http://free.worldweatheronline.com/feed/weather.ashx?q=<City>,<Country>&format=xml&num_of_days=1&key=XXXXXXXXXXXXXXXXXXXXXX
String reqUrl = url + lat + "," + lon + keys;
con = (HttpConnection) Connector.open(reqUrl, Connector.READ_WRITE,true);
con.setRequestMethod(HttpConnection.GET);
weatherxmlparser(con.openDataInputStream());
} catch (Exception e) {
}
}

Note.pngNote: Weather API returns data in XML, CSV, json format. XML is easy to parse, so I will use xml in &format field.

<data>
<request>
<type>LatLon</type>
<query>Lat 28.64 and Lon 77.24</query>
</request>
<current_condition>
<observation_time>11:10 AM</observation_time>
<temp_C>33</temp_C>
<temp_F>91</temp_F>
<weatherCode>353</weatherCode>
<weatherIconUrl>
<![CDATA[
http://www.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0009_light_rain_showers.png
]]>
</weatherIconUrl>
<weatherDesc>
<![CDATA[ Light rain shower ]]>
</weatherDesc>
<windspeedMiles>11</windspeedMiles>
<windspeedKmph>17</windspeedKmph>
<winddirDegree>60</winddirDegree>
<winddir16Point>ENE</winddir16Point>
<precipMM>1.4</precipMM>
<humidity>59</humidity>
<visibility>3</visibility>
<pressure>998</pressure>
<cloudcover>75</cloudcover>
</current_condition>
<weather>
<date>2012-07-25</date>
<tempMaxC>37</tempMaxC>
<tempMaxF>99</tempMaxF>
<tempMinC>28</tempMinC>
<tempMinF>82</tempMinF>
<windspeedMiles>10</windspeedMiles>
<windspeedKmph>16</windspeedKmph>
<winddirection>E</winddirection>
<winddir16Point>E</winddir16Point>
<winddirDegree>87</winddirDegree>
<weatherCode>116</weatherCode>
<weatherIconUrl>
<![CDATA[
http://www.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0002_sunny_intervals.png
]]>
</weatherIconUrl>
<weatherDesc>
<![CDATA[ Partly Cloudy ]]>
</weatherDesc>
<precipMM>8.7</precipMM>
</weather>
</data>

Above is sample XML data response. We need to parse the xml data to obtain required information. Important information required observation_time, temp_C, weatherIconUrl and weatherDesc. We need to design the XML parser to parse the weather feed. XML parser for this will be one below:

public void weatherxmlparser(InputStream xmldata) {
Reader r = new InputStreamReader(xmldata);
 
try {
XmlParser parser = new XmlParser(r);
traverse( parser, "" );
} catch (Exception e) {
}
}
public void traverse(XmlParser parser, String indent) {
try {
boolean leave = false;
do {
ParseEvent event = parser.read();
ParseEvent pe;
switch (event.getType()) {
// For example, <title>
case Xml.START_TAG:
// see API doc of StartTag for more access methods
// Pick up Time for display
if ("observation_time".equals(event.getName())) {
pe = parser.read();
this.r.wtr.time = pe.getText();
}
// Pick up temperature for display and so on
if ("temp_C".equals(event.getName())) {
pe = parser.read();
this.r.wtr.tempINDegC = pe.getText();
}
 
if ("weatherIconUrl".equals(event.getName())) {
pe = parser.read();
//download image for the corresponding URL
this.r.wtr.wicon = loadImage(pe.getText());
 
}
if ("weatherDesc".equals(event.getName())) {
pe = parser.read();
this.r.wtr.climate = pe.getText();
}
traverse( parser, "" ) ; // recursion call for each <tag></tag>
break;
// For example </title>
case Xml.END_TAG:
leave = true;
break;
// For example </data>
case Xml.END_DOCUMENT:
leave = true;
break;
// For example, the text between tags
case Xml.TEXT:
break;
case Xml.WHITESPACE:
break;
default:
}
} while (!leave);
} catch (Exception e) {
}
}

Refreshment home.png
Weather data obtained from server is parsed and painted on Canvas of a CustomItem.

News Feed

To get latest new, we will be using Yahoo News RSS feed.

Category Feed URL
Top Stories http://news.yahoo.com/rss/topstories
World News http://news.yahoo.com/rss/world
Entertainment http://news.yahoo.com/rss/entertainment
Movies http://news.yahoo.com/rss/movies

In news form, user selects an choice from choice group. Through itemState listener we are able to identify the change in the selection. Depending upon the selection, request to server is sent through HTTP request. Which is done using Thread. Using Thread because server may busy at the time of request and may not respond to our request. Therefore we make repeated request until server responds to our request. Then by opening I/O streams we can read the data. The thread t handles the task of requesting and opening I/O connection.

public void getFeeds(String url) {
yahooNewURL = url;
t = new Thread(this);
t.start();
}
public void run() {
try {
con = (HttpConnection) Connector.open(yahooNewURL,Connector.READ_WRITE, true);
con.setRequestMethod(HttpConnection.GET);
getData(con.openInputStream());
} catch (Exception e) {
// TODO: handle exception
}
}
<rss xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
<channel>
<title>Movies News Headlines - Yahoo! News</title>
<link>http://news.yahoo.com/movies/</link>
<description>
Get the latest Movies news headlines from Yahoo! News. Find breaking Movies news, including analysis and opinion on top Movies stories, photos and more.
</description>
<language>en-US</language>
<copyright>Copyright (c) 2012 Yahoo! Inc. All rights reserved</copyright>
<pubDate>Mon, 23 Jul 2012 20:19:11 -0400</pubDate>
<ttl>5</ttl>
<image>
<title>Movies News Headlines - Yahoo! News</title>
<link>http://news.yahoo.com/movies/</link>
<url>http://l.yimg.com/a/i/us/nws/th/main_142c.gif</url>
</image>
<item>
<title>"Dark Knight Rises" earns $160.8 million in debut</title>
<description>
<p><a href="http://news.yahoo.com/dark-knight-rises-earns-160-8-million-debut-001911324--finance.html"><img src="http://l3.yimg.com/bt/api/res/1.2/34Jg14ltXARef75.MHa9rw--/YXBwaWQ9eW5ld3M7Zmk9ZmlsbDtoPTg2O3E9ODU7dz0xMzA-/http://media.zenfs.com/en_us/News/Reuters/2012-07-24T002136Z_1_CBRE86N010600_RTROPTP_2_FILM-US-USA-SHOOTING-BOXOFFICE-OFFICIAL.JPG" width="130" height="86" alt="A poster for &quot;The Dark Knight Rises&quot; is displayed in Burbank" align="left" title="A poster for &quot;The Dark Knight Rises&quot; is displayed in Burbank" border="0" /></a>LOS ANGELES (Reuters) - Warner Bros said on Monday &quot;The Dark Knight Rises&quot; took in $160.8 million at U.S. and Canadian box offices over the weekend, which is lower than industry estimates for the debut that felt the impact of last week&#039;s movie theater massacre in Colorado. &quot;Dark Knight Rises&quot; also opened in 17 international markets with $88 million in ticket sales to put its global total at just under $250 million for its debut. Warner Bros, a unit of Time Warner Inc, made no initial comment beyond simply releasing the figures. ...</p><br clear="all"/>
</description>
<link>
http://news.yahoo.com/dark-knight-rises-earns-160-8-million-debut-001911324--finance.html
</link>
<pubDate>Mon, 23 Jul 2012 20:19:11 -0400</pubDate>
<source url="http://www.reuters.com/">Reuters</source>
<guid isPermaLink="false">
dark-knight-rises-earns-160-8-million-debut-001911324--finance
</guid>
<media:content url="http://l3.yimg.com/bt/api/res/1.2/34Jg14ltXARef75.MHa9rw--/YXBwaWQ9eW5ld3M7Zmk9ZmlsbDtoPTg2O3E9ODU7dz0xMzA-/http://media.zenfs.com/en_us/News/Reuters/2012-07-24T002136Z_1_CBRE86N010600_RTROPTP_2_FILM-US-USA-SHOOTING-BOXOFFICE-OFFICIAL.JPG" type="image/jpeg" width="130" height="86"/>
<media:text type="html">
<p><a href="http://news.yahoo.com/dark-knight-rises-earns-160-8-million-debut-001911324--finance.html"><img src="http://l3.yimg.com/bt/api/res/1.2/34Jg14ltXARef75.MHa9rw--/YXBwaWQ9eW5ld3M7Zmk9ZmlsbDtoPTg2O3E9ODU7dz0xMzA-/http://media.zenfs.com/en_us/News/Reuters/2012-07-24T002136Z_1_CBRE86N010600_RTROPTP_2_FILM-US-USA-SHOOTING-BOXOFFICE-OFFICIAL.JPG" width="130" height="86" alt="A poster for &quot;The Dark Knight Rises&quot; is displayed in Burbank" align="left" title="A poster for &quot;The Dark Knight Rises&quot; is displayed in Burbank" border="0" /></a>LOS ANGELES (Reuters) - Warner Bros said on Monday &quot;The Dark Knight Rises&quot; took in $160.8 million at U.S. and Canadian box offices over the weekend, which is lower than industry estimates for the debut that felt the impact of last week&#039;s movie theater massacre in Colorado. &quot;Dark Knight Rises&quot; also opened in 17 international markets with $88 million in ticket sales to put its global total at just under $250 million for its debut. Warner Bros, a unit of Time Warner Inc, made no initial comment beyond simply releasing the figures. ...</p><br clear="all"/>
</media:text>
<media:credit role="publishing company"/>
</item>
</channel>
</rss>

Above shows the sample RSS feed in XML format received from server. We need to parse this XML to retrieve the required information. We are interested in reading title, link and published date. Therefore our parser should be designed to read only these fields located between <item> and </item> tag. So we do recursive parsing between each <item>and </item> tags until the end of document is reached.
Code for the same will be:

public void getData(InputStream xmldata) {
Reader r = new InputStreamReader(xmldata);
try {
XmlParser parser = new XmlParser(r);
ParseEvent pe = null;
parser.skip();
parser.read(Xml.START_TAG, null, "rss");
parser.skip();
parser.read(Xml.START_TAG, null, "channel");
boolean trucking = true;
while (trucking) {
pe = parser.read();
if (pe.getType() == Xml.START_TAG) {
String name = pe.getName();
if (name.equals("item")) {
String title, link, description;
title = link = description = null;
while ((pe.getType() != Xml.END_TAG)
|| (pe.getName().equals(name) == false)) {
pe = parser.read();
if (pe.getType() == Xml.START_TAG
&& pe.getName().equals("title")) {
pe = parser.read();
title = pe.getText();
} else if (pe.getType() == Xml.START_TAG
&& pe.getName().equals("link")) {
pe = parser.read();
link = pe.getText();
} else if (pe.getType() == Xml.START_TAG
&& pe.getName().equals("pubDate")) {
pe = parser.read();
description = pe.getText();
}
}
this.r.newsf.append(new customlist(title, link,
description, this.r));
} else {
while ((pe.getType() != Xml.END_TAG)
|| (pe.getName().equals(name) == false))
pe = parser.read();
}
}
if (pe.getType() == Xml.END_TAG && pe.getName().equals("rss")){
trucking = false;
t = null;
}
}
} catch (Exception e) {
}
}

Once we get these required information, we need to append this to our form. Which is done using this.r.newsf.append(new customlist(title,link,description, this.r)); . Where r - instance of Refreshment MIDlet, newsf is the form where we need to append.

Refreshment news.png

Finally after parsing and appending the whole XML document. News Form looks like as above.

Working with MapCanvas

Bookmarking the location

Bookmarking feature enables to save the favorite locations on mobile. You visit some place or hotels, you may forget the address or name of the hotel. So using bookmark feature you can tag and save the current location in mobile phone.

if(c == bookmarkCommand){
Display.getDisplay(this).setCurrent(bookmark);
}
else if(c == okCommand){
writeSavedData(bookmark.getString(), lat, lon);
Display.getDisplay(this).setCurrent(mapCanvas);
}
else if(c == deleteCommand){
clearbookmark();
homef.deleteAll();
homef.append(wtr);
readSavedData();
}

Locating bookmarked location on map

public void updateMap(double lat, double lon, String name){
mapCanvas.getMapDisplay().removeAllMapObjects();
mapCanvas.getMapDisplay().reconnect();
mapCanvas.getMapDisplay().zoomTo(
new GeoBoundingBox(new GeoCoordinate(lat, lon, 0)), false);
mapCanvas.getMapDisplay().addMapObject(
mapCanvas.getMapFactory().createStandardMarker(
new GeoCoordinate(lat, lon, 0), 1, name, 2));
Display.getDisplay(this).setCurrent(mapCanvas);
}

Saving, Loading and Clearing bookmarks

We will be using record store to save the bookmark location, including name and date of bookmark using RMS.

We need a some structured way to store - latitude, longitude, date of bookmark and bookmark name. So that we can retrieve and use the same point to locate on map. Fist let us construct a schema to store in our database (record store).

Type Column 1 Column 2 Column 3 Column 4
Field name Latitude Bookmark name Longitude Bookmark date
Datatype double String double String

Saving Bookmark name in between Latitude and Longitude is that we can differentiate two double value, i.e String between them acts as a delimiter.

public void readSavedData() {
homef.append("Recient Visit");
openRefreshmentRecordStore();
try {
 
if(refreshmentRecordStore.getNumRecords() == 0){ /*Checking the record store for records. if total no. of record is zero*/
homef.append("\nYou have not visited any place."); /* append some string like, you have not bookmarked any places */
}
byte[] byt = new byte[1000]; /* read the records from record store and add them to home screen */
ByteArrayInputStream Istream = new ByteArrayInputStream(byt);
DataInputStream dinp = new DataInputStream(Istream);
for (int i = 1; i <= refreshmentRecordStore.getNumRecords(); i++) {
refreshmentRecordStore.getRecord(i,byt,0);
double lat = dinp.readDouble(); /* latitude is a double value, that is stored at first column */
String place = dinp.readUTF(); /* Favorite name a string value, that is stored at second column */
double lon = dinp.readDouble(); /* longitude is double value, that is stored at third column */
String date = dinp.readUTF(); /* date is string value, that is stored at fourth column */
homef.append(new recentvisitlist(place, lat, lon,date,this)); /*create a list item with these value and append to home form*/
dinp.reset();
}
dinp.close();
Istream.close();
 
 
} catch (Exception e) {
 
}closeRefreshmentRecordStore();
}
public void writeSavedData(String title, double lat, double lon) {
DateField now = new DateField("",DateField.DATE_TIME); /* get current system date and time */
now.setDate(new Date());
String date = now.getDate().toString(); /* convert the date to string, so that we can store it in record store */
openRefreshmentRecordStore(); /* open record store and save */
try{
ByteArrayOutputStream Ostream = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(Ostream);
dout.writeDouble(lat);
dout.writeUTF(title);
dout.writeDouble(lon);
dout.writeUTF(date);
dout.flush();
byte[] byt = Ostream.toByteArray();
refreshmentRecordStore.addRecord(byt, 0, byt.length);
Ostream.close();
dout.close();
}catch (Exception e) {
// TODO: handle exception
}
closeRefreshmentRecordStore();
}
public void clearbookmark(){
try{
RecordStore.deleteRecordStore("refreshmentDataBase"); /* to clear all bookmark, delete the record store */
}catch (Exception e) {
// TODO: handle exception
}
}

More with maps

You can add functions like finding route between two locations, searching an address and locating them on map. Since there are wiki articles on working on map, I am not covering them here in this article or example.

Source Files

File:Refreshment.zip

References

Category Bar
Icon Command
Custom Item
Canvas
Alert

451 page views in the last 30 days.