package net.osmand; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.TimeZone; import net.osmand.plus.R; import org.apache.commons.logging.Log; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import android.content.Context; import android.location.Location; import android.util.Xml; public class GPXUtilities { public final static Log log = LogUtil.getLog(GPXUtilities.class); private final static String GPX_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; //$NON-NLS-1$ private final static NumberFormat latLonFormat = new DecimalFormat("0.00#####", new DecimalFormatSymbols(Locale.US)); public static class TrkPt { public double lat; public double lon; public double ele; public double speed; public long time; } public static class WptPt { public double lat; public double lon; public String name; // by default public long time = 0; } public static class TrkSegment { public List<TrkPt> points = new ArrayList<TrkPt>(); } public static class Track { public List<TrkSegment> segments = new ArrayList<TrkSegment>(); } public static class GPXFile { public List<Track> tracks = new ArrayList<Track>(); public List<WptPt> points = new ArrayList<WptPt>(); } public static String writeGpxFile(File fout, GPXFile file, Context ctx) { try { SimpleDateFormat format = new SimpleDateFormat(GPX_TIME_FORMAT); format.setTimeZone(TimeZone.getTimeZone("UTC")); FileOutputStream output = new FileOutputStream(fout); XmlSerializer serializer = Xml.newSerializer(); serializer.setOutput(output, "UTF-8"); //$NON-NLS-1$ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); //$NON-NLS-1$ serializer.startDocument("UTF-8", true); //$NON-NLS-1$ serializer.startTag(null, "gpx"); //$NON-NLS-1$ serializer.attribute(null, "version", "1.1"); //$NON-NLS-1$ //$NON-NLS-2$ serializer.attribute(null, "creator", Version.APP_NAME_VERSION); //$NON-NLS-1$ serializer.attribute("xmlns", "xsi", "http://www.w3.org/2001/XMLSchema-instance"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ serializer.attribute("xsi", "schemaLocation", "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ serializer.attribute(null, "xmlns", "http://www.topografix.com/GPX/1/1"); //$NON-NLS-1$ //$NON-NLS-2$ for (Track track : file.tracks) { serializer.startTag(null, "trk"); //$NON-NLS-1$ for (TrkSegment segment : track.segments) { serializer.startTag(null, "trkseg"); //$NON-NLS-1$ for (TrkPt p : segment.points) { serializer.startTag(null, "trkpt"); //$NON-NLS-1$ serializer.attribute(null, "lat", latLonFormat.format(p.lat)); //$NON-NLS-1$ //$NON-NLS-2$ serializer.attribute(null, "lon", latLonFormat.format(p.lon)); //$NON-NLS-1$ //$NON-NLS-2$ serializer.startTag(null, "time"); //$NON-NLS-1$ serializer.text(format.format(new Date(p.time))); serializer.endTag(null, "time"); //$NON-NLS-1$ serializer.startTag(null, "ele"); //$NON-NLS-1$ serializer.text(p.ele + ""); //$NON-NLS-1$ serializer.endTag(null, "ele"); //$NON-NLS-1$ if (p.speed > 0) { serializer.startTag(null, "speed"); //$NON-NLS-1$ serializer.text(p.speed + ""); //$NON-NLS-1$ serializer.endTag(null, "speed"); //$NON-NLS-1$ } serializer.endTag(null, "trkpt"); //$NON-NLS-1$ } serializer.endTag(null, "trkseg"); //$NON-NLS-1$ } serializer.endTag(null, "trk"); //$NON-NLS-1$ } for (WptPt l : file.points) { serializer.startTag(null, "wpt"); //$NON-NLS-1$ serializer.attribute(null, "lat", latLonFormat.format(l.lat)); //$NON-NLS-1$ serializer.attribute(null, "lon", latLonFormat.format(l.lon)); //$NON-NLS-1$ //$NON-NLS-2$ serializer.startTag(null, "name"); //$NON-NLS-1$ serializer.text(l.name); serializer.endTag(null, "name"); //$NON-NLS-1$ if (l.time != 0) { serializer.startTag(null, "time"); //$NON-NLS-1$ serializer.text(format.format(new Date(l.time))); serializer.endTag(null, "time"); //$NON-NLS-1$ } serializer.endTag(null, "wpt"); //$NON-NLS-1$ } serializer.endTag(null, "gpx"); //$NON-NLS-1$ serializer.flush(); serializer.endDocument(); } catch (RuntimeException e) { log.error("Error saving gpx", e); //$NON-NLS-1$ return ctx.getString(R.string.error_occurred_saving_gpx); } catch (IOException e) { log.error("Error saving gpx", e); //$NON-NLS-1$ return ctx.getString(R.string.error_occurred_saving_gpx); } return null; } public static class GPXFileResult { public ArrayList<List<Location>> locations = new ArrayList<List<Location>>(); public ArrayList<WptPt> wayPoints = new ArrayList<WptPt>(); // special case for cloudmate gpx : they discourage common schema // by using waypoint as track points and rtept are not very close to real way // such as wpt. However they provide additional information into gpx. public boolean cloudMadeFile; public String error; } public static GPXFileResult loadGPXFile(Context ctx, File f){ GPXFileResult res = new GPXFileResult(); try { boolean cloudMade = false; XmlPullParser parser = Xml.newPullParser(); parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$ int tok; Location current = null; String currentName = ""; //$NON-NLS-1$ while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) { if (tok == XmlPullParser.START_TAG) { if (parser.getName().equals("copyright")) { //$NON-NLS-1$ cloudMade |= "cloudmade".equalsIgnoreCase(parser.getAttributeValue("", "author")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } else if (parser.getName().equals("trkseg")) { //$NON-NLS-1$ res.locations.add(new ArrayList<Location>()); } else if (parser.getName().equals("wpt") || parser.getName().equals("trkpt") || //$NON-NLS-1$//$NON-NLS-2$ (!cloudMade && parser.getName().equals("rtept"))) { //$NON-NLS-1$ // currently not distinguish different point represents all as a line try { currentName = ""; //$NON-NLS-1$ current = new Location("gpx_file"); //$NON-NLS-1$ current.setLatitude(Double.parseDouble(parser.getAttributeValue("", "lat"))); //$NON-NLS-1$ //$NON-NLS-2$ current.setLongitude(Double.parseDouble(parser.getAttributeValue("", "lon"))); //$NON-NLS-1$ //$NON-NLS-2$ } catch (NumberFormatException e) { current = null; } } else if (current != null && parser.getName().equals("name")) { //$NON-NLS-1$ if (parser.next() == XmlPullParser.TEXT) { currentName = parser.getText(); } } } else if (tok == XmlPullParser.END_TAG) { if (parser.getName().equals("wpt") || //$NON-NLS-1$ parser.getName().equals("trkpt") || (!cloudMade && parser.getName().equals("rtept"))) { //$NON-NLS-1$ //$NON-NLS-2$ if (current != null) { if (parser.getName().equals("wpt") && !cloudMade) { //$NON-NLS-1$ WptPt pt = new WptPt(); pt.lat = current.getLatitude(); pt.lon = current.getLongitude(); pt.name = currentName; res.wayPoints.add(pt); } else { if (res.locations.isEmpty()) { res.locations.add(new ArrayList<Location>()); } res.locations.get(res.locations.size() - 1).add(current); } } } } } } catch (XmlPullParserException e) { log.error("Error reading gpx", e); //$NON-NLS-1$ res.error = ctx.getString(R.string.error_reading_gpx); } catch (IOException e) { log.error("Error reading gpx", e); //$NON-NLS-1$ res.error = ctx.getString(R.string.error_reading_gpx); } return res; } }