×
Namespaces

Variants
Actions

Archived:Python on Symbian/10. Location Based Services

From Nokia Developer Wiki
Jump to: navigation, search

Archived.pngAquivado: Este artigo foi arquivado, pois o conteúdo não é mais considerado relevante para se criar soluções comerciais atuais. Se você achar que este artigo ainda é importante, inclua o template {{ForArchiveReview|escreva a sua justificativa}}.

All PySymbian articles have been archived. PySymbian is no longer maintained by Nokia and is not guaranteed to work on more recent Symbian devices. It is not possible to submit apps to Nokia Store.

Article Metadata
Code ExampleArticle
Created: hamishwillee (30 Nov 2010)
Last edited: hamishwillee (08 May 2013)

Original Authors: Pankaj Nathani

This chapter explains the location and positioning technologies available on Symbian mobile devices, and demonstrates how position information can be combined with information from online services to create a simple mapping application.

Contents

Introduction

GPS enabled mobile devices are already replacing dedicated hardware for mapping, search and navigation. This trend is likely to continue as more devices have integrated positioning modules, and companies such as Nokia and Google provide free mapping solutions as part of their standard offering. This is good news for developers. The world might not need another mapping application but there are still endless opportunities for compelling location-aware applications.

One of the most powerful ways to personalize mobile applications is to make them location aware. An application that tells you when the next train you want to catch is leaving is very useful: One that tells you how far it is to walk to the train, and wakes you up when you're about to sleep though your station is even more useful!

In this chapter we demonstrate how to add location context sensitivity to Python applications.

Figure 10.1 Basic classification of location based services

Getting location information

Python on Symbian allows the location of a mobile device to be determined based on information from the telephone network or from GPS receivers. There are other sophisticated methods for determining location (for example, indoor positioning using WLAN or Bluetooth) but these methods are beyond the scope of this book.

Network-based location

Network-based location tracking techniques use the service provider's network infrastructure to get the approximate position of the mobile device. The accuracy of network-provided location information is relatively poor when compared to GPS positioning (at best several hundred meters, and decreasing in suburban and rural areas where cells are more widely spaced). However, network-provided information has the advantage of being available indoors and on low end mobile handsets.

The following location information can be obtained from the mobile network:

  • Mobile Country Code (MCC) - Represents the user's home country, as described by the SIM card's IMSI number. A list of MCCs is available here.
  • Mobile Network Code (MNC) - Identifies a mobile phone operator/carrier within a country (is used in conjunction with the MCC to identify the carrier globally)
  • Location Area Code (LAC) - Uniquely represents a small geographical location.
  • Cell ID (CID) - The unique number of a GSM cell for a given service provider.
The network information above is provided by the location module . The module is very simple: It has a single function, gsm_location(), that returns a tuple containing the GSM location information for the device.

Note.pngNote: The Location capability is needed in order to use the location module. If the application (or the Python shell) does not have the capability gsm_location() returns None.

The example below shows how to access and print the location information, and is followed by a screenshot of the script running on a phone:
import location
print location.gsm_location()
MCC, MNC, LAC, CID = location.gsm_location()
print "MCC =" + str(MCC)
print "MNC =" + str(MNC)
print "LAC =" + str(LAC)
print "CID =" + str(CID)
Figure 10.2 Results from gsm location()

Note that the values above are all codes, not positioning co-ordinates that you can use directly in your application. There are a number of databases which map LAC/CIDs to location. Some of them, such as Google, provide web services that allow you to access the information over an Internet connection using the techniques discussed in chapter 14. For more information, search the Internet using the term "Mapping cell ID to location" or look for an open source project providing a cell ID database - www.opencellid.org.

GPS positioning

The Global Positioning System (GPS) is a space-based global navigation system. A commercial GPS receiver can locate its position to within 3 to 15 metres, 95% of the time, by precisely timing the signals sent from a number of GPS satellites. GPS receivers can only be used outside as they require line of sight to at least 3 satellites.

Symbian platform mobile devices often include an integrated GPS receiver and support for Assisted GPS (A-GPS) - a GSM network based system that improves GPS startup performance. Symbian devices can also use positioning information from Bluetooth-enabled external GPS receivers.

The Symbian platform UI includes applications for navigation, position, trip distance calculation and satellite status (showing the signal strength for each detected satellite). Screenshots are given below:

Figure 10.3 Symbian UI screenshots

Python applications use the positioning module to get information from both built in and external Bluetooth GPS receivers. Like the location module, it is simple to use.

The following code shows how to import the positioning module and how to use the modules() function to list the available modules:

>>> import positioning
>>> positioning.modules()
[{'available': 0, 'id': 270526873, 'name': u'Bluetooth GPS'}, {'available': 1, 'id': 270526858, 'name': u'Integrated GPS'}, {'available': 1, 'id': 270559509, 'name': u'Network based'}]

The function returns a dictionary object for each supported module. The object indicates whether the module is available (a value of "1"), and contains its ID and a human readable name (description). By reformatting the list returned by the code above, we can see that this device supports an integrated GPS receiver, an AGPS module and a module for working with external Bluetooth GPS receivers, but that only the integrated GPS and AGPS are currently available:

{'available': 0, 'id': 270526873, 'name': u'Bluetooth GPS'}
{'available': 1, 'id': 270526858, 'name': u'Integrated GPS'}
{'available': 1, 'id': 270559509, 'name': u'Network based'}

We select the GPS module that we want to use by specifying its id with the select_module(id) function as follows: (In a real application we'd first check whether the module is available).

>>> positioning.select_module(270526858)

We can get detailed characteristics for a specific module using the module_info(id) function.

>>> positioning.module_info(270526858)
{'available': 1, 'status': {'data_quality': 3, 'device_status': 7}, 'version': u'1.00(0)', 'name': u'Integrated GPS', 'position_quality': {'vertical_accuracy':10.0, 'time_to_first_fix': 1000000L, 'cost': 1, 'time_to_next_fix': 1000000L, 'horizontal_accuracy': 10.0, 'power_consumption': 3}, 'technology': 1, 'id': 270526858, 'capabilities': 127, 'location': 1}

These characteristics can be used to compare the available GPS hardware at runtime and select the most appropriate for a specific use case.

In many case we just want to use the device's default module (usually the integrated GPS receiver). We can fetch its id using the default_module() function. The code fragment shows how to use this function to get the default module and information about the default module.

>>> positioning.default_module() #returns the default GPS module id
270526858
>>> positioning.module_info(default_module()) # returns the module details for the default GPS module
{'available': 1, 'status': {'data_quality': 3, 'device_status': 7}, 'version': u'1.00(0)', 'name': u'Integrated GPS', 'position_quality': {'vertical_accuracy':10.0, 'time_to_first_fix': 1000000L, 'cost': 1, 'time_to_next_fix': 1000000L, 'horizontal_accuracy': 10.0, 'power_consumption': 3}, 'technology': 1, 'id': 270526858, 'capabilities': 127, 'location': 1}

Tip.pngTip: The Emulator can be used to test positioning functions. On the Emulator menu do: SimPSYConfigurator | Select Config File| some config files or Tools | Position).

After we have selected a positioning module, we can call position() to get the current position (the default module is used if no positioning module has been specified).

The syntax of position() is:

position( course=0, satellites=0, callback=None, interval=positioning.POSITION_INTERVAL, partial=0 )

When called without any parameters (all parameters are optional), the function blocks until positioning information is available and then returns a dictionary with the format below.

Before using the position() function we must first call set_requestors() on the service. The set_requestors() function specifies the type of requestor (a service or a contact), requestor data (e.g. telephone number) and the format of the requestor data. Most applications use the following (unfortunately the API is very thinly documented):

>>> positioning.set_requestors([{"type":"service","format":"application","data":"gps_app"}]
>>> positioning.position()
{'satellites': None, 'position': {'latitude': 19.120155602532, 'altitude': 16.0, 'vertical_accuracy': 7.5, 'longitude': 72.895265188042, 'horizontal_accuracy': 80.956672668457}, 'course': None}

Though 'satellites' are listed as None in the code above there is, in fact, a satellite signal. If there were no satellite signal there would be no data available and the function would return NaN} (not a number) as shown below:

>>> positioning.position()
{'satellites': None, 'position': {'latitude': NaN, 'altitude': NaN, 'vertical_accuracy': NaN, 'longitude': NaN, 'horizontal_accuracy': NaN}, 'course': None}

To get 'satellites' and 'course' information we must specify the value 1 in the {Icode and satellite parameters passed in. The function then returns information about course and satellites if it is available. If we set the partial parameter to 1 the function may return with incomplete information before the final fix is calculated.

>>> positioning.set_requestors([{"type":"service","format":"application","data":"gps_app"}]
>>> positioning.position(course=1,satellites=1, partial=1)
{'satellites': {'horizontal_dop': 2.86999988555908, 'used_satellites': 4, 'vertical_dop': 0.980000019073486, 'time': 1253539177.0, 'satellites': 11, 'time_dop': 1.69000005722046}, 'position': {'latitude': 19.1201448737, 'altitude': 16.0, 'vertical_accuracy': 6.5, 'longitude': 72.89526787025, 'horizontal_accuracy': 121.031242370605}, 'course': {'speed': NaN, 'heading': NaN, 'heading_accuracy': NaN, 'speed_accuracy': NaN}}

Some applications require occasional position fixes and use position() as shown above. Other applications require a continuous feed of position information at fixed intervals. In this case, we specify an interval in microseconds and a callback function. If a callback is passed as an argument, the position() function completes immediately. The callback function is called immediately with the current position, and is then called repeatedly at the specified time interval. The stop_position() function can be used to stop an on-going positioning request. Note that the callback function is called with the the current position information as parameter; therefore you must ensure your callback function definition has a parameter.

The example below shows a script which uses a callback function to display the position every three seconds.

import positioning, appuifw
 
#callback function
def cb_pos(info):
appuifw.note(unicode(info))
 
#start polling
positioning.set_requestors([{"type":"service","format":"application","data":"gps_app"}]
positioning.position(course=1,satellites=1, callback=cb_pos, interval=3000000, partial=0)


The last function in the positioning module is last_position(). This returns the cached value of the most recent GPS data received (which might be useful if you've started your application inside).

The simple example below demonstrates the use of the positioning module in more detail. The example prints gps_data every second for ten minutes, using a while loop and is well documented in the in-source comments. The main functions are:

  • initialize_gps() - Initializes the GPS by selecting the GPS module, setting requestors and requesting for a fix every 0.5 seconds
  • cb_gps(event) - Call back function which is called every 0.5 seconds
  • stop_gps() - Stops the GPS
import e32, appuifw, positioning
 
def initialize_gps():
'''This function initializes the GPS. The select_module(module_id) can be used to select the GPS module in this function.
In this case we are using the default GPS (integrated GPS) hence we do not need to select it.'''

appuifw.note(u'Intializing GPS')
global gps_data
#Intitialize the global dictionary with some initial dummy value (0.0 in this case)
gps_data = {
'satellites': {'horizontal_dop': 0.0, 'used_satellites': 0, 'vertical_dop': 0.0, 'time': 0.0,'satellites': 0, 'time_dop':0.0},
'position': {'latitude': 0.0, 'altitude': 0.0, 'vertical_accuracy': 0.0, 'longitude': 0.0, 'horizontal_accuracy': 0.0},
'course': {'speed': 0.0, 'heading': 0.0, 'heading_accuracy': 0.0, 'speed_accuracy': 0.0}
}
try:
# Set requesters - it is mandatory to set at least one
positioning.set_requestors([{"type":"service","format":"application","data":"gps_app"}])
# Request for a fix every 0.5 seconds
positioning.position(course=1,satellites=1,callback=cb_gps, interval=500000,partial=0)
# Sleep for 3 seconds for the intitial fix
e32.ao_sleep(3)
except:
appuifw.note(u'Problem with GPS','error')
 
def cb_gps(event):
global gps_data
gps_data = event
 
def stop_gps():
'''Function to stop the GPS'''
try:
positioning.stop_position()
appuifw.note(u'GPS stopped','error')
except:
appuifw.note(u'Problem with GPS','error')
 
#initialize GPS
initialize_gps()
 
#Set time for the script to run. 10 minutes in this case.
time_to_run=10
minutes=time_to_run*60
 
# Print the GPS data for 10 minutes.
while (minutes > 0):
print gps_data['satellites']['used_satellites'], gps_data['position']['latitude'], gps_data['position']['longitude'], gps_data['course']['speed']
minutes=minutes-1
e32.ao_sleep(1)
 
# Stop the on-going request
stop_gps()

Symbian platform devices can also use the positioning module, exactly as described above, to access an external Bluetooth GPS receiver, for example, where the device doesn't have an integrated receiver, or where the external device is more accurate.

Using a socket to access an external receiver

This is a method for accessing GPS data from an external device over Bluetooth. We can use a Bluetooth socket connection to communicate directly with the receiver using "NMEA sentences": http://www.gpsinformation.org/dale/nmea.htm. The format of an NMEA sentence is as shown below:

$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
 
Where:
GGA Global Positioning System Fix Data
123519 Fix taken at 12:35:19 UTC
4807.038,N Latitude 48 deg 07.038' N
01131.000,E Longitude 11 deg 31.000' E
1 Fix quality: 0 = invalid
1 = GPS fix (SPS)
2 = DGPS fix
3 = PPS fix
4 = Real Time Kinematic
5 = Float RTK
6 = estimated (dead reckoning) (2.3 feature)
7 = Manual input mode
8 = Simulation mode
08 Number of satellites being tracked
0.9 Horizontal dilution of position
545.4,M Altitude, Meters, above mean sea level
46.9,M Height of geoid (mean sea level) above WGS84
ellipsoid
(empty field) time in seconds since last DGPS update
(empty field) DGPS station ID number
*47 the checksum data, always begins with *

First, we establish a connection with the Bluetooth GPS receiver using the socket module (the Bluetooth GPS receiver must first be paired with the mobile device).

>>> import socket
>>> address, services = socket.bt_discover() # search for the Bluetooth device
>>> gps_receiver = (address, services.values()[0]) # select the external Bluetooth GPS receiver
>>> connection = socket.socket(socket.AF_BT, socket.SOCK_STREAM)
>>> connection.connect(gps_receiver) # establish connection

Next, we listen to the NMEA sentences transmitted by the external GPS receiver. We split the sentence to obtain the latitude and longitude. Note that Python's "file like sockets" API makes this very easy by providing each sentence as a single line (see chapter 9 for more information).

>>> gps_data = connection.makefile("r", 0)
>>> if (gps_data.startswith("$GPGGA"): # check if gps_data is a valid NMEA sentence. Refer to the sentence syntax above.
gps_data = gps_data.split(",") # string handling
latitude = gps_data[2] # define Latitude
longitude = gps_data[4] # define Longitude
>>> print latitude, longitude

Other positioning technologies

In addition to using the location and positioning module, there are online web services which return the location information for the handset based on its IP address. This information can be obtained using the urllib module as described in chapter 14. The accuracy of these services varies enormously - care should be taken because even though these services may return very detailed information, it is not uncommon for them to be incorrect even at the city level. To find more information on these services, use the term 'geo ip' in your preferred search engine.

Maps with GPS information: Where am I?

In the previous section, we discussed how to get the position of the mobile handset. We can be combined this information with mapping information to create applications that provide a personalized, contextual and compelling experience for the end-user. Not only can we show the user where they are, but we can add features like custom descriptions, URLs, labels, groups, icons and so on.

This section demonstrates a simple mapping application called Where Am I? (originally contributed by Herb Jellinek). The application determines the location of the user and maps it on Google Maps. Where Am I is compatible with devices with an integrated GPS receiver. The application could be made to work with an external Bluetooth GPS receiver or with another web service, such as Yahoo Maps or [ http://api.maps.ovi.com/ Ovi Maps], with minor code modifications,

The application has a minimal user interface which allows us to focus on the positioning and mapping features. It demonstrates good exception handling, default access point selection techniques and debug logging which should provide a useful reference for new Python programmers on the Symbian platform:

Figure 10.4

Let’s dive into the code now! The Where Am I? application is comprised of five files as shown in the following diagram.

Figure 10.5 Where Am I? application

An overview of each file is given in the table below, and their implementations are discussed individually in the following sections.

Table 10.1: Where Am I Source Code Files
File Name Function
main.py The main application script that uses other libraries and displays the map.
lat_lon.py Obtains the latitude and longitude of the current location using the positioning module.
google_maps.py Plots the current location on Google maps.
persist_ap.py Saves the access point for future use.
my_logging.py Adds entries to the debug log.

Obtaining latitude and longitude co-ordinates

The lat_lon.py module provides a wrapper around the positioning module initialization sequence and reduces its API to a few key functions:

  • hasPositioning() returns True if the device can get position information, and False otherwise.
  • getLatLon() returns a tuple containing the latitude and longitude (or (None, None) if positioning isn't supported)
  • getPos() returns a dictionary containing the following attributes (or None if positioning isn't supported):
    • time: <float, Unix time>
    • latitude: <float>
    • longitude: <float>
    • altitude: <float>
    • horizontal_accuracy: <float>
    • vertical_accuracy: <float>

The code is much the same as discussed earlier in this chapter and should be straightforward to understand.

# lat_lon.py
# A simple module that wraps the positioning module to hide its initialization
# sequence and simplify its API to a couple of simple calls.
 
import positioning
 
modules = positioning.modules()
if len(modules) == 0:
 
# There's no way to get our position, so don't try.
def getPos():
return None
# There's no way to get our position, so don't try.
def getLatLon():
return None, None
# There's no way to get our position.
def hasPositioning():
return False
 
else:
 
# select a positioning module (a device might have several)
positioning.select_module(positioning.default_module())
 
# The documentation is completely opaque
# regarding the meanings of the argument to set_requestors.
positioning.set_requestors([{"type": "service",
"format": "application",
"data": "loc"}])
 
# Return the current position of the device.
def getPos():
return positioning.position()['position']
# Return the current position of the device as a simple lat/lon tuple.
def getLatLon():
pos = getPos()
return (pos['latitude'], pos['longitude'])
# Yes, we can do positioning.
def hasPositioning():
return True

Retrieving Google Maps

The google_maps.py module is used to retrieve a Google map for a specified latitude and longitude in a specified image format, and to save it in a temporary file.

Google requires an API key in all requests to the Google Maps API. If you do not have an API Key, you can obtain one from http://code.google.com/apis/maps/signup.html. This API key should be assigned the GMAPS_KEY variable in the example below.

Warning.pngWarning: The Google Maps web API used in Where Am I? is available free for non-commercial use only. You should always read the terms of use carefully before using any web API in your application.

The source code below is well documented. The GoogleMaps class creates a URL (GMAPS_URL) in the correct format for the Google Maps web services API and includes the API Key GMAPS_KEY. It then uses the urllib module to retrieve the map and saves it to a temporary file. The urllib module is discussed in Advanced Network Programming.

# google_maps.py
# Module for retrieving and displaying Google Maps.
#
import urllib
from graphics import Image
# for generating temp file names
import random
 
from my_logging import log
 
# The "key" that lets the Google Maps service know who is
# accessing its service. Treat this as confidential information.
#
# You must obtain your own Google Maps key from
# http://code.google.com/apis/maps/signup.html
#
GMAPS_KEY = "not set yet"
 
if GMAPS_KEY == "not set yet":
print "You must obtain a Google Maps key."
print "See the google_maps.py source code for details."
 
# The format of the images we retrieve. Legal values are gif, jpg,
# jpg-baseline, png8, png32
GOOGLE_IMAGE_FORMAT = "jpg"
 
# Mapping from Google image format name to file extension.
EXTENSIONS = {"gif": "gif", "jpg": "jpg", "jpg-baseline": "jpg",
"png8": "png", "png32": "png"}
 
# Where to store the images we retrieve. Keep it on D:, the temp device.
# We pass this to urllib.urlretrieve, which should have the ability
# to generate its own temp file names, but which is broken in Python on the Symbian platform.
TEMP_FILE = u"D:\\temp"+str(random.randint(0, 10000))+"."+EXTENSIONS[GOOGLE_IMAGE_FORMAT]
 
# The minimum zoom factor
MIN_ZOOM = 0
 
# The maximum zoom factor.
MAX_ZOOM = 19
 
# Base URL to be used to retrieve map images from Google. It's a format
# string with the following fields, in order from left to right:
#
# %f - latitude of center of map
# %f - longitude of center of map
# %d - zoom factor to use, an integer from 0 (entire Earth) to
# 19 (individual buildings)
# %d - image width, in pixels
# %d - image height, in pixels
# %s - map type, a string equal to one of "roadmap" (a standard roadmap image),
# "mobile" (a mobile roadmap map image, with larger features and text),
# "satellite" (a satellite image),
# "terrain" (a physical relief map, showing terrain and vegetation),
# "hybrid" (a hybrid of the satellite and roadmap image).
# %s - markers, a set of marker descriptors as described at
# http://code.google.com/apis/maps/documentation/staticmaps/#Markers
#
GMAPS_URL = "http://maps.google.com/staticmap?center=%f,%f&format="+GOOGLE_IMAGE_FORMAT+
"&zoom=%d&size=%dx%d&maptype=%s&markers=%s&key="+GMAPS_KEY
 
 
#
# An object that can produce a map of the area surrounding a given location.
#
class GoogleMaps:
# Create a new Google Maps map factory. We pass in the desired image
# width and height and map type, as those are likely to be common across
# successive requests. map type is optional and defaults to "mobile".
# marker color is optional and defaults to "green". marker size is
# optional and defaults to "small".
def __init__(self, (width, height), mapType="mobile", markerColor="green",
markerSize="small"):
self.width = width
self.height = height
self.mapType = mapType
self.markerColor = markerColor
self.markerSize = markerSize
 
# Given a sequence of lat-lon pairs, return a string containing
# map markers in the Google Maps format. We use a fixed marker color,
# size, and no character labels.
#
# The string will look like
# markers=markerDescriptor1|markerDescriptor2|markerDescriptor3|... etc.
# where a markerDescription looks like
# latitude,longitude,{size}{color}{alphanumeric-character}
#
# latitude - a latitudinal value with precision to 6 decimal places
# longitude - a longitudinal value with precision to 6 decimal places
# size - the size of marker, one of "tiny", "mid", "small"
# color - a color from the set {black, brown, green, purple, yellow,
# blue, gray, orange, red, white}.
# alphanumeric-character - a single lowercase alphanumeric character
# from the set {a-z, 0-9}. Default and mid sized markers are the
# only ones capable of displaying an alphanumeric-character parameter.
#
# Only the latitude and longitude parameters are required. The others
# are optional.
def makeMarkerString(self, positions):
str = ""
for (lat, lon) in positions:
str = str + ("%f,%f,%s%s|" % (lat, lon, self.markerSize,
self.markerColor))
return str
 
# Set the width and height for all subsequent requests.
def setImageSize(self, pos):
width, height = pos
log("setImageSize(%d,%d)" % (width, height))
self.width = width
self.height = height
 
# Get the map centered at centerLatLon, at zoom factor zoom,
# with markers at positions markerPositions, which may be empty.
def getMapImage(self, centerLatLon, zoom, markerPositions):
lat, lon = centerLatLon
url = (GMAPS_URL % (lat, lon, zoom, self.width, self.height,
self.mapType, self.makeMarkerString(markerPositions)))
log("url = "+url)
file, ignoredHeaders = urllib.urlretrieve(url, TEMP_FILE)
return Image.open(file)

Saving the access point

The Google Maps API is accessed over the Internet, which requires an access point to be specified by the user. In order to minimise the number of times the application has to prompt the user the persist_ap.py module saves the user's preferred access point to a database and, while it remains valid, reuses it for subsequent connections.

The access point selection and persistence logic is as follows (in pseudo code).

Open the access point database.
If it doesn't exist:
Get list of available access points.
If none available:
Display error message.
After user confirms, abort program.
Otherwise:
Display list of available access points.
Get user's selection.
Store it in DB.
Set it as default.
If it does exist:
Get name of access point from DB.
Look up name of access point in list of available ones.
If not found:
(duplicate logic of "If it doesn't exist" case above)
Otherwise:
Set this access point as the default.

We don't reproduce the whole of the persist_ap.py module here. However, we do briefly discuss two important functions: selectAP() and removePersistentAP().

selectAP() allows the user to select a valid access point and saves it in a database (CONN_DB_NAME).

# Database containing connection info
CONN_DB_NAME = u"e:\\python\\conninfo.db"
 
# DB key for the Internet Access Point
IAPID_KEY = u"iapid"
 
def terminalError(msg):
appuifw.note(msg, 'error')
 
#
# Let the user select an AP from the system's list.
# Write it to the DB and return the ID and access point object.
#
def userChooseAndSelectAP(db):
accessPoints = socket.access_points()
if accessPoints and len(accessPoints) > 0:
apID = socket.select_access_point()
ap = socket.access_point(apID)
socket.set_default_access_point(ap)
db[IAPID_KEY] = str(apID)
return apID, ap
else:
terminalError(u"No access points available")
return (None, None)

removePersistentAP() clears the saved access point from the database (CONN_DB_NAME).

#
# Open the connection info DB in the given mode.
# Return the DB object, or return None if it was not possible to open it
# in that mode.
#
def openDB(mode="w"):
try:
db = e32dbm.open(CONN_DB_NAME, mode)
log("opened DB %s mode %s" % (db, mode))
return db
except:
log("openDB %s returns None" % mode)
return None
 
# Remove the Access Point info stored in the database.
# Note that this doesn't affect the application's current default access point.
# A subsequent call to selectAP should pop up the Access Point menu and
# then set the default AP.
#
def removePersistentAP():
db = openDB(mode="w")
try:
del db[IAPID_KEY]
finally:
if db:
db.close()

Debug logging

my_logging.py provides a very simply interface for writing debug statements to a file: (gFile). It is similar to the logging mechanism described in chapter 17, except that it can be turned off, provides a dedicated method for logging exceptions and does not flush pending bytes to the file.

The four logging functions are:

  • disableLogging() - disable logging
  • logException() - log an exception
  • getLogFile() - return path of the log file
  • log(msg) - log msg to the log file
# my_logging.py
# Tiny logging module.
# This abstracts away the means for adding entries to the debug log,
# allowing us to turn it off, for example.
#
 
import time
 
# Log file name - use the .txt extension so Symbian's File Manager app knows
# how to open it, should you want to do that on the handset.
LOG_FILE_NAME = 'e:/Python/my-log.txt'
 
gFile = open(LOG_FILE_NAME, 'w')
 
gEnabled = True
 
def disableLogging():
global gEnabled
gEnabled = False
 
def logException():
import traceback
traceback.print_exc(None, gFile)
 
def getLogFile():
return gFile
 
def log(msg):
global gEnabled
if gEnabled:
logStr = time.strftime('%I:%M:%S: ', time.localtime())+str(msg)+'\n'
gFile.write(logStr)

Using the above libraries in main.py

main.py uses the simple APIs we've created to get the device's latitude and longitude, retrieve a map and then create a canvas to display the map to the user. We also allow the user to zoom in and out using up and down keys and update the map accordingly.

Again the code below is well documented in-source, and is in any case quite straightforward:

# main.py
# Repeatedly fetch our position and display a map of it. Zoom in and out
# using the up and down rocker keys.
#
 
import e32, appuifw
from graphics import Image
from key_codes import *
 
import sys
sys.path.append('e:\Python\lib')
 
import persist_ap
import lat_lon, google_maps
 
from my_logging import log, logException
 
APP_LOCK = e32.Ao_lock()
 
DEFAULT_ZOOM = 17
 
def main():
# Select a network access point
persist_ap.selectAP()
 
appuifw.app.screen='full'
appuifw.exit_key_handler = lambda:APP_LOCK.signal()
 
# This will create our map images.
mapFactory = google_maps.GoogleMaps((240, 320), mapType="satellite")
 
image = None
canvas = None
 
# Make zoom a list so we can set the zoom value from within handleEvent.
# The outer zoom list is available globally because we are using indices and there is no local list.
# We could alternatively have made zoom global
zoom = [DEFAULT_ZOOM]
 
# Set the zoom value. See note above regarding why we do it this way.
def setZoom(newValue):
zoom[0] = newValue
 
# Get the zoom value. See note above regarding why we do it this way.
def getZoom():
return zoom[0]
 
# Draw the map image.
def drawImage(rect):
canvas.blit(image)
canvas.text(((10, 20)),
u"Zoom: %d%%" % ((100.0 * getZoom()) / google_maps.MAX_ZOOM))
 
# Handle keyboard events, like the zoom keys.
def handleEvent(event):
log("handleEvent %s" % event)
try:
if event["type"] == appuifw.EEventKeyUp:
if event["scancode"] == EScancodeUpArrow:
# zoom in (larger factor)
setZoom(min(google_maps.MAX_ZOOM, getZoom() + 1))
elif event["scancode"] == EScancodeDownArrow:
# zoom out (smaller factor)
setZoom(max(google_maps.MIN_ZOOM, getZoom() - 1))
except:
logException()
 
canvas = appuifw.Canvas(redraw_callback=drawImage,
event_callback=handleEvent)
appuifw.app.body = canvas
 
oldLatLon = None
while True:
latLon = lat_lon.getLatLon()
if oldLatLon == latLon:
continue
oldLatLon = latLon
image = mapFactory.getMapImage(latLon, getZoom(), (latLon,))
drawImage(())
 
APP_LOCK.wait()
 
if __name__ == "__main__":
main()

Summary

This chapter illustrated various techniques of getting location information from the device using location and positioning modules. The chapter also highlights a sample application "Where am I", which maps the current location of the device on Google Maps.


Licence icon cc-by-sa 3.0-88x31.png© 2010 Symbian Foundation Limited. Portions copyright Bogdan Galiceanu, Hamish Willee, Marcelo Barros de Almeida, Mike Jipping, Pankaj Nathani and others in wiki document history list. This document is licensed under the Creative Commons Attribution-Share Alike 2.0 license. See http://creativecommons.org/licenses/by-sa/2.0/legalcode for the full terms of the license.
Note that this content was originally hosted on the Symbian Foundation developer wiki.

This page was last modified on 8 May 2013, at 09:29.
131 page views in the last 30 days.