/*
* Copyright (C) 2014 Alec Dhuse
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package co.foldingmap.mapImportExport;
import co.foldingmap.map.vector.VectorLayer;
import co.foldingmap.map.vector.CoordinateList;
import co.foldingmap.map.vector.VectorObjectList;
import co.foldingmap.map.vector.VectorObject;
import co.foldingmap.map.vector.Coordinate;
import co.foldingmap.map.vector.MapPoint;
import co.foldingmap.map.vector.LineString;
import co.foldingmap.GUISupport.ProgressIndicator;
import co.foldingmap.map.DigitalMap;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
/**
* This class is used to import GPX files.
* These files are a common way of exchanging GPS data between programs.
* Currently only supports GPX 1.0.
*
* See http://www.topografix.com/gpx.asp for more information.
*
* @author Alec
*/
public class GpxImporter extends Thread {
private DigitalMap mapData;
private String xml;
private ProgressIndicator progressIndicator;
public GpxImporter(DigitalMap mapData, File gpxFile, ProgressIndicator progressIndicator) {
this.mapData = mapData;
this.progressIndicator = progressIndicator;
this.xml = readGpxFile(gpxFile);
}
/**
* Returns a coordinate representing a single trkpt tag in a trkseg parent
* tag.
*
* Tags will have the form:
*
* <trkpt lat="4.765787534415722" lon="11.227527866140008">
* <ele>482.71923828125</ele>
* </trkpt>
*
* @param xml
* @return
*/
public static Coordinate getTrackSegment(String xml) {
Coordinate segmentCoordinate;
float altitude, latitude, longitude;
int offset, xmlLength;
int propertyStart, propertyEnd, trackpointTagStart, trackpointTagEnd;
String propertyText, time, waypointXML;
offset = 0;
segmentCoordinate = Coordinate.UNKNOWN_COORDINATE;
xmlLength = xml.length();
altitude = 0;
latitude = 0;
longitude = 0;
time = "";
try {
trackpointTagStart = xml.indexOf("<trkpt", offset);
trackpointTagEnd = xml.indexOf("</trkpt>", trackpointTagStart);
if ((trackpointTagStart >=0) && (trackpointTagStart < trackpointTagEnd)) {
waypointXML = xml.substring(trackpointTagStart, trackpointTagEnd);
propertyStart = waypointXML.indexOf("lat=\"") + 5;
propertyEnd = waypointXML.indexOf("\"", propertyStart);
if ((propertyStart >= 0) && (propertyStart < propertyEnd)) {
propertyText = waypointXML.substring(propertyStart, propertyEnd);
latitude = Float.parseFloat(propertyText);
}
propertyStart = waypointXML.indexOf("lon=\"") + 5;
propertyEnd = waypointXML.indexOf("\"", propertyStart);
if ((propertyStart >= 0) && (propertyStart < propertyEnd)) {
propertyText = waypointXML.substring(propertyStart, propertyEnd);
longitude = Float.parseFloat(propertyText);
}
propertyStart = waypointXML.indexOf("<ele>") + 5;
propertyEnd = waypointXML.indexOf("</ele>", propertyStart);
if ((propertyStart > 0) && (propertyStart < propertyEnd)) {
propertyText = waypointXML.substring(propertyStart, propertyEnd);
altitude = Float.parseFloat(propertyText);
}
propertyStart = waypointXML.indexOf("<time>") + 6;
propertyEnd = waypointXML.indexOf("</time>", propertyStart);
if ((propertyStart > 0) && (propertyStart < propertyEnd)) {
propertyText = waypointXML.substring(propertyStart, propertyEnd);
time = propertyText;
}
segmentCoordinate = new Coordinate(altitude, latitude, longitude, time);
}
} catch (Exception e) {
System.err.println("Error on GpxImport.getTrackSegment(String) - " + e);
}
return segmentCoordinate;
}
/**
* Returns all the Coordinates in a trkseg tag as a CoordinateList.
*
* @param xml
* @return
*/
public static CoordinateList<Coordinate> getTrackSegments(String xml) {
CoordinateList<Coordinate> coordinates;
Coordinate currentTrackPoint;
int offset, xmlLength;
int trackPointStart, trackPointEnd;
String trackPointXML;
coordinates = new CoordinateList<Coordinate>();
offset = 0;
xmlLength = xml.length();
try {
while (offset < xmlLength) {
trackPointStart = xml.indexOf("<trkpt", offset);
trackPointEnd = xml.indexOf("</trkpt>", trackPointStart) + 8;
if ((trackPointStart >= 0) && (trackPointStart < trackPointEnd)) {
trackPointXML = xml.substring(trackPointStart, trackPointEnd);
currentTrackPoint = getTrackSegment(trackPointXML);
offset = trackPointEnd;
if (!currentTrackPoint.equals(Coordinate.UNKNOWN_COORDINATE))
coordinates.add(currentTrackPoint);
} else {
break;
}
}
} catch (Exception e) {
System.err.println("Error in GpxImporter.getTrackSegment(String) - " + e);
}
return coordinates ;
}
private void importGpx() {
VectorObjectList<VectorObject> waypoints, tracks;
VectorLayer importLayer;
progressIndicator.updateProgress("Loading Waypoints", 1);
waypoints = parseWaypoints(xml, progressIndicator);
progressIndicator.updateProgress("Loading Tracks", 50);
tracks = parseTracks(xml);
importLayer = new VectorLayer("GPX Import");
importLayer.setParentMap(mapData);
importLayer.addAllObjects(tracks);
importLayer.addAllObjects(waypoints);
mapData.addLayer(importLayer);
progressIndicator.updateProgress("Finishing Import", 99);
progressIndicator.finish();
}
/**
* Parses the WayPoints from the GPX file and returns a list of MapPoints.
*
* @param xml
* @param progressPanel
* ProgressPanel to update parse progress, can be null.
* @return
*/
public static VectorObjectList<VectorObject> parseWaypoints(String xml, ProgressIndicator progressIndicator) {
Coordinate newCoordinate;
float altitude, latitude, longitude, percentDone;
int offset, xmlLength;
int propertyStart, propertyEnd, waypointTagStart, waypointTagEnd;
VectorObjectList<VectorObject> waypoints;
MapPoint newPoint;
String description, name, propertyText, objectClass;
String symbol, time, waypointXML;
offset = 0;
waypoints = new VectorObjectList<VectorObject>();
xmlLength = xml.length();
try {
while (offset < xmlLength) {
altitude = 0;
description = "";
latitude = 0;
longitude = 0;
name = "New WayPoint";
objectClass = "(Unspecified Point)";
symbol = "";
time = null;
percentDone = (((float) offset) / ((float) xmlLength)) * 100;
waypointTagStart = xml.indexOf("<wpt", offset);
waypointTagEnd = xml.indexOf("</wpt>", waypointTagStart);
if (percentDone > 0 && progressIndicator != null)
progressIndicator.updateProgress("Loading GPX Waypoints", (int) (percentDone - 51));
if ((waypointTagStart >= 0) && (waypointTagStart < waypointTagEnd)) {
waypointXML = xml.substring(waypointTagStart, waypointTagEnd);
propertyStart = waypointXML.indexOf("lat=\"") + 5;
propertyEnd = waypointXML.indexOf("\"", propertyStart);
if ((propertyStart >= 0) && (propertyStart < propertyEnd)) {
propertyText = waypointXML.substring(propertyStart, propertyEnd);
latitude = Float.parseFloat(propertyText);
offset = propertyEnd + 1;
}
propertyStart = waypointXML.indexOf("lon=\"") + 5;
propertyEnd = waypointXML.indexOf("\"", propertyStart);
if ((propertyStart >= 0) && (propertyStart < propertyEnd)) {
propertyText = waypointXML.substring(propertyStart, propertyEnd);
longitude = Float.parseFloat(propertyText);
offset = propertyEnd + 1;
}
propertyStart = waypointXML.indexOf("<ele>") + 5;
propertyEnd = waypointXML.indexOf("</ele>", propertyStart);
if ((propertyStart >= 0) && (propertyStart < propertyEnd)) {
propertyText = waypointXML.substring(propertyStart, propertyEnd);
altitude = Float.parseFloat(propertyText);
offset = propertyEnd + 6;
}
propertyStart = waypointXML.indexOf("<name>") + 6;
propertyEnd = waypointXML.indexOf("</name>", propertyStart);
if ((propertyStart >= 0) && (propertyStart < propertyEnd)) {
propertyText = waypointXML.substring(propertyStart, propertyEnd);
name = propertyText;
offset = propertyEnd + 7;
}
propertyStart = waypointXML.indexOf("<desc>") + 6;
propertyEnd = waypointXML.indexOf("</desc>", propertyStart);
if ((propertyStart >= 0) && (propertyStart < propertyEnd)) {
propertyText = waypointXML.substring(propertyStart, propertyEnd);
description = propertyText;
offset = propertyEnd + 7;
}
propertyStart = waypointXML.indexOf("<time>") + 6;
propertyEnd = waypointXML.indexOf("</time>", propertyStart);
if ((propertyStart >= 0) && (propertyStart < propertyEnd)) {
propertyText = waypointXML.substring(propertyStart, propertyEnd);
time = propertyText;
offset = propertyEnd + 7;
}
propertyStart = waypointXML.indexOf("<sym>") + 5;
propertyEnd = waypointXML.indexOf("</sym>", propertyStart);
if ((propertyStart >= 0) && (propertyStart < propertyEnd)) {
propertyText = waypointXML.substring(propertyStart, propertyEnd);
symbol = propertyText;
offset = propertyEnd + 7;
}
GpxGarminExtensions garminExt = new GpxGarminExtensions();
objectClass = garminExt.getClassFromSymbol(symbol);
if (time != null) {
newCoordinate = new Coordinate(altitude, latitude, longitude, time);
} else {
newCoordinate = new Coordinate(altitude, latitude, longitude);
}
newPoint = new MapPoint(name, objectClass, description, newCoordinate);
offset = waypointTagEnd + 6;
waypoints.add(newPoint);
} else {
break;
}
} //end while loop
} catch (Exception e) {
System.err.println("Error in GpxImporter.parseWaypoints(String) offset: " + offset + " Error: " + e);
}
return waypoints;
}
public static VectorObjectList<VectorObject> parseTracks(String xml) {
CoordinateList<Coordinate> coordinates;
Coordinate trackSegmentCoordinate;
int offset, xmlLength;
int propertyStart, propertyEnd, trackTagStart, trackTagEnd;
int trackSegmentTagStart, trackSegmentTagEnd;
LineString newLineString;
VectorObjectList<VectorObject> tracks;
String name, propertyText, trackSegmentXML, trackXML;
name = "New Line";
offset = 0;
tracks = new VectorObjectList<VectorObject>();
xmlLength = xml.length();
try {
while (offset < xmlLength) {
trackTagStart = xml.indexOf("<trk>", offset);
trackTagEnd = xml.indexOf("</trk>", trackTagStart);
if ((trackTagStart >= 0) && (trackTagStart < trackTagEnd)) {
trackXML = xml.substring(trackTagStart, trackTagEnd);
propertyStart = trackXML.indexOf("<name>") + 6;
propertyEnd = trackXML.indexOf("</name>", propertyStart);
if ((propertyStart > 0) && (propertyStart < propertyEnd)) {
propertyText = trackXML.substring(propertyStart, propertyEnd);
name = propertyText;
offset = propertyEnd + 7;
}
while (offset < xmlLength) {
trackSegmentTagStart = xml.indexOf("<trkseg>", offset);
trackSegmentTagEnd = xml.indexOf("</trkseg>", trackSegmentTagStart);
if ((trackSegmentTagStart >= 0) && (trackSegmentTagStart < trackSegmentTagEnd)) {
offset = trackSegmentTagEnd + 9;
trackSegmentTagStart += 8;
trackSegmentXML = xml.substring(trackSegmentTagStart, trackSegmentTagEnd);
coordinates = getTrackSegments(trackSegmentXML);
if (coordinates.size() > 0) {
newLineString = new LineString(name, "(Unspecified Linestring)", coordinates);
tracks.add(newLineString);
}
} else {
break;
}
}
} else {
break;
}
} //end while loop
} catch (Exception e) {
System.out.println("Error in GpxImporter.parseTracks(String) - " + e);
}
return tracks;
}
private static String readGpxFile(File gpxFile) {
BufferedReader br;
StringBuilder sb = new StringBuilder();
try {
br = new BufferedReader(new FileReader(gpxFile));
while (br.ready())
sb.append(br.readLine().trim());
} catch (Exception e) {
System.err.println("Error in GpxImporter.readGpxFile(File) - " + e);
}
return sb.toString();
}
@Override
public void run() {
importGpx();
}
}