Place Lab: A privacy-observant location system  
Home
Software
Beacon Database
Documentation
Sample Traces
Publications
People
Mailing Lists
Users & Projects
Press
 
Intel Research Seattle
Intel

The Place Lab Developers' Tutorial

This tutorial will provide you with an overview of the Place Lab API and a primer on using it to build location-enhanced applications. If you find it best to begin with examples, the bolded links in the Table of Contents below lead to sections with runnable code from the org.placelab.examples package. The javadocs from each section offer more in depth information.

Table of Contents

  • Setup: Preparing to develop for/with Place Lab.
  • Spotting: The Place Lab interface for sensing the real world.
    • Measurement: The common type that all spotters must produce.
    • BeaconMeasurement: A measurement of a scan for beacons.
    • BeaconReading: An individual sighting of a radio beacon within a BeaconMeasurement.
    • WiFiSpotter: A spotter that scans on 802.11 wireless cards.
    • WiFiSpotterExample: A sample program that shows how to use a WiFiSpotter.
    • RemoteGSMSpotter: A spotter which uses a Bluetooth connection to slave phone to spot GSM.
    • LogSpotter: A class to read Measurements stored in a log file.
    • LogSpotterExample: A sample program that shows how to use a LogSpotter.
    • Other Spotters: Descriptions of some of the other types of Spotters available in Place Lab.
  • Mapping: Looking up the locations of Beacons using the persistent local cache of Beacon data
    • Coordinate: Place Lab's standard representation for a location in the world
    • CoordinateExample: A sample program showing how to use Coordinate and its coordinate frames.
    • Beacon: A generalized representation of a cell-based radio access point tied to a location.
    • Mapper: A class the manages the persistent cache of known beacons
    • MapLoader: An application to load your local mapper from the database at placelab.org and other sources on the web.
    • MapperExample: Shows how to go from the BeaconMeasurement's BeaconReadings to Beacons using the mapper
    • Place Lab Stumbler: A wardriving application to generate trace files for LogSpotters and placelab.org
  • Tracking: Using Measurements, a Mapper, and a little (or a lot of) math to track device location
    • Tracker: A class that aggregates Spotter Measurements into location Estimates.
    • Estimate: A class that includes location information and, possibly, a notion of uncertainty.
    • CentroidTrackerExample: An example using the simplest type of BeaconTracker.
    • ParticleFilterTracker: A Tracker that uses probabilistic techniques and pluggable motion and sensor models.
  • Utility Classes
    • PlacelabWithProxy: This class can create and manage spotters and trackers for an application.
    • PlacelabExample: A sample showing how to use the PlaceLab object and get estimates.
    • Servlets and Proxies: Applications can access location information from Place Lab's web proxy through a common interface. The web proxy's functionality can also be extended with servlets.
    • ServletExample: An example of a servlet that prints the last estimated device position.
  • Maps and Visualizations
    • MapWads: Data files containing maps and places.
    • MapView: An extensible SWT control for displaying maps and overlaying visualizations.
    • MapDemo: An application using a MapView to show the user's location on a map.

Setup

Place Lab code is hosted on Sourceforge and can be accessed via CVS.

Back to Table of Contents

CVS Access

Back to Table of Contents

Development Tools

Most Place Lab developers use the Eclipse IDE and the build is configured to support it. Builds can also be performed using Ant. If you're using Eclipse, make sure and copy the appropriate classpath.* file to be named .classpath in your placelab directory after checking out the module from the CVS repository. If you make any global changes to your .classpath file, make sure you reflect those changes back to the classpath.* files.

Of course, if you are just trying to use Place Lab to build your location-aware applications, you can simply use placelab.jar (along with the necessary support files and native pieces such as spotter.dll) with whatever developement environment you choose.

Building for the phones requires the Series 60 MIDP SDK available from Nokia and you may want to be aware of Known Issues In The Nokia 6600 MIDP 2.0 Implementation v1.3. The Phone Utilities HOWTO Guide describes how to use the Place Lab helper tools like error logging and persistent storage on the phones.

Back to Table of Contents

Directory Structure

The source for Place Lab is divided into two directories: src and srcx. Both are compiled to the bin directory by Eclipse. src (and package org.placelab inside it) is for release-quality code and is included in official releases. srcx (and package org.placelabx inside it) is for research, debug, or otherwise not-ready-for-primetime code. No file in src should ever rely on a file in srcx.

Back to Table of Contents

Spotting

In Place Lab, spotters are the objects that do the environmental sensing (i.e. detect radio beacons) for the system. The root class Spotter.java is an interface that all spotters implement. In many cases, spotters will have a native code component that understands how to reach the specific hardware that does the sensing.

org.placelab.spotter.Spotter
Javadoc
Source
Back to Table of Contents

Measurement

Spotters all must produce readings of type Measurement. Because different spotters can produce readings of very different types, different kids of measurements might have very little in common. The one thing they must have is a timestamp of when the measurement was made. Individual spotters subclass Measurement in order to produce their own type of reading (for example, WiFiSpotter produces a BeaconMeasurement, GPSSpotter produces a GPSMeasurement).

org.placelab.core.Measurement
Javadoc
Source
Back to Table of Contents

BeaconMeasurement

BeaconMeasurements are produced by WiFiSpotter, BluetoothSpotter, GSMSpotter, LogSpotter, and potentially other Spotters. and represent a the result of a single scan of the environment for radio beacons. The measurement includes a set of BeaconReadings for each radio beacon seen that have the unique id as well as the observed signal strength.

org.placelab.core.BeaconMeasurement
Javadoc
Source
Back to Table of Contents

BeaconReading

A BeaconReading represents a sighting of a single wireless beacon from a Spotter that spots for wireless beacons. Spotters typically return BeaconMeasurements that have zero or more BeaconReadings in them

org.placelab.core.BeaconReading
Javadoc
Source
Back to Table of Contents

WiFiSpotter

The WiFiSpotter scans all of the 802.11 wireless cards on the machine and returns the networks that it sees. This always involves running native code. At this point, we have native support for XP, OS X, Pocket PC 2003, and some linux configurations.

org.placelab.spotter.WiFiSpotter
Javadoc
Source
Back to Table of Contents

WiFiSpotterExample

package org.placelab.example;

import org.placelab.core.BeaconMeasurement;
import org.placelab.core.WiFiReading;
import org.placelab.spotter.Spotter;
import org.placelab.spotter.SpotterException;
import org.placelab.spotter.WiFiSpotter;

/**
* A sample that creates a WiFiSpotter and uses it to get measurements.
* This will only return readings if a WiFi card is present.
*/
public class WiFiSpotterExample {

    public static void main(String[] args) {
        
        Spotter s = new WiFiSpotter();
        try {
            s.open();
            BeaconMeasurement m = (BeaconMeasurement) s.getMeasurement();
            System.out.println(m.numberOfReadings() + " APs were seen\n");
            if (m.numberOfReadings() > 0) {
                System.out.println(pad("MAC Address", 20) + pad("SSID", 30)
                        + pad("RSSI", 10));
                // Iterate through the Vector and print the readings
                for (int i = 0; i < m.numberOfReadings(); i++) {
                    WiFiReading r = (WiFiReading) m.getReading(i);
                    System.out.println(pad(r.getId(), 20)
                            + pad(r.getSsid(), 30) + pad("" + r.getRssi(), 10));
                }
            }
        } catch (SpotterException ex) {
            ex.printStackTrace();
        }
    }
    
    // Pad out a string to the passed length
    public static String pad(String str, int len) {
        StringBuffer sb = new StringBuffer(str);
        for (int i=str.length(); i < len; i++) {
            sb.append(" ");
        }
        return sb.toString();
    }
    
}

Back to Table of Contents

RemoteGSMSpotter

If you have a 60 Series cell phone (e.g. Nokia 6600 or 6620) your laptop client can position itself using both GSM and WiFi at the same time; WiFi in the laptop and GSM by the phone relayed to the laptop over Bluetooth. To make this work, you need both a laptop running Place Lab with Bluetooth and a 60 Series cell phone running Place Lab with Bluetooth.

First, you must pair the phone and the laptop. This only needs to be done once, but is a critical step. Run your phone’s Bluetooth utility, discover your laptop, and create a pairing. (This usually involved typing a password-key on both devices). Then on the phone set this laptop as "authorized" to allow it to make connections without having to select accept every time the laptop talks to the phone. Next, as is the case for any of the Place Lab applications on the phone, run the Servers native portion of Place Lab on the phone and go back to the application chooser without exiting the Servers application. Next run the midlet named "BTGSM" on the phone. This starts a Bluetooth server that will relay out GSM beacons to devices that ask for them. You will need to answer "Yes" to two questions. (If, after you start your laptop’s spotter, it asks you for permission more times than twice, you didn't do the first step correctly). When BTGSM first starts up it displays the URL used to access the server. Write down or copy the host address and port number of this URL. (The other parts are the same for all devices). For my phone, for example, this address is: ‘000e6d43ec17:4’. After a few seconds the phone will switch to a different display showing how many readings it has taken, what the GSM coverage has been for the life of the run, and what the current cell tower’s ID is.

The laptop must have a Bluetooth interface and must also have the Microsoft Bluetooth software installed. This software is included as part of Windows XP SP2, but can also be installed separately from SP2. (Visit http://eben.phlegethon.org/bluetooth.html to find out how). RemoteGSMSpotter takes a constructor argument of the address (e.g. 000e6d43ec17:4) that was printed by the cell phone. (This spotter's constructor also takes a second Boolean argument that enables or disables buffering, see the Javadocs for more about buffering). Call open() on the spotter as you would any other and the spotter is ready to go. For several reasons the phone is hard coded to take a GSM reading once a second, so asking for measurements more frequently than that will not result in more data being collected. Here is sample output for org.placelab.samples.RemoteGSMSpotterSample Getting 5 GSM readings from the phone, 2 seconds apart.

Tower ID               Name                                   Signal strength
310:380:1301:52025     AT&T Wirel:1301:52025:1301:52025       78        
310:380:1301:52025     AT&T Wirel:1301:52025:1301:52025       83        
310:380:1301:52025     AT&T Wirel:1301:52025:1301:52025       78        
310:380:1301:52025     AT&T Wirel:1301:52025:1301:52025       75        
310:380:1301:52025     AT&T Wirel:1301:52025:1301:52025       75   
org.placelab.spotter.RemoteGSMSpotter
Javadoc
Source
Back to Table of Contents

LogSpotter

A LogSpotter takes a trace file created by NetStumbler or Place Lab Stumbler and replays it, creating BeaconMeasurements, to simulate a route traveled.

org.placelab.spotter.LogSpotter
Javadoc
Source
Back to Table of Contents

LogSpotterExample

package org.placelab.example;


import org.placelab.core.BeaconReading;
import org.placelab.core.StumblerMeasurement;
import org.placelab.spotter.LogSpotter;
import org.placelab.spotter.SpotterException;
import org.placelab.util.Cmdline;

/** This sample demonstrates the use of the LogSpotter class
*  We show how to parse a large text-exported trace,
*  report the size of the trace and enlist some key values in the log.
*/

public class LogSpotterExample {

    
    public static void main(String [] args)
    {
        Cmdline.parse(args);
        
        String inputFile = Cmdline.getArg("tracefile");
        
        if (inputFile == null) {
            System.err.println("Usage: java " + LogSpotterExample.class.getName() + " --tracefile filename");
            System.exit(1);
        }
        
        LogSpotter log = LogSpotter.newSpotter(inputFile);
        try {
            log.open();
            // output as many chunks as are there in the inputfile
            if(!log.logIsFinished()) do {
                StumblerMeasurement m = (StumblerMeasurement)log
                        .getMeasurement();
                if(m == null) {
                    System.out.println("log is finished");
                    break;
                }
                if (m.numberOfReadings() > 0) {
                    System.out.println(pad("Timestamp", 20)
                            + pad("Latitude", 20) + pad("Longitude", 20)
                            + pad("BSSID", 30) + pad("RSSI", 10));
                    //iterate through the Vector and print the readings
                    for (int i = 0; i < m.numberOfReadings(); i++) {
                        BeaconReading br = (BeaconReading) m
                                .getReading(i);
                        System.out.println(pad(""
                                + (((long) m.getTimestamp() / 1000L) * 1000L),
                                20)
                                + pad(m.getPosition().getLatitudeAsString(),
                                        20)
                                + pad(m.getPosition().getLongitudeAsString(),
                                        20)
                                + pad(br.getId(), 30)
                                + pad("" + br.getNormalizedSignalStrength(), 10));
                    }
                    System.out.println();
                }
            } while(!log.logIsFinished());
        } catch (SpotterException ex) {
            ex.printStackTrace();
        }

    }
    
    public static String pad(String str, int len) {
        StringBuffer sb = new StringBuffer(str);
        for (int i=str.length(); i < len; i++) {
            sb.append(" ");
        }
        return sb.toString();
    }
    
}

Back to Table of Contents

Other Spotters

  • BluetoothSpotter: Scans for Bluetooth devices on systems with JSR-82 implementations.
    org.placelab.spotter.BluetoothSpotter
    Javadoc
    Source
  • NMEAGPSSpotter: Listens to an NMEA GPS device to get GPSMeasurements.
    org.placelab.spotter.NMEAGPSSpotter
    Javadoc
    Source
  • GSMSpotter: Scans for GSM cell towers on phones.
    org.placelab.midp.GSMSpotter
    Javadoc
    Source

Back to Table of Contents

Mapping

Coordinate

Place Lab maps both access points and devices in two dimensions. That is to say, we estimate latitude and longitude, but we do not model or estimate altitude at this point (there is preliminary support for three dimensional coordinates). Coordinate is the class Place Lab uses to represent all of its locations. Coordinates are like regular two dimensional points in that they have an X and Y. X and Y refer to the distance in meters from the "origin" of the Earth. This origin can be pretty much wherever you want, you can change it in CoordinateTranslator. Making the origin close to you makes the meters look reasonable (like 875) rather than huge (like 789674) making debugging easier.

Behind the scenes (in org.placelab.core.Types) Placelab chooses to use TwoDCoordinate or FixedTwoDCoordinate depending on whether it is running on a CLDC 1.0 phone or not. Applications that intend to run on both phones and larger devices will use the String based interface to Coordinate, but applications not targeted at phones can cast any Coordinate to a TwoDCoordinate and use the double based interface.

org.placelab.core.Coordinate
Javadoc
Source
Back to Table of Contents

CoordinateExample

package org.placelab.example;

import org.placelab.core.TwoDCoordinate;


/**
* A sample program that shows how to use 2DCoordinates
*/
public class CoordinateExample {

    public static void main(String[] args) {
        TwoDCoordinate c1,c2,c3;
        
        // The coordinates of Intel Research Seattle
        c1 = new TwoDCoordinate(47.656,-122.318);
        
        // The coordinates of Intel Research Berkeley
        c2 = new TwoDCoordinate(37.87042,-122.26780);
        
        double distance = c1.distanceFrom(c2);
                
        System.out.println("IRS is at ("+c1.getLatitude()+","+c1.getLongitude()+")");
        System.out.println("IRB is at ("+c2.getLatitude()+","+c2.getLongitude()+")");
        System.out.println("IRS and IRB are " + (int)distance/1000 +
                           " km or " + (int)distance/1609 +
                           " miles apart");
        
        // Move c2 5000 meters north
        c2.moveBy(0, 5000);
        
        System.out.println("The point 5 km north of IRB has lat=" +
               c2.getLatitude() + " lon=" + c2.getLongitude());
               
    }
}

Back to Table of Contents

Mapper

The Mapper manages the persistent cache of known access points. The mapper can take BeaconReadings and will return Beacon objects for the BeaconReadings if it knows about them.

org.placelab.mapper.Mapper
Javadoc
Source
Back to Table of Contents

MapLoader

Before running any applications that use the Mapper, you must first load your local cache of Beacons from databases on the web. The MapLoaderGUI utility allows you to select your region and download all the known Beacons for it.

org.placelab.mapper.loader.MapLoaderGUI
Javadoc
Source
Back to Table of Contents

MapperExample

package org.placelab.example;

import org.placelab.core.BeaconMeasurement;
import org.placelab.core.BeaconReading;
import org.placelab.mapper.Beacon;
import org.placelab.mapper.CompoundMapper;
import org.placelab.mapper.Mapper;
import org.placelab.spotter.LogSpotter;
import org.placelab.spotter.Spotter;
import org.placelab.spotter.SpotterException;
import org.placelab.spotter.WiFiSpotter;

/**
* This sample is very similar to CoordinateSample with the addition
* of a lookup in the persistent AP cache
*/
public class MapperExample {

    public static void main(String[] args) {
        Spotter s;
        if(args.length >= 1) {
            s = LogSpotter.newSpotter(args[0]);
        } else {
            s = new WiFiSpotter();
        }
        try {
            s.open();
            BeaconMeasurement m = (BeaconMeasurement) s.getMeasurement();
            Mapper mapper;
            // This Mapper can tell us where APs are
            // The default Mapper (set in PlacelabProperties) will be selected
            // here.  The first argument says to exit on error, and the second
            // says to cache Beacons in memory as they are accessed.
            mapper = CompoundMapper.createDefaultMapper(true, true);
            
            int knownAPs = 0;
            for (int i = 0; i < m.numberOfReadings(); i++) {
                BeaconReading r = (BeaconReading) m.getReading(i);
                // Lets lookup this AP in the map
                Beacon b = (Beacon) mapper.findBeacon(r.getId());
                if (b == null) {
                    System.out.println(r.getId() + " is an unknown AP");
                } else {
                    System.out.println(r.getId() + " (" + r.getHumanReadableName()
                            + ") is thought to be at " + b.getPosition());
                    knownAPs++;
                }
            }
            System.out.println("\nOf the " + m.numberOfReadings() + " APs "
                    + knownAPs + " were known.");
        } catch (SpotterException ex) {
            ex.printStackTrace();
        }
    }
}

Back to Table of Contents

Place Lab Stumbler

Place Lab Stumbler is an application that is used to collect and determine the locations of WiFi, Bluetooth, or GSM beacons in conjunction with a GPS unit. It also can be used to create trace files that can be played back by a LogSpotter and that can be submitted to placelab.org for incorporation into the site database of Beacons. For more information view the laptop stumbling HOWTO document and the phone stumbling HOWTO document.

org.placelab.stumbler.gui.PlacelabStumblerGUI
Javadoc
Source
org.placelab.stumbler.PlacelabStumbler
Javadoc
Source
org.placelab.midp.stumbler.StumblerMidlet
Javadoc
Source

Back to Table of Contents

Tracking

Tracker

Tracker is the abstract super-class for the objects in Place Lab that aggregate measurements from the spotters into a single estimate of device position. Trackers are used in the following way: measurements are fed to the tracker, and the tracker produces estimated locations. Interested objects can also register with trackers to get call-backs when the tracker's estimate changes.

To implement a tracker, you must furnish three functions. The first is the set of measurement types the tracker understands. It is not required that a tracker understand all kinds of measurements. A tracker that can triangulate 802.11 signals, for example, may not understand GPSMeasurements. The second is a method that consumes the measurements coming from the spotter. Thirdly, the system may call a spotter to let it know that some time has elapsed without a measurement occurring. This provides a sophisticated tracker the opportunity to update an estimate based on its prediction of the velocity or path of the device.

org.placelab.client.tracker.Tracker
Javadoc
Source
Back to Table of Contents

Estimate

An Estimate is produced each time a tracker makes a prediction about the device's position. An estimate contains a Coordinate estimate of the device's position. More sophisticated trackers may return subtypes that also include uncertainty and other meta-data.

org.placelab.client.tracker.Estimate
Javadoc
Source
Back to Table of Contents

CentroidTrackerExample

package org.placelab.example;

import org.placelab.client.tracker.Estimate;
import org.placelab.client.tracker.Tracker;
import org.placelab.client.tracker.TwoDPositionEstimate;
import org.placelab.core.BeaconMeasurement;
import org.placelab.core.BeaconReading;
import org.placelab.core.Measurement;
import org.placelab.core.TwoDCoordinate;
import org.placelab.mapper.Beacon;
import org.placelab.mapper.CompoundMapper;
import org.placelab.mapper.Mapper;
import org.placelab.mapper.WiFiBeacon;
import org.placelab.spotter.LogSpotter;
import org.placelab.spotter.Spotter;
import org.placelab.spotter.WiFiSpotter;

/**
* This sample is a tracker that calculates the centroid of the observed
* readings.
*
* This tracker estimates the device position to be the geometric center of the
* readings that have the same timestamp.
*/
public class CentroidTrackerExample extends Tracker {
    /** The mapper to query for information about beacons */
    private Mapper mapper;
    
    private TwoDPositionEstimate estimate;
    
    public CentroidTrackerExample(Mapper m) {
        mapper = m;
    }

    /** return an estimate based on the last set of measurements we saw * */
    public Estimate getEstimate() {
        if (estimate != null) {
            return estimate;
        } else {
            return new TwoDPositionEstimate(getLastUpdatedTime(), TwoDCoordinate.NULL, 0.0);
        }
    }

    /**
     * updateEstimateImpl uses the passed measurement to compute a simple
     * geometic center.
     */
    public void updateEstimateImpl(Measurement m) {
        // we only want BeaconMeasurements
        if (!(m instanceof BeaconMeasurement)) {
            return;
        }
        BeaconMeasurement meas = (BeaconMeasurement) m;

        /* calculate the mean */
        double totalLat=0.0, totalLon=0.0;
        int count=0;
        for (int i=0; i < meas.numberOfReadings(); i++) {
            BeaconReading reading = meas.getReading(i);
            Beacon beacon = mapper.findBeacon(reading.getId());
            if (beacon == null) continue;
            TwoDCoordinate pos = (TwoDCoordinate)((WiFiBeacon)beacon).getPosition();
            totalLat += pos.getLatitude();
            totalLon += pos.getLongitude();
            count++;
        }
        TwoDCoordinate mean = new TwoDCoordinate(totalLat/count, totalLon/count);
        
        /* you can also calculate the standard deviation here.  For this sample,
         * let's ignore it.
         */
        
        estimate = new TwoDPositionEstimate(getLastUpdatedTime(), mean, 0.0);
    }

    //** returns true if m is a wifi measurement **/
    public boolean acceptableMeasurement(Measurement m) {
        return (m instanceof BeaconMeasurement);
    }

    public void updateWithoutMeasurement(long durationMillis) {
    }

    protected void resetImpl() {
        estimate = null;
    }

    public static void main(String[] args) {
        try {
            Spotter spotter;
            Mapper mapper;

            // Create a mapper
            mapper = CompoundMapper.createDefaultMapper(true, true);
            
            // Create a new tracker
            Tracker t = new CentroidTrackerExample(mapper);
            Estimate e1, e2;

            // Create either a live spotter or a log spotter
            if (args.length == 0) {
                spotter = new WiFiSpotter();
            } else {
                spotter = LogSpotter.newSpotter(args[0]);
            }
            spotter.open();

            // Get readings from the spotter
            BeaconMeasurement m = (BeaconMeasurement)spotter.getMeasurement();
            System.out.println("The spotter saw " + m.numberOfReadings() + " readings.");
            t.updateEstimate(m);
            e1 = t.getEstimate();
            System.out.println("Estimated position: " + e1.getCoord());

            // wait around for 2 seconds
            Thread.sleep(2000);

            // Do it all again
            m = (BeaconMeasurement)spotter.getMeasurement();
            System.out.println("The spotter saw " + m.numberOfReadings() + " readings.");
            t.updateEstimate(m);
            e2= t.getEstimate();
            System.out.println("Estimated position: " + e2.getCoord().getLatitudeAsString()
                    + "," + e2.getCoord().getLongitudeAsString());

            
            // Calculate the distance between the 2 readings
            TwoDCoordinate c1 = (TwoDCoordinate)e1.getCoord(),
                c2 = (TwoDCoordinate)e2.getCoord();
            double distance = c1.distanceFrom(c2);
            System.out.println("The distance between estimates is: "
                    + (int) distance + " meters.");

        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }
}

Back to Table of Contents

ParticleFilterTracker

This tracker uses probablistic machine-learning techniques to generate it's predictions of device location. It implements a two dimensional particle filter with motion and sensor models from org.placelab.client.beaconparticlefilter. This tracker produces estimates of type ParticleFilterEstimate that include its confidence in the estimated device position. The documentation for this class and its helper classes is quite thin, and for that we deeply apologize.

org.placelab.client.tracker.ParticleFilterTracker
Javadoc
Source
Back to Table of Contents

Utility Classes

PlacelabWithProxy

The PlacelabWithProxy class is a wrapping of spotter and tracker creation and maintenance. If the no-arg constructor is called, the system creates a WiFiSpotter, a CentroidTracker and the system default Mapper. The spotter is asked for measurements once a second and the measurements are mapped and fed into the tracker, much as our last sample program does. Interested clients can register with the object to receive estimates from the tracker. This object also allows a spotter, tracker and pulse frequency to be passed in on creation time. This allows a log spotter or an alternate tracker to be used instead. Passing in a pulse frequency of -1 will cause the PlacelabWithProxy object to not automatically ask the spotter for readings.

org.placelab.client.PlacelabWithProxy
Javadoc
Source
Back to Table of Contents

PlaceLabExample

package org.placelab.example;

import org.placelab.client.PlacelabWithProxy;
import org.placelab.client.tracker.Estimate;
import org.placelab.client.tracker.EstimateListener;
import org.placelab.client.tracker.Tracker;
import org.placelab.core.Coordinate;
import org.placelab.core.Measurement;
import org.placelab.spotter.LogSpotter;

/**
* This sample shows how to use the PlacelabWithProxy object to manage
* spotters and trackers for you.
*/
public class PlaceLabExample implements EstimateListener {
    Estimate lastEstimate=null;

    public static void main(String[] args) {
        try {
            PlacelabWithProxy placelab;            
            if (args.length == 0) {
                // Create a default placelab (with a live spotter)
                placelab = new PlacelabWithProxy();
            } else {
                // Create a placelab (with an explicit log spotter)
                placelab = new PlacelabWithProxy(LogSpotter.newSpotter(args[0]),
                                        null, // use default tracker
                                        null, // use default mapper
                                        2000  // poll spotter every 2s
                                        );
            }
            placelab.addEstimateListener(new PlaceLabExample());
            placelab.createProxy();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    
    public void estimateUpdated(Tracker t, Estimate e, Measurement m) {
            System.out.println("Estimated position: " + e.getCoord());
            
            if (lastEstimate != null) {
                Coordinate c =  e.getCoord();
                Coordinate last = lastEstimate.getCoord();
                System.out.println("Estimate moved " + c.distanceFromAsString(last) + " meters.");
            }
            lastEstimate = e;
    }
}

Back to Table of Contents

Servlets and Proxies

Place Lab includes an HTTP proxy and servlet engine that allows applications to both create simple web front-ends and to include Place Lab services on remote web servers.

Place Lab communicates location information to web services via a web proxy that that runs on port 2080. This proxy is started when a PlacelabWithProxy object is created and the createProxy() method is called on it. The proxy adds the location information by appending an HTTP request header called X-PlaceLab.Location that has lat,long as its value.

Place Lab also includes a simple servlet engine to allow the application to trap and answer HTTP requests based on their URLs. This trapping and answering allows a program to add simple behaviors with HTML UIs with little effort. The servlet behavior is encapsulated in objects of type Servlet, and they are installed by calling methods in ProxyServletEngine.

To use the proxy server or the servlet engine, set your browser configuration to use http://localhost:2080 as its HTTP proxy.

org.placelab.proxy.Servlet
Javadoc
Source
org.placelab.proxy.CoreServlet
Javadoc
Source
org.placelab.proxy.ProxyServletEngine
Javadoc
Source
Back to Table of Contents

ServletExample

package org.placelab.example;

import java.util.Hashtable;

import org.placelab.client.PlacelabWithProxy;
import org.placelab.client.tracker.Estimate;
import org.placelab.client.tracker.EstimateListener;
import org.placelab.client.tracker.Tracker;
import org.placelab.core.Measurement;
import org.placelab.proxy.HTTPRequest;
import org.placelab.proxy.HTTPResponse;
import org.placelab.proxy.Servlet;
import org.placelab.proxy.ServletServer;
import org.placelab.spotter.LogSpotter;

/**
* This sample creates a placelab object and installs a servlet
* in it.
*/
public class ServletE