Monday 3 November 2008

An Editable Waypoint Layer

A GPS without waypoints is like an Android without Activities, so here I illustrate what needs to be done to add an editable waypoint layer to Android.

First of all we need a waypoint content provider, which is just a wrapper around a database table. I am not planning to elaborate on this here as it is just another content provider.

We start with the map. The idea is just to tap on the map at the location where we want to position the waypoint. However, this conflicts with the usual use of touch to pan the map around. I worked around this by having a button on the map that puts the map in different modes. One is the panning mode, another the editing mode for (right now just) adding waypoints. At startup the map is in panning mode. Once a user clicks the button it toggles into editing mode and the next touch will create a waypoint and start the waypoint editor. This is how this looks:

This is all easily done within the onClick() method on the map:

protected synchronized void setDrawingMode(boolean drawing){
Button button = (Button)findViewById(R.id.modeButton);
if (!drawing){
drawingMode = false;
button.setText("Add Waypoint");
} else {
drawingMode = true;
button.setText("Pan");
}
}

@Override
public void onClick(View v) {
setDrawingMode(!drawingMode);
}


Now my onTouch() method distinguishes between the drawing and panning mode and when in editing mode takes the current location and call the waypoint editor (which is hard-coded here, would be better with intent negotation):

public boolean onTouch(View v, MotionEvent event){
if (drawingMode){
setDrawingMode(false);
MapView mv = (MapView)v;
Projection p = mv.getProjection();
GeoPoint point = p.fromPixels(Math.round(event.getX()), Math.round(event.getY()));
ComponentName comp = new ComponentName(this.getPackageName(), WaypointEditor.class.getName());
Intent intent = new Intent().setComponent(comp);
intent.putExtra(WaypointEditor.LAT, point.getLatitudeE6());
intent.putExtra(WaypointEditor.LON, point.getLongitudeE6());
startActivity(intent);
return true;
} else {
return false;
}
}

You might have noticed that I set the drawing mode to false as soon as the onTouch() call happens. This is because quite often I get more than one touch event from what my clumsy fingers believe is a single tap. We catch only the first, the last ones will be translated into panning events again.

I will not show the waypoint editor here, but waypoints are shown through an ItemizedOverlay, more or less as it comes out of the box. But waypoints should be editable as well, so I override the onTap() method on the ItemizedOverlay:

@Override
protected boolean onTap(int index) {
Waypoint i = items.get(index);
ComponentName comp = new ComponentName(WaypointEditor.class.getPackage().getName(), WaypointEditor.class.getName());
Intent intent = new Intent().setComponent(comp);
intent.putExtra(WaypointEditor.ID, i.id());
context.startActivity(intent);
return true;
}

1 comment:

apilyugina said...

Thanks for the great explanation!one more article on topic http://www.enterra-inc.com/techzone/gps-development-issues-on-android/