/* This file is part of Wattzap Community Edition.
*
* Wattzap Community Edtion 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.
*
* Wattzap Community Edition 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 Wattzap. If not, see <http://www.gnu.org/licenses/>.
*/
package com.wattzap.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import com.wattzap.model.GPXReader;
import com.wattzap.model.UserPreferences;
import com.wattzap.model.dto.Telemetry;
import com.wattzap.model.dto.WorkoutData;
import com.wattzap.model.power.Power;
/**
* Import TCX Format files
*
* @author David George
* @date 2nd May 2014
*/
public class TcxImporter extends DefaultHandler {
State currentState = State.UNDEFINED;
StringBuilder buffer;
protected static final String TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
private final SimpleDateFormat timestampFormatter;
ArrayList<Telemetry> data;
Telemetry point;
double distance = 0;
Rolling rSpeed = new Rolling(20);
Rolling pAve = new Rolling(20);
ExponentialMovingAverage gradeAve = new ExponentialMovingAverage(0.8);
private final UserPreferences userPrefs = UserPreferences.INSTANCE;
private static Logger logger = LogManager.getLogger("TCX Importer");
public TcxImporter() {
super();
currentState = State.UNDEFINED;
timestampFormatter = new SimpleDateFormat(TIMESTAMP_FORMAT);
data = new ArrayList<Telemetry>();
}
public void startElement(String uri, String name, String qName,
Attributes atts) {
if (currentState == State.TRACKPOINT) {
// Only if we are in a TRACKPOINT state can we enter any of these
// states
if ("Cadence".equalsIgnoreCase(name)) {
buffer = new StringBuilder();
} else if ("Time".equalsIgnoreCase(name)) {
buffer = new StringBuilder();
} else if ("DistanceMeters".equalsIgnoreCase(name)) {
buffer = new StringBuilder();
} else if ("AltitudeMeters".equalsIgnoreCase(name)) {
buffer = new StringBuilder();
} else if ("HeartRateBpm".equalsIgnoreCase(name)) {
buffer = new StringBuilder();
} else if ("Extensions".equalsIgnoreCase(name)) {
currentState = State.EXTENSIONS;
} else if ("Position".equalsIgnoreCase(name)) {
currentState = State.POSITION;
}
} else if (currentState == State.EXTENSIONS) {
if ("Watts".equalsIgnoreCase(name)) {
buffer = new StringBuilder();
} else if ("Speed".equalsIgnoreCase(name)) {
buffer = new StringBuilder();
}
} else if (currentState == State.POSITION) {
if ("LatitudeDegrees".equalsIgnoreCase(name)) {
buffer = new StringBuilder();
} else if ("LongitudeDegrees".equalsIgnoreCase(name)) {
buffer = new StringBuilder();
}
} else if ("Trackpoint".equalsIgnoreCase(name)) {
point = new Telemetry();
currentState = State.TRACKPOINT;
} else if ("DistanceMeters".equalsIgnoreCase(name)) {
buffer = new StringBuilder();
}
}
public void endElement(String uri, String name, String qName) {
try {
if (currentState == State.TRACKPOINT) {
// Only if we are in a TRACKPOINT state can we enter any of
// these
// states
if ("Cadence".equalsIgnoreCase(name)) {
int cadence = Integer.parseInt(buffer.toString().trim());
point.setCadence(cadence);
currentState = State.TRACKPOINT;
} else if ("Time".equalsIgnoreCase(name)) {
String tt = buffer.toString().trim();
Date d = timestampFormatter.parse(tt);
point.setTime(d.getTime());
currentState = State.TRACKPOINT;
} else if ("HeartRateBpm".equalsIgnoreCase(name)) {
int hr = Integer.parseInt(buffer.toString().trim());
point.setHeartRate(hr);
currentState = State.TRACKPOINT;
} else if ("DistanceMeters".equalsIgnoreCase(name)) {
double distance = Double.parseDouble(buffer.toString()
.trim());
point.setDistanceMeters(distance);
currentState = State.TRACKPOINT;
} else if ("AltitudeMeters".equalsIgnoreCase(name)) {
double altitude = Double.parseDouble(buffer.toString()
.trim());
point.setElevation(altitude);
currentState = State.TRACKPOINT;
} else if ("Trackpoint".equalsIgnoreCase(name)) {
// finalize data
int current = data.size();
if (current > 0) {
Telemetry last = data.get(current - 1);
double d = GPXReader.distance(point.getLatitude(),
last.getLatitude(), point.getLongitude(),
last.getLongitude(), point.getElevation(),
last.getElevation());
if (point.getSpeedKMH() == -1) {
// calculate speed, s = d / t
double speed = rSpeed.add(d * 3600
/ (point.getTime() - last.getTime()));
point.setSpeed(speed);
}
double gradient = (point.getElevation() - last
.getElevation()) / d;
if (point.getPower() == -1) {
int p = (int) pAve.add(Power.getPower(
userPrefs.getTotalWeight(), gradient,
point.getSpeedKMH()));
if (p > userPrefs.getMaxPower()
&& (p > (last.getPower() * 2.0))) {
// We are above FTP and power has doubled,
// remove power
// spikes
p = (int) (last.getPower() * 1.05);
}
if (p > (userPrefs.getMaxPower() * 4)) {
// power is 4 x FTP, this is a spike
p = last.getPower();
}
if (p > 0) {
point.setPower(p);
}
}
distance += d;
point.setDistanceMeters(distance);
point.setGradient((gradient) * 100);
} else {
if (point.getPower() != -1) {
point.setResistance(WorkoutData.POWERMETER);
} else {
point.setResistance(WorkoutData.GPS);
}
}
data.add(point);
currentState = State.UNDEFINED;
}
} else if (currentState == State.EXTENSIONS) {
if ("Watts".equalsIgnoreCase(name)) {
int power = Integer.parseInt(buffer.toString().trim());
point.setPower(power);
} else if ("Speed".equalsIgnoreCase(name)) {
double speed = Double.parseDouble(buffer.toString().trim());
point.setSpeed(speed);
} else if ("Extensions".equalsIgnoreCase(name)) {
currentState = State.TRACKPOINT;
}
} else if (currentState == State.POSITION) {
if ("LatitudeDegrees".equalsIgnoreCase(name)) {
double latitude = Double.parseDouble(buffer.toString()
.trim());
point.setLatitude(latitude);
} else if ("LongitudeDegrees".equalsIgnoreCase(name)) {
double longitude = Double.parseDouble(buffer.toString()
.trim());
point.setLongitude(longitude);
} else if ("Position".equalsIgnoreCase(name)) {
currentState = State.TRACKPOINT;
}
}
if ("DistanceMeters".equalsIgnoreCase(name)) {
distance = Double.parseDouble(buffer.toString().trim());
}
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void characters(char ch[], int start, int length) {
if (buffer != null) {
buffer.append(ch, start, length);
}
// System.out.println("buffer " + buffer);
}
public enum State {
TIME, HR, CADENCE, WATTS, SPEED, TRACKPOINT, EXTENSIONS, POSITION, UNDEFINED
}
}