// GPSTest2 -- a program that tests your GPS using the Java ME Location API // By Thomas Padron-McCarthy (padrone@lysator.liu.se) // Latest change to this file: July 23, 2008 // No copyright. No warranty. No nothing. Share and enjoy! import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import javax.microedition.location.*; import java.util.Date; import java.util.Vector; import java.util.Enumeration; // An Alert, with an image and a static show method class MyAlert extends Alert { private static Image image = null; public MyAlert(String title, String text) { super(title, text, null, null); if (image == null) { try { image = Image.createImage("/warning.png"); } catch (java.io.IOException e) { // Ok, the image didn't work. Ignore it. } } setImage(image); setTimeout(Alert.FOREVER); } public static void show(String title, String text, Display display) { MyAlert alert = new MyAlert(title, text); display.setCurrent(alert); } } // Used by YesOrNoDialogue to report the user's answer to the caller interface YesOrNoCallback { public void gotAnswer(boolean answer); } // A dialogue that waits for a "yes" or "no" answer from the user class YesOrNoDialogue extends Form implements CommandListener { private Command yes_command = new Command("Yes", Command.OK, 0); private Command no_command = new Command("No", Command.CANCEL, 0); YesOrNoCallback callback; private Display display; public YesOrNoDialogue(String question, YesOrNoCallback callback, Display display) { super("Question"); this.callback = callback; this.display = display; addCommand(yes_command); addCommand(no_command); setCommandListener(this); // StringItem question_item = new StringItem("Question:", question); StringItem question_item = new StringItem(null, question); question_item.setLayout(Item.LAYOUT_CENTER | Item.LAYOUT_VCENTER); append(question_item); } public void commandAction(Command c, Displayable s) { if (c == no_command) { callback.gotAnswer(false); } else if (c == yes_command) { callback.gotAnswer(true); } else { MyAlert.show("Internal error", "Internal error. This can't happen.", display); } } } // YesOrNoDialogue // A Form that shows data about a LocationProvider object class LocationProviderForm extends Form implements CommandListener { private Command refresh_command = new Command("Refresh", Command.SCREEN, 0); private Command back_command = new Command("Back", Command.BACK, 0); private LocationProvider lp; private Display display; private Displayable window_under; public LocationProviderForm(LocationProvider lp, Display display, Displayable window_under) { super("LocationProvider"); this.lp = lp; this.display = display; this.window_under = window_under; refresh(); addCommand(refresh_command); addCommand(back_command); setCommandListener(this); } public void setLocationProvider(LocationProvider lp) { this.lp = lp; refresh(); } public void commandAction(Command c, Displayable s) { if (c == refresh_command) { refresh(); } else if (c == back_command) { display.setCurrent(window_under); } else { MyAlert.show("Internal error", "Internal error. This can't happen.", display); } } public void refresh() { deleteAll(); Runnable r = new Runnable() { public void run() { StringItem object_string_item = new StringItem("Object: ", "" + lp); append(object_string_item); if (lp == null) return; // getState might hang, so this must be in a separate thread int state = lp.getState(); String state_string = "" + state + " ("; switch (state) { case LocationProvider.AVAILABLE: state_string += "AVAILABLE"; break; case LocationProvider.OUT_OF_SERVICE: state_string += "OUT_OF_SERVICE"; break; case LocationProvider.TEMPORARILY_UNAVAILABLE: state_string +="TEMPORARILY_UNAVAILABLE"; break; default: state_string += "unknonw"; break; } state_string += ")"; StringItem state_string_item = new StringItem("State: ", state_string); append(state_string_item); Location last_location = LocationProvider.getLastKnownLocation(); StringItem last_location_item = new StringItem("Last location: ", "" + last_location); append(last_location_item); } // run }; Thread t = new Thread(r); t.start(); } // refresh } // LocationProviderForm // A Form that shows data about a Location object class LocationForm extends Form implements CommandListener { private Command refresh_command = new Command("Get location", Command.SCREEN, 2); private Command back_command = new Command("Back", Command.BACK, 2); private Command mark_command = new Command("Mark!", Command.SCREEN, 1); private Location location; private Display display; private Displayable window_under; private GPSTest2 midlet; public LocationForm(Location location, Display display, Displayable window_under, GPSTest2 midlet) { super("Location"); this.location = location; this.display = display; this.window_under = window_under; this.midlet = midlet; refresh(); addCommand(refresh_command); addCommand(back_command); addCommand(mark_command); setCommandListener(this); } public void setLocation(Location location) { this.location = location; refresh(); } public void commandAction(Command c, Displayable s) { if (c == refresh_command) midlet.get_location(); else if (c == back_command) { display.setCurrent(window_under); } else if (c == mark_command) { midlet.mark(); } else { MyAlert.show("Internal error", "Internal error. This can't happen.", display); } } public void refresh() { deleteAll(); Runnable r = new Runnable() { public void run() { StringItem object_item = new StringItem("Object: ", "" + location); object_item.setLayout(Item.LAYOUT_LEFT | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER); append(object_item); if (location == null) return; boolean valid = location.isValid(); StringItem valid_item = new StringItem("Valid:", "" + valid); valid_item.setLayout(Item.LAYOUT_LEFT | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER); append(valid_item); if (!valid) return; QualifiedCoordinates qc = location.getQualifiedCoordinates(); double lat = qc.getLatitude(); double lon = qc.getLongitude(); String lat_string = Coordinates.convert(lat, Coordinates.DD_MM_SS); String lon_string = Coordinates.convert(lon, Coordinates.DD_MM_SS); StringItem lat_item = new StringItem("Latitude: ", lat_string); lat_item.setLayout(Item.LAYOUT_LEFT | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER); append(lat_item); StringItem lon_item = new StringItem("Longitude: ", lon_string); lon_item.setLayout(Item.LAYOUT_LEFT | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER); append(lon_item); StringItem h_a_item = new StringItem("Horizontal accuracy:", "" + qc.getHorizontalAccuracy()); h_a_item.setLayout(Item.LAYOUT_LEFT | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER); append(h_a_item); StringItem v_a_item = new StringItem("Vertical accuracy:", "" + qc.getVerticalAccuracy()); v_a_item.setLayout(Item.LAYOUT_LEFT | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER); append(v_a_item); StringItem altitude_item = new StringItem("Altitude:", "" + qc.getAltitude()); altitude_item.setLayout(Item.LAYOUT_LEFT | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER); append(altitude_item); long timestamp = location.getTimestamp(); Date date = new Date(timestamp); String date_string = date.toString(); StringItem timestamp_item = new StringItem("Timestamp:", "" + timestamp + " (" + date_string + ")"); timestamp_item.setLayout(Item.LAYOUT_LEFT | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER); append(timestamp_item); StringItem course_item = new StringItem("Course:", "" + location.getCourse()); course_item.setLayout(Item.LAYOUT_LEFT | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER); append(course_item); StringItem speed_item = new StringItem("Speed: ", "" + location.getSpeed()); speed_item.setLayout(Item.LAYOUT_LEFT | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER); append(speed_item); int method = location.getLocationMethod(); String method_string = ""; if ((method & Location.MTA_ASSISTED) != 0) method_string += "MTA_ASSISTED"; if ((method & Location.MTA_UNASSISTED) != 0) method_string += "MTA_UNASSISTED"; /* -- These constants are in the docs, but missing in WTK 2.5.2 for Linux. if ((method & Location.MTA_ANGLEOFARRIVAL) != 0) method_string += "MTA_ANGLEOFARRIVAL"; if ((method & Location.MTA_CELLID) != 0) method_string += "MTA_CELLID"; if ((method & Location.MTA_SATELLITE) != 0) method_string += "MTA_SATELLITE"; if ((method & Location.MTA_SHORTRANGE) != 0) method_string += "MTA_SHORTRANGE"; if ((method & Location.MTA_TIMEDIFFERENCE) != 0) method_string += "MTA_TIMEDIFFERENCE"; if ((method & Location.MTA_TIMEOFARRIVAL) != 0) method_string += "MTA_TIMEOFARRIVAL"; if ((method & Location.MTA_NETWORKBASED) != 0) method_string += "MTA_NETWORKBASED"; if ((method & Location.MTA_TERMINALBASED) != 0) method_string += "MTA_TERMINALBASED"; */ if (method_string.equals("")) method_string = "none"; StringItem method_item = new StringItem("Location method:", method + " (" + method_string + ")"); method_item.setLayout(Item.LAYOUT_LEFT | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER); append(method_item); } // run }; Thread t = new Thread(r); t.start(); } // refresh } // LocationForm // A Form that shows, and lets the user edit, data about a Criteria object class CriteriaForm extends Form implements CommandListener { private Command save_command = new Command("Save", Command.OK, 0); private Command back_command = new Command("Back", Command.BACK, 0); private Command reset_command = new Command("Reset", Command.SCREEN, 0); private Criteria criteria; private Display display; private Displayable window_under; private TextField h_a_item; private TextField v_a_item; private TextField time_item; private ChoiceGroup power_item; private ChoiceGroup cost_item; private ChoiceGroup speed_item; private ChoiceGroup altitude_item; private ChoiceGroup address_item; public CriteriaForm(Criteria criteria, Display display, Displayable window_under) { super("Criteria"); this.criteria = criteria; this.display = display; this.window_under = window_under; h_a_item = new TextField("Horizontal accuracy (in meters)", "", 30, TextField.NUMERIC); append(h_a_item); v_a_item = new TextField("Veritical accuracy (in meters):", "", 30, TextField.NUMERIC); append(v_a_item); time_item = new TextField("Preferred response time (in seconds):", "", 30, TextField.DECIMAL); append(time_item); power_item = new ChoiceGroup("Power consumption:", ChoiceGroup.EXCLUSIVE); power_item.append("No requirements", null); power_item.append("Only low power consumption allowed", null); power_item.append("Average power consumption allowed", null); power_item.append("High power consumption allowed", null); append(power_item); cost_item = new ChoiceGroup("Cost allowed:", ChoiceGroup.MULTIPLE); cost_item.append("Yes, allowed", null); append(cost_item); speed_item = new ChoiceGroup("Speed and course required:", ChoiceGroup.MULTIPLE); speed_item.append("Yes, required", null); append(speed_item); altitude_item = new ChoiceGroup("Altitude required:", ChoiceGroup.MULTIPLE); altitude_item.append("Yes, required", null); append(altitude_item); address_item = new ChoiceGroup("Address info required:", ChoiceGroup.MULTIPLE); address_item.append("Yes, required", null); append(address_item); load_values(); addCommand(save_command); addCommand(back_command); addCommand(reset_command); setCommandListener(this); } public void setCriteria(Criteria criteria) { this.criteria = criteria; load_values(); } public void commandAction(Command c, Displayable s) { if (c == save_command) { save_values(); display.setCurrent(window_under); } else if (c == back_command) { display.setCurrent(window_under); } else if (c == reset_command) { load_values(); } else { MyAlert.show("Internal error", "This can't happen.", display); } } public void load_values() { h_a_item.setString("" + criteria.getHorizontalAccuracy()); v_a_item.setString("" + criteria.getVerticalAccuracy()); time_item.setString("" + criteria.getPreferredResponseTime() / 1000.0); int power = criteria.getPreferredPowerConsumption(); switch (power) { case Criteria.NO_REQUIREMENT: power_item.setSelectedIndex(0, true); break; case Criteria.POWER_USAGE_LOW: power_item.setSelectedIndex(1, true); break; case Criteria.POWER_USAGE_MEDIUM: power_item.setSelectedIndex(2, true); break; case Criteria.POWER_USAGE_HIGH: power_item.setSelectedIndex(3, true); break; default: MyAlert.show("Internal error", "This can't happen.", display); break; } cost_item.setSelectedIndex(0, criteria.isAllowedToCost()); speed_item.setSelectedIndex(0, criteria.isSpeedAndCourseRequired()); altitude_item.setSelectedIndex(0, criteria.isAltitudeRequired()); address_item.setSelectedIndex(0, criteria.isAddressInfoRequired()); } public void save_values() { try { criteria.setHorizontalAccuracy(Integer.parseInt(h_a_item.getString())); criteria.setVerticalAccuracy(Integer.parseInt(v_a_item.getString())); criteria.setPreferredResponseTime((int)(Double.parseDouble(time_item.getString()) * 1000)); switch (power_item.getSelectedIndex()) { case 0: criteria.setPreferredPowerConsumption(Criteria.NO_REQUIREMENT); break; case 1: criteria.setPreferredPowerConsumption(Criteria.POWER_USAGE_LOW); break; case 2: criteria.setPreferredPowerConsumption(Criteria.POWER_USAGE_MEDIUM); break; case 3: criteria.setPreferredPowerConsumption(Criteria.POWER_USAGE_HIGH); break; default: MyAlert.show("Internal error", "This can't happen.", display); break; } boolean[] flag_array = new boolean[1]; cost_item.getSelectedFlags(flag_array); criteria.setCostAllowed(flag_array[0]); speed_item.getSelectedFlags(flag_array); criteria.setSpeedAndCourseRequired(flag_array[0]); altitude_item.getSelectedFlags(flag_array); criteria.setAltitudeRequired(flag_array[0]); address_item.getSelectedFlags(flag_array); criteria.setAddressInfoRequired(flag_array[0]); } catch (NumberFormatException e) { MyAlert.show("NumberFormatException", "Not the right kind of number. This can't happen.", display); } catch (Throwable e) { MyAlert.show("Throwable", "Some other error. Try again.", display); } } } // CriteriaForm // A Canvas that keeps a list of marked locations, and shows them as map, // along with their accuracy cirles. // There is also a current location, which is also shown on the map. // Will probably not work if the map spans the poles, or the 180 meridian. // Will probably give large errors if the points span a large area (thousands of miles). class MapCanvas extends Canvas { private Display display; private Vector points = new Vector(); double min_lat, max_lat, min_lon, max_lon; private Location current_location; public MapCanvas(Display display) { this.display = display; } public void setLocation(Location location) { if (location == null || !location.isValid()) { return; } this.current_location = location; repaint(); QualifiedCoordinates qc = location.getQualifiedCoordinates(); double lat = qc.getLatitude(); double lon = qc.getLongitude(); double accuracy = qc.getHorizontalAccuracy(); /* -- The author is too lazy to write the code to -- translate the accuracies (in meters) to latitudes and longitudes, -- so we don't adjust the map so it always shows all of the accuracy circles. -- The following code is completely wrong. if (first_point || lat - accuracy < min_lat) min_lat = lat - accuracy; if (first_point || lat + accuracy > max_lat) max_lat = lat + accuracy; if (first_point || lon - accuracy < min_lon) min_lon = lon - accuracy; if (first_point || lon + accuracy > max_lon) max_lon = lon + accuracy; */ boolean first_point = (points.size() == 0); if (first_point || lat < min_lat) min_lat = lat; if (first_point || lat > max_lat) max_lat = lat; if (first_point || lon < min_lon) min_lon = lon; if (first_point || lon > max_lon) max_lon = lon; } public Vector get_points() { return points; } public void add_point(Location location) { if (location == null || !location.isValid()) { MyAlert.show("Not a valid location", "No valid current location available.", display); return; } boolean first_point = (points.size() == 0); points.addElement(location); QualifiedCoordinates qc = location.getQualifiedCoordinates(); double lat = qc.getLatitude(); double lon = qc.getLongitude(); double accuracy = qc.getHorizontalAccuracy(); /* if (first_point || lat - accuracy < min_lat) min_lat = lat - accuracy; if (first_point || lat + accuracy > max_lat) max_lat = lat + accuracy; if (first_point || lon - accuracy < min_lon) min_lon = lon - accuracy; if (first_point || lon + accuracy > max_lon) max_lon = lon + accuracy; */ if (first_point || lat < min_lat) min_lat = lat; if (first_point || lat > max_lat) max_lat = lat; if (first_point || lon < min_lon) min_lon = lon; if (first_point || lon > max_lon) max_lon = lon; repaint(); } private int round(double d) { return (int)(d + 0.5); } public final int margin = 10; public void paint(Graphics g) { System.out.println("*** Painting...."); int width = getWidth(); int height = getHeight(); g.setColor(0xffffff); g.fillRect(0, 0, width, height); Coordinates lower_left_corner = new Coordinates(min_lat, min_lon, 0); Coordinates upper_left_corner = new Coordinates(max_lat, min_lon, 0); Coordinates lower_right_corner = new Coordinates(min_lat, max_lon, 0); Coordinates upper_right_corner = new Coordinates(max_lat, max_lon, 0); double lat_side = max_lat - min_lat; double lon_side = max_lon - min_lon; double lat_side_meter = lower_left_corner.distance(upper_left_corner); double lon_side_meter = lower_left_corner.distance(lower_right_corner); double pixels_per_lat_meter = (width - 2 * margin) / lat_side_meter; double pixels_per_lon_meter = (height - 2 * margin) / lon_side_meter; System.out.println("pixels_per_lat_meter = " + pixels_per_lat_meter); System.out.println("pixels_per_lon_meter = " + pixels_per_lon_meter); double pixels_per_meter = Math.min(pixels_per_lat_meter, pixels_per_lon_meter); if (lat_side_meter * pixels_per_meter > height - 2 * margin) { pixels_per_meter = (height - 2 * margin) / lat_side_meter; } if (lon_side_meter * pixels_per_meter > width - 2 * margin) { pixels_per_meter = (width - 2 * margin) / lon_side_meter; } System.out.println("pixels_per_meter = " + pixels_per_meter); double lat_side_pixels = lat_side_meter * pixels_per_meter; double lon_side_pixels = lon_side_meter * pixels_per_meter; double pixels_per_lat = lat_side_pixels / lat_side; double pixels_per_lon = lon_side_pixels / lon_side; System.out.println("pixels_per_lat = " + pixels_per_lat); System.out.println("pixels_per_lon = " + pixels_per_lon); // First, draw the error circles Enumeration e = points.elements(); while (e.hasMoreElements()) { Location location = (Location)e.nextElement(); QualifiedCoordinates qc = location.getQualifiedCoordinates(); double lat = qc.getLatitude(); double lon = qc.getLongitude(); double accuracy = qc.getHorizontalAccuracy(); double x = margin + (lon - min_lon) * pixels_per_lon; double y = margin + (max_lat - lat) * pixels_per_lat; double radius = accuracy * pixels_per_meter; g.setColor(0x00ff00); g.fillArc(round(x - radius), round(y - radius), round(2 * radius), round(2 * radius), 0, 360); } // Second, draw points and lines e = points.elements(); int prev_x = 0; int prev_y = 0; boolean has_previous = false; while (e.hasMoreElements()) { Location location = (Location)e.nextElement(); QualifiedCoordinates qc = location.getQualifiedCoordinates(); double lat = qc.getLatitude(); double lon = qc.getLongitude(); double x = margin + (lon - min_lon) * pixels_per_lon; double y = margin + (max_lat - lat) * pixels_per_lat; int ix = round(x); int iy = round(y); g.setColor(0x000000); g.fillArc(ix - 4, iy - 4, 8, 8, 0, 360); if (has_previous) { g.setColor(0x000000); g.drawLine(ix, iy, prev_x, prev_y); } has_previous = true; prev_x = ix; prev_y = iy; } // Third, draw current location, if avaliable if (current_location != null) { QualifiedCoordinates qc = current_location.getQualifiedCoordinates(); double lat = qc.getLatitude(); double lon = qc.getLongitude(); double accuracy = qc.getHorizontalAccuracy(); double x = margin + (lon - min_lon) * pixels_per_lon; double y = margin + (max_lat - lat) * pixels_per_lat; double radius = accuracy * pixels_per_meter; g.setColor(0xff0000); g.fillArc(round(x) - 4, round(y) - 4, 8, 8, 0, 360); g.drawArc(round(x - radius), round(y - radius), round(2 * radius), round(2 * radius), 0, 360); } // Fourth, draw a map scale g.setColor(0x0000ff); double max_scale = (width - 2 * margin) / pixels_per_meter; int scale_length = 0; System.out.println("*** max_scale = " + max_scale + " m"); if (max_scale <= 1) { // No scale! } else if (max_scale < 10) { scale_length = round(1 * pixels_per_meter); g.drawString("1 m", width - margin - scale_length / 2, height - 2 * margin, Graphics.BOTTOM | Graphics.HCENTER); } else if (max_scale < 100) { scale_length = round(10 * pixels_per_meter); g.drawString("10 m", width - margin - scale_length / 2, height - 2 * margin, Graphics.BOTTOM | Graphics.HCENTER); } else if (max_scale < 1000) { scale_length = round(100 * pixels_per_meter); g.drawString("100 m", width - margin - scale_length / 2, height - 2 * margin, Graphics.BOTTOM | Graphics.HCENTER); } else if (max_scale < 10000) { scale_length = round(1000 * pixels_per_meter); g.drawString("1 km", width - margin - scale_length / 2, height - 2 * margin, Graphics.BOTTOM | Graphics.HCENTER); } else { scale_length = round(10000 * pixels_per_meter); g.drawString("10 km", width - margin - scale_length / 2, height - 2 * margin, Graphics.BOTTOM | Graphics.HCENTER); } if (scale_length != 0) { g.drawLine(width - margin - scale_length, height - 2 * margin, width - margin - scale_length, height); g.drawLine(width - margin - scale_length, height - margin, width - margin, height - margin); g.drawLine(width - margin, height - 2 * margin, width - margin, height); } System.out.println("*** Done painting."); } // paint } // MapCanvas // A Form that shows a help text for this program class HelpForm extends Form implements CommandListener { private Command back_command = new Command("Back", Command.BACK, 0); private Display display; private Displayable window_under; public HelpForm(Display display, Displayable window_under) { super("Instructions"); this.display = display; this.window_under = window_under; addCommand(back_command); setCommandListener(this); StringItem help_item = new StringItem(null, "If you want, you can first edit the criteria for the location provider. " + "Then, before you can retrieve any GPS locations, you must get a location provider. " + "When you have a location provider, you can either get a single location at a time, " + "or start listening to location updates. " + "You can inspect the current location provider, " + "and the current (i. e., latest retrieved) location. " + "Marked locations, and the current location, are shown on the map, with error margins."); append(help_item); } public void commandAction(Command c, Displayable s) { if (c == back_command) { display.setCurrent(window_under); } else { MyAlert.show("Internal error", "Internal error. This can't happen.", display); } } } // HelpForm // A Form that shows some information about this program class AboutForm extends Form implements CommandListener { private Command back_command = new Command("Back", Command.BACK, 0); private Display display; private Displayable window_under; public AboutForm(Display display, Displayable window_under) { super("About GPSTest2"); this.display = display; this.window_under = window_under; addCommand(back_command); setCommandListener(this); StringItem info_item = new StringItem(null, "This is GPSTest2, a program that tests your GPS using the Java ME Location API. " + "It is intended not so much for end-users, " + "as for programmers who want to test how the GPS, and the Location interface, " + "behaves, in general and on a particular device."); append(info_item); StringItem version_item = new StringItem("Version:", "0.1 (July 23, 2008)"); append(version_item); StringItem author_item = new StringItem("Author:", "Thomas Padron-McCarthy"); append(author_item); StringItem mail_item = new StringItem("E-mail:", "padrone@lysator.liu.se"); append(mail_item); } public void commandAction(Command c, Displayable s) { if (c == back_command) { display.setCurrent(window_under); } else { MyAlert.show("Internal error", "Internal error. This can't happen.", display); } } } // AboutForm // A Form that shows data about the marked points class CalcForm extends Form implements CommandListener { private Command refresh_command = new Command("Refresh", Command.SCREEN, 0); private Command back_command = new Command("Back", Command.BACK, 0); private Display display; private MapCanvas map; public CalcForm(Display display, MapCanvas map) { super("Marked points, so far"); this.display = display; this.map = map; refresh(); addCommand(refresh_command); addCommand(back_command); setCommandListener(this); } public void commandAction(Command c, Displayable s) { if (c == refresh_command) { refresh(); } else if (c == back_command) { display.setCurrent(map); } else { MyAlert.show("Internal error", "Internal error. This can't happen.", display); } } public void refresh() { deleteAll(); Vector points = map.get_points(); int nr_points = points.size(); StringItem nr_string_item = new StringItem("Marked points: ", "" + nr_points); append(nr_string_item); if (nr_points == 0) return; double latsum = 0; double lonsum = 0; QualifiedCoordinates[] coordinates_array = new QualifiedCoordinates[nr_points]; int nr_coordinates = 0; Enumeration e = points.elements(); while (e.hasMoreElements()) { Location location = (Location)e.nextElement(); QualifiedCoordinates qc = location.getQualifiedCoordinates(); double lat = qc.getLatitude(); double lon = qc.getLongitude(); latsum += lat; lonsum += lon; coordinates_array[nr_coordinates++] = location.getQualifiedCoordinates(); } double avg_lat = latsum / nr_points; double avg_lon = lonsum / nr_points; String avg_lat_string = Coordinates.convert(avg_lat, Coordinates.DD_MM_SS); String avg_lon_string = Coordinates.convert(avg_lon, Coordinates.DD_MM_SS); StringItem avg_lat_string_item = new StringItem("Average latitude: ", avg_lat_string); append(avg_lat_string_item); StringItem avg_lon_string_item = new StringItem("Average longitude: ", avg_lon_string); append(avg_lon_string_item); // Sort the array of coordinates, with those with better horizontal accuracy first for (int nr_sorted = 0; nr_sorted < nr_coordinates - 1; ++nr_sorted) { int lowest_so_far = nr_sorted; for (int i = nr_sorted + 1; i < nr_coordinates - 1; ++i) { if (coordinates_array[i].getHorizontalAccuracy() < coordinates_array[lowest_so_far].getHorizontalAccuracy()) lowest_so_far = i; } QualifiedCoordinates temp = coordinates_array[nr_sorted]; coordinates_array[nr_sorted] = coordinates_array[lowest_so_far]; coordinates_array[lowest_so_far] = temp; } // Remove the 10 percent with worst accuracy nr_coordinates *= 0.9; // Find the new average point, with those accuracy-outliers removed latsum = 0; lonsum = 0; for (int i = 0; i < nr_coordinates; ++i) { QualifiedCoordinates qc = coordinates_array[i]; double lat = qc.getLatitude(); double lon = qc.getLongitude(); latsum += lat; lonsum += lon; } avg_lat = latsum / nr_coordinates; avg_lon = lonsum / nr_coordinates; Coordinates avg_point = new Coordinates(avg_lat, avg_lon, 0); // Sort the array of coordinates, with those closer to the average point first for (int nr_sorted = 0; nr_sorted < nr_coordinates - 1; ++nr_sorted) { int closest_so_far = nr_sorted; double least_distance_so_far = avg_point.distance(coordinates_array[closest_so_far]); for (int i = nr_sorted + 1; i < nr_coordinates - 1; ++i) { double this_distance = avg_point.distance(coordinates_array[i]); if (this_distance < least_distance_so_far) { closest_so_far = i; least_distance_so_far = this_distance; } } QualifiedCoordinates temp = coordinates_array[nr_sorted]; coordinates_array[nr_sorted] = coordinates_array[closest_so_far]; coordinates_array[closest_so_far] = temp; } // Remove the 10 percent furthest away from the average point nr_coordinates *= 0.9; StringItem used_string_item = new StringItem("Now using points: ", "" + nr_coordinates); append(used_string_item); // Find the new average point, with both accuracy-outliers and position-outliers removed latsum = 0; lonsum = 0; for (int i = 0; i < nr_coordinates; ++i) { QualifiedCoordinates qc = coordinates_array[i]; double lat = qc.getLatitude(); double lon = qc.getLongitude(); latsum += lat; lonsum += lon; } avg_lat = latsum / nr_coordinates; avg_lon = lonsum / nr_coordinates; avg_lat_string = Coordinates.convert(avg_lat, Coordinates.DD_MM_SS); avg_lon_string = Coordinates.convert(avg_lon, Coordinates.DD_MM_SS); avg_lat_string_item = new StringItem("Average latitude: ", avg_lat_string); append(avg_lat_string_item); avg_lon_string_item = new StringItem("Average longitude: ", avg_lon_string); append(avg_lon_string_item); } // refresh } // CalcForm // A Form that shows data about the orientation of the device class OrientationForm extends Form implements CommandListener { private Command refresh_command = new Command("Refresh", Command.SCREEN, 0); private Command back_command = new Command("Back", Command.BACK, 0); private Command auto_command = new Command("Auto-refresh", Command.SCREEN, 0); private Command stop_command = new Command("Stop", Command.SCREEN, 0); private Display display; private MapCanvas map; private boolean keep_refreshing = false; StringItem azimuth_item = new StringItem("Compass Azimuth: ", ""); StringItem pitch_item = new StringItem("Pitch: ", ""); StringItem roll_item = new StringItem("Roll: ", ""); StringItem magnetic_item = new StringItem("Relative to: ", ""); public OrientationForm(Display display, MapCanvas map) { super("Orientation"); this.display = display; this.map = map; append(azimuth_item); append(pitch_item); append(roll_item); append(magnetic_item); keep_refreshing = false; refresh(); addCommand(refresh_command); addCommand(back_command); addCommand(auto_command); addCommand(stop_command); setCommandListener(this); } public void commandAction(Command c, Displayable s) { if (c == refresh_command) { keep_refreshing = false; refresh(); } else if (c == back_command) { keep_refreshing = false; display.setCurrent(map); } else if (c == auto_command) { keep_refreshing = true; refresh(); } else if (c == stop_command) { keep_refreshing = false; } else { MyAlert.show("Internal error", "Internal error. This can't happen.", display); } } public void refresh() { Runnable r = new Runnable() { public void run() { try { do { Orientation orientation = Orientation.getOrientation(); float azimuth = orientation.getCompassAzimuth(); float pitch = orientation.getPitch(); float roll = orientation.getRoll(); boolean magnetic = orientation.isOrientationMagnetic(); azimuth_item.setText("" + azimuth); pitch_item.setText("" + pitch); roll_item.setText("" + roll); if (magnetic) magnetic_item.setText("Magnetic field"); else magnetic_item.setText("True north"); } while (keep_refreshing); } catch(LocationException e) { MyAlert.show("LocationException", "There was a problem when trying to get orientation.", display); } catch(Throwable e) { MyAlert.show("Throwable", "Some other error when trying to get orientation.", display); } } // run }; // Runnable Thread t = new Thread(r); t.start(); } // refresh } // OrientationForm // The GPSTest2 midlet class itself public class GPSTest2 extends MIDlet implements CommandListener, YesOrNoCallback { MapCanvas map; private Display display; private Command exit_command = new Command("Exit", Command.EXIT, 4); private Command about_command = new Command("About", Command.SCREEN, 3); private Command help_command = new Command("Help", Command.SCREEN, 3); private Command show_lp_command = new Command("Show LP", "Show Location Provider", Command.SCREEN, 2); private Command show_location_command = new Command("Show Location", Command.SCREEN, 2); private Command edit_criteria_command = new Command("Edit Criteria", Command.SCREEN, 2); private Command get_lp_command = new Command("Get LP", "Get Location Provider", Command.SCREEN, 2); private Command get_location_command = new Command("Get Location", Command.SCREEN, 2); private Command start_listening_command = new Command("Start listening", Command.SCREEN, 2); private Command automark_command = new Command("Listen with automark", Command.SCREEN, 2); private Command stop_listening_command = new Command("Stop listening", Command.SCREEN, 2); private Command mark_command = new Command("Mark!", Command.SCREEN, 1); private Command calculate_command = new Command("Calculate", Command.SCREEN, 3); private Command orientation_command = new Command("Show Orientation", Command.SCREEN, 3); public GPSTest2() { } public void destroyApp(boolean unconditional) { } public void pauseApp() { } public void startApp() { display = Display.getDisplay(this); map = new MapCanvas(display); map.addCommand(show_lp_command); map.addCommand(show_location_command); map.addCommand(edit_criteria_command); map.addCommand(get_lp_command); map.addCommand(get_location_command); map.addCommand(start_listening_command); map.addCommand(automark_command); map.addCommand(stop_listening_command); map.addCommand(mark_command); map.addCommand(calculate_command); map.addCommand(orientation_command); map.addCommand(help_command); map.addCommand(about_command); map.addCommand(exit_command); map.setCommandListener(this); display.setCurrent(map); } public void gotAnswer(boolean answer) { if (answer) { notifyDestroyed(); } else { display.setCurrent(map); } } public void commandAction(Command c, Displayable s) { if (c == exit_command) { YesOrNoDialogue dialogue = new YesOrNoDialogue("Do you really want to quit?", this, display); display.setCurrent(dialogue); } else if (c == about_command) about(); else if (c == help_command) help(); else if (c == show_lp_command) show_lp(); else if (c == show_location_command) show_location(); else if (c == edit_criteria_command) edit_criteria(); else if (c == get_lp_command) get_lp(); else if (c == get_location_command) get_location(); else if (c == start_listening_command) start_listening(false); else if (c == automark_command) start_listening(true); else if (c == stop_listening_command) stop_listening(); else if (c == mark_command) mark(); else if (c == calculate_command) calculate(); else if (c == orientation_command) orientation(); else { MyAlert.show("Internal error", "Internal error. This can't happen.", display); } } private AboutForm about_form = null; private void about() { Display d = Display.getDisplay(this); if (about_form == null) about_form = new AboutForm(d, map); d.setCurrent(about_form); } private HelpForm help_form = null; private void help() { Display d = Display.getDisplay(this); if (help_form == null) help_form = new HelpForm(d, map); d.setCurrent(help_form); } private LocationProvider current_lp; private LocationProviderForm lp_form = null; private void show_lp() { Display d = Display.getDisplay(this); if (lp_form == null) lp_form = new LocationProviderForm(current_lp, d, map); else lp_form.refresh(); d.setCurrent(lp_form); } private Criteria current_criteria = new Criteria(); private CriteriaForm criteria_form = null; private void edit_criteria() { Display d = Display.getDisplay(this); if (criteria_form == null) criteria_form = new CriteriaForm(current_criteria, d, map); d.setCurrent(criteria_form); } private void get_lp() { current_lp = null; try { current_lp = LocationProvider.getInstance(current_criteria); if (lp_form != null) lp_form.setLocationProvider(current_lp); } catch(LocationException e) { MyAlert.show("LocationException", "There was a problem when trying to get a location provider.", display); } catch(Throwable e) { MyAlert.show("Throwable", "Some other error. Try again.", display); } } // public, so it can be called to update the location public void get_location() { if (current_lp == null) { MyAlert.show("No LocationProvider", "No LocationProvider when trying to get a location.", display); return; } Runnable r = new Runnable() { public void run() { // current_location = null; try { current_location = current_lp.getLocation(-1); if (map != null) map.setLocation(current_location); if (l_form != null) l_form.setLocation(current_location); } catch(LocationException e) { MyAlert.show("LocationException", "There was a problem when trying to get a location.", display); } catch(InterruptedException e) { MyAlert.show("InterruptedException", "Reached timeout when trying to get a location.", display); } catch(Throwable e) { MyAlert.show("Throwable", "Some other error when trying to get a location.", display); } } // run }; // Runnable Thread t = new Thread(r); t.start(); } // get_location private Location current_location; private LocationForm l_form = null; private void show_location() { Display d = Display.getDisplay(this); if (l_form == null) l_form = new LocationForm(current_location, d, map, this); else l_form.refresh(); d.setCurrent(l_form); } class MyLocationListener implements LocationListener { private boolean automark; public MyLocationListener(boolean automark) { this.automark = automark; } public void locationUpdated(LocationProvider provider, Location location) { System.out.println("PRINTLN: " + "*** New location:"); current_location = location; if (map != null) map.setLocation(current_location); if (l_form != null) l_form.setLocation(current_location); if (location == null || !location.isValid()) { System.out.println("PRINTLN: " + "invalid location (null or not valid)"); return; } if (automark) map.add_point(location); QualifiedCoordinates qc = location.getQualifiedCoordinates(); // For testing in the emulator. Don't leave it in the production code! // qc.setHorizontalAccuracy(1000.0f); double lat = qc.getLatitude(); double lon = qc.getLongitude(); System.out.println("PRINTLN: " + " Latitude = " + lat); System.out.println("PRINTLN: " + " Longitude = " + lon); System.out.println("PRINTLN: " + "---"); } public void providerStateChanged(LocationProvider provider, int newState) { System.out.println("PRINTLN: " + "*** New provider state:"); System.out.println("PRINTLN: " + "" + newState); } } private void start_listening(final boolean automark) { if (current_lp == null) { MyAlert.show("No LocationProvider", "No LocationProvider when trying to get a location.", display); return; } Runnable r = new Runnable() { public void run() { LocationListener listener = new MyLocationListener(automark); current_lp.setLocationListener(listener, -1, -1, -1); } }; Thread t = new Thread(r); t.start(); } private void stop_listening() { current_lp.setLocationListener(null, 0, 0, 0); } // public, so it can be called to mark the location public void mark() { map.add_point(current_location); } private CalcForm calc_form = null; private void calculate() { Display d = Display.getDisplay(this); if (calc_form == null) calc_form = new CalcForm(d, map); else calc_form.refresh(); d.setCurrent(calc_form); } private OrientationForm orientation_form = null; private void orientation() { Display d = Display.getDisplay(this); if (orientation_form == null) orientation_form = new OrientationForm(d, map); else orientation_form.refresh(); d.setCurrent(orientation_form); } } // GPSTest2