/*********************************************************************************
* TotalCross Software Development Kit *
* Copyright (C) 2000-2012 SuperWaba Ltda. *
* All Rights Reserved *
* *
* This library and virtual machine is distributed in the hope that it will *
* be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* *
* This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 *
* A copy of this license is located in file license.txt at the root of this *
* SDK or can be downloaded here: *
* http://www.gnu.org/licenses/lgpl-3.0.txt *
* *
*********************************************************************************/
package totalcross.io.device.gps;
import totalcross.io.*;
import totalcross.io.device.*;
import totalcross.sys.*;
import totalcross.ui.*;
import totalcross.ui.event.*;
/**
* Control that display GPS coordinates read from the COM (or Bluetooth, or IR) port.
* In Windows Mobile and Android, it uses the native API instead of reading from the COM port.
*
* For example:
*
* <pre>
* add(gps = new GPSView(1000),LEFT,TOP);
* </pre>
* See the tc.samples.io.device.GPSTest.
*
* On Android, don't forget to turn on the GPS, going to Settings / Security & Location / Enable GPS satellites.
* The other platforms may require that as well.
*
* If the GPS fails connecting to the satellites, and the phone has signal, you can use the cell tower location as a
* rough location. The precision is vary between 50m to 3km, depending where the phone is. You can get the
* latitude and longitude using CellInfo.toCoordinates.
*
* This class uses the GPS class to show the values on Labels. You can use the GPS class standalone if you wish.
*
* See the tc.samples.maps.GoogleMaps sample.
*
* @see totalcross.io.device.gps.GPS
* @see totalcross.phone.CellInfo#toCoordinates()
*
* @since TotalCross 1.38
*/
public class GPSView extends Container implements TimerListener
{
/** String that will be printed with there longitude can't be acquired. You can localize this string. */
public static String LON_LOW_SIGNAL = "lon: low signal";
/** String that will be printed with there longitude can't be acquired. You can localize this string. */
public static String LAT_LOW_SIGNAL = "lat: low signal";
private Label[] text = new Label[5];
private TimerEvent timer;
private int readInterval = 2000;
/** Class used to retrieve the GPS coordinates. */
public GPS gps;
/** Constructs a GPS using a read interval of 2 seconds. */
public GPSView() throws IOException
{
this(2000);
}
/**
* Constructs a GPSView using the given read interval.
*
* @param readInterval
* The interval used to fetch data, in milliseconds. A ControlEvent.PRESSED is posted each time the TRIGGERED event occurs.
* @throws IOException
*/
public GPSView(int readInterval) throws IOException
{
this((PortConnector)null,readInterval);
}
/** Constructs a GPSView using the given PortConnector and read interval.
* In Android and Windows Mobile, you should use the other constructor.
* #GPSView(int)
*/
public GPSView(PortConnector sp, int readInterval) throws IOException
{
this(sp == null ? new GPS() : new GPS(sp), readInterval);
}
/** Constructs a GPSView using the given GPS as input.
* @since TotalCross 1.5
*/
public GPSView(GPS gps, int readInterval) throws IOException
{
this.gps = gps;
this.readInterval = readInterval;
for (int i = 0; i < text.length; i++)
text[i] = new Label("");
}
public void initUI()
{
for (int i = 0; i < text.length; i++)
add(text[i], LEFT, AFTER);
text[0].setText("GPS Initialising");
timer = addTimer(readInterval);
addTimerListener(this);
}
public void timerTriggered(TimerEvent e)
{
if (e.type == TimerEvent.TRIGGERED && timer.triggered)
retrieveGPSData();
}
// public methods available from GPS class
/** Removes the timer and stops the GPS. */
public void stop() throws IOException
{
TimerEvent t = timer;
timer = null;
removeTimer(t);
gps.stop();
}
/** Retrieves the GPS data and updates the fields with it. */
public void retrieveGPSData()
{
if (timer == null || gps == null)
return;
if (!gps.retrieveGPSData())
lowSignal(gps.lowSignalReason);
else
showGPSData();
repaintNow();
postPressedEvent(); // guich@tc126_67
}
private void showGPSData()
{
double lat = gps.location[0];
double lon = gps.location[1];
double absoluteLat = lat < 0 ? -lat : lat;
int degrees = (int) absoluteLat;
int minutes = (int) ((absoluteLat - degrees) * 60);
double seconds = (((absoluteLat - degrees) * 60) - minutes) * 60;
text[0].setText("lat: " + degrees + " " + minutes + " " + seconds + (lat < 0 ? " S" : " N"));
double absoluteLon = lon < 0 ? -lon : lon;
degrees = (int) absoluteLon;
minutes = (int) ((absoluteLon - degrees) * 60);
seconds = (((absoluteLon - degrees) * 60) - minutes) * 60;
text[1].setText("lon: " + degrees + " " + minutes + " " + seconds + (lon < 0 ? " W" : " E"));
gps.lastFix.inc(0,Settings.timeZoneMinutes,0);
text[2].setText("fix: "+gps.lastFix);
gps.lastFix.inc(0,-Settings.timeZoneMinutes,0);
text[3].setText(gps.velocity != GPS.INVALID ? "speed: " + gps.velocity : "");
text[4].setText(gps.direction != GPS.INVALID ? "direction: " + gps.direction : "");
if (gps.satellites > 0)
text[2].setText(text[2].getText() + " " + "sat: "+gps.satellites);
}
private void lowSignal(String ex)
{
text[0].setText(LAT_LOW_SIGNAL);
text[1].setText(LON_LOW_SIGNAL);
for (int i = 2; i < text.length; i++)
text[i].setText("");
if (ex != null)
text[text.length-1].setText(ex);
}
public int getPreferredWidth()
{
return FILL;
}
public int getPreferredHeight()
{
return fmH * text.length + insets.top + insets.bottom;
}
protected void onColorsChanged(boolean colorsChanged)
{
int b = getBackColor();
int f = getForeColor();
for (int i = 0; i < text.length; i++)
text[i].setBackForeColors(b, f);
}
protected void onFontChanged()
{
for (int i = 0; i < text.length; i++)
text[i].setFont(font);
}
}