/*
* Copyright (c) 2013, Will Szumski
* Copyright (c) 2013, Doug Szumski
*
* This file is part of Cyclismo.
*
* Cyclismo 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.
*
* Cyclismo 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 Cyclismo. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.cowboycoders.cyclismo.io.file;
import android.content.Context;
import android.location.Location;
import org.cowboycoders.cyclismo.R;
import org.cowboycoders.cyclismo.content.MyTracksLocation;
import org.cowboycoders.cyclismo.content.Sensor;
import org.cowboycoders.cyclismo.content.Sensor.SensorData;
import org.cowboycoders.cyclismo.content.Sensor.SensorDataSet;
import org.cowboycoders.cyclismo.content.Track;
import org.cowboycoders.cyclismo.content.Waypoint;
import org.cowboycoders.cyclismo.io.file.TrackWriterFactory.TrackFileFormat;
import org.cowboycoders.cyclismo.util.LocationUtils;
import org.cowboycoders.cyclismo.util.StringUtils;
import org.cowboycoders.cyclismo.util.UnitConversions;
import org.fluxoid.utils.LatLongAlt;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.text.NumberFormat;
public class CsvTrackWriter implements TrackFormatWriter {
private static final NumberFormat SHORT_FORMAT = NumberFormat.getInstance();
static {
SHORT_FORMAT.setMaximumFractionDigits(4);
}
private final Context context;
private PrintWriter printWriter;
private Track track;
private int segmentIndex;
private int pointIndex;
private Location previousLocation;
public CsvTrackWriter(Context context) {
this.context = context;
}
@Override
public String getExtension() {
return TrackFileFormat.CSV.getExtension();
}
@Override
public void prepare(Track aTrack, OutputStream out) {
track = aTrack;
printWriter = new PrintWriter(out);
segmentIndex = 0;
pointIndex = 0;
}
@Override
public void close() {
printWriter.close();
}
@Override
public void writeHeader() {
writeCommaSeparatedLine(context.getString(R.string.generic_name),
context.getString(R.string.track_edit_activity_type_hint),
context.getString(R.string.generic_description));
writeCommaSeparatedLine(track.getName(), track.getCategory(), track.getDescription());
writeCommaSeparatedLine();
}
@Override
public void writeFooter() {
// Do nothing
}
@Override
public void writeBeginWaypoints() {
writeCommaSeparatedLine(context.getString(R.string.generic_name),
context.getString(R.string.marker_edit_marker_type_hint),
context.getString(R.string.generic_description),
context.getString(R.string.description_location_latitude),
context.getString(R.string.description_location_longitude),
context.getString(R.string.description_location_altitude),
context.getString(R.string.description_location_bearing),
context.getString(R.string.description_location_accuracy),
context.getString(R.string.description_location_speed),
context.getString(R.string.description_time));
}
@Override
public void writeEndWaypoints() {
writeCommaSeparatedLine();
}
@Override
public void writeWaypoint(Waypoint waypoint) {
Location location = waypoint.getLocation();
writeCommaSeparatedLine(waypoint.getName(),
waypoint.getCategory(),
waypoint.getDescription(),
Double.toString(location.getLatitude()),
Double.toString(location.getLongitude()),
Double.toString(location.getAltitude()),
Double.toString(location.getBearing()),
SHORT_FORMAT.format(location.getAccuracy()),
SHORT_FORMAT.format(location.getSpeed()),
StringUtils.formatDateTimeIso8601(location.getTime()));
}
@Override
public void writeBeginTrack(Location firstPoint) {
writeCommaSeparatedLine(context.getString(R.string.description_track_segment),
context.getString(R.string.description_track_point),
context.getString(R.string.description_location_latitude),
context.getString(R.string.description_location_longitude),
context.getString(R.string.description_location_altitude),
context.getString(R.string.description_location_bearing),
context.getString(R.string.description_location_accuracy),
context.getString(R.string.description_time),
context.getString(R.string.description_location_speed),
context.getString(R.string.description_spheroid_speed),
context.getString(R.string.description_sensor_distance_meters),
context.getString(R.string.description_sensor_power),
context.getString(R.string.description_sensor_cadence),
context.getString(R.string.description_sensor_heart_rate));
}
@Override
public void writeEndTrack(Location lastPoint) {
// Do nothing
}
@Override
public void writeOpenSegment() {
segmentIndex++;
pointIndex = 0;
}
@Override
public void writeCloseSegment() {
// Do nothing
}
@Override
public void writeLocation(Location location) {
String distance = "";
String power = "";
String cadence = "";
String heartRate = "";
String spheroidSpeed = "";
if (location instanceof MyTracksLocation) {
SensorDataSet sensorDataSet = ((MyTracksLocation) location).getSensorDataSet();
if (sensorDataSet != null) {
if (sensorDataSet.hasDistance()) {
SensorData sensorData = sensorDataSet.getDistance();
if (sensorData.hasValue() && sensorData.getState() == Sensor.SensorState.SENDING) {
distance = Float.toString(sensorData.getValue());
}
}
if (sensorDataSet.hasPower()) {
SensorData sensorData = sensorDataSet.getPower();
if (sensorData.hasValue() && sensorData.getState() == Sensor.SensorState.SENDING) {
power = Float.toString(sensorData.getValue());
}
}
if (sensorDataSet.hasCadence()) {
SensorData sensorData = sensorDataSet.getCadence();
if (sensorData.hasValue() && sensorData.getState() == Sensor.SensorState.SENDING) {
cadence = Float.toString(sensorData.getValue());
}
}
if (sensorDataSet.hasHeartRate()) {
SensorData sensorData = sensorDataSet.getHeartRate();
if (sensorData.hasValue() && sensorData.getState() == Sensor.SensorState.SENDING) {
heartRate = Float.toString(sensorData.getValue());
}
}
}
}
if (previousLocation != null) {
spheroidSpeed = Double.toString(getSpheroidSpeed(previousLocation, location));
}
writeCommaSeparatedLine(Integer.toString(segmentIndex),
Integer.toString(pointIndex++),
Double.toString(location.getLatitude()),
Double.toString(location.getLongitude()),
Double.toString(location.getAltitude()),
Double.toString(location.getBearing()),
SHORT_FORMAT.format(location.getAccuracy()),
//StringUtils.formatDateTimeIso8601(location.getTime()), // Eg. 2015-08-09T12:35:57.880Z
Long.toString(location.getTime()), // ms since the epoch
SHORT_FORMAT.format(location.getSpeed()),
spheroidSpeed,
distance,
power,
cadence,
heartRate);
// Used for calculating the speed from lat longs as is done on some analysis tools.
previousLocation = location;
}
/**
* Calculates the average speed whilst travelling from the source to the destination across a
* spheroidal Earth. This method is used by some tools for calculating the speed from TCX files
* etc.
*
* @param src is the starting location.
* @param dst is the finishing location.
* @return speed in m/s.
*/
private static double getSpheroidSpeed(Location src, Location dst) {
LatLongAlt prevLatLong = LocationUtils.locationToLatLongAlt(src);
LatLongAlt latLong = LocationUtils.locationToLatLongAlt(dst);
double dist = org.fluxoid.utils.LocationUtils.getGradientCorrectedDistance(prevLatLong, latLong);
double time_delta = (dst.getTime() - src.getTime()) / UnitConversions.S_TO_MS;
return dist / time_delta;
}
/**
* Writes a single line of a CSV file.
*
* @param values the values to be written as CSV
*/
private void writeCommaSeparatedLine(String... values) {
StringBuilder builder = new StringBuilder();
boolean isFirst = true;
for (String value : values) {
if (!isFirst) {
builder.append(',');
}
isFirst = false;
builder.append('"');
if (value != null) {
builder.append(value.replaceAll("\"", "\"\""));
}
builder.append('"');
}
printWriter.println(builder.toString());
}
}