mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-15 00:49:55 +00:00
KML File Creation - enhancements
... fix problem of not visible track (Windows,...) ... use "run" data series if track isRun() ... add "start" and "end" placemark ... enable translation of texts
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "KmlRideFile.h"
|
||||
#include <QDebug>
|
||||
#include <QObject>
|
||||
|
||||
#include <time.h>
|
||||
#include <iostream>
|
||||
@@ -58,6 +59,9 @@ using kmldom::PlacemarkPtr;
|
||||
using kmldom::TimeStampPtr;
|
||||
using kmldom::StylePtr;
|
||||
using kmldom::StyleMapPtr;
|
||||
using kmldom::LineStylePtr;
|
||||
using kmldom::CoordinatesPtr;
|
||||
using kmldom::PointPtr;
|
||||
|
||||
static int kmlFileReaderRegistered =
|
||||
RideFileFactory::instance().registerReader(
|
||||
@@ -65,9 +69,18 @@ static int kmlFileReaderRegistered =
|
||||
//
|
||||
// Utility functions
|
||||
//
|
||||
static const char kDotIcon[] =
|
||||
static const string kDotIcon =
|
||||
"http://maps.google.com/mapfiles/kml/shapes/shaded_dot.png";
|
||||
|
||||
static const string kStartIcon =
|
||||
"http://maps.google.com/mapfiles/kml/paddle/wht-circle.png";
|
||||
|
||||
static const string kEndIcon =
|
||||
"http://maps.google.com/mapfiles/kml/paddle/stop.png";
|
||||
|
||||
static const char* kStyleMapId = "style-map";
|
||||
static const char* kStyleMapIdUrl = "#style-map";
|
||||
|
||||
|
||||
static ExtendedDataPtr CreateExtendedData() {
|
||||
KmlFactory* kml_factory = KmlFactory::GetFactory();
|
||||
@@ -89,12 +102,14 @@ static GxSimpleArrayDataPtr CreateGxSimpleArrayData(string name) {
|
||||
return sa;
|
||||
}
|
||||
|
||||
static PlacemarkPtr CreateGxTrackPlacemark(string name, GxTrackPtr tracks) {
|
||||
static PlacemarkPtr CreateGxTrackPlacemark(string name, string description, GxTrackPtr tracks) {
|
||||
KmlFactory* kml_factory = KmlFactory::GetFactory();
|
||||
|
||||
PlacemarkPtr placemark = kml_factory->CreatePlacemark();
|
||||
placemark->set_name(name);
|
||||
placemark->set_id(name);
|
||||
placemark->set_description(description);
|
||||
placemark->set_styleurl(kStyleMapIdUrl);
|
||||
|
||||
placemark->set_geometry(tracks);
|
||||
return placemark;
|
||||
@@ -107,6 +122,59 @@ static GxTrackPtr CreateGxTrack(string name) {
|
||||
return track;
|
||||
}
|
||||
|
||||
static IconStylePtr CreateIconStyle(double scale, string iconUrl ) {
|
||||
KmlFactory* kml_factory = KmlFactory::GetFactory();
|
||||
IconStyleIconPtr icon = kml_factory->CreateIconStyleIcon();
|
||||
icon->set_href(iconUrl);
|
||||
IconStylePtr icon_style = kml_factory->CreateIconStyle();
|
||||
icon_style->set_icon(icon);
|
||||
icon_style->set_scale(scale);
|
||||
return icon_style;
|
||||
}
|
||||
|
||||
static PlacemarkPtr CreateStartPlacemark(double lat, double lon) {
|
||||
KmlFactory* kml_factory = KmlFactory::GetFactory();
|
||||
|
||||
// Create <coordinates>.
|
||||
CoordinatesPtr coordinates = kml_factory->CreateCoordinates();
|
||||
coordinates->add_latlng(lat, lon);
|
||||
// Create <Point> and give it <coordinates>.
|
||||
PointPtr point = kml_factory->CreatePoint();
|
||||
point->set_coordinates(coordinates); // point takes ownership
|
||||
// Create Placemark
|
||||
PlacemarkPtr placemark = kml_factory->CreatePlacemark();
|
||||
placemark->set_name(QObject::tr("Start").toStdString());
|
||||
placemark->set_id("start");
|
||||
placemark->set_geometry(point); // placemark takes ownership
|
||||
|
||||
StylePtr style = kml_factory->CreateStyle();
|
||||
style->set_iconstyle(CreateIconStyle(1, kStartIcon));
|
||||
placemark->set_styleselector(style);
|
||||
return placemark;
|
||||
}
|
||||
|
||||
static PlacemarkPtr CreateEndPlacemark(double lat, double lon) {
|
||||
KmlFactory* kml_factory = KmlFactory::GetFactory();
|
||||
|
||||
// Create <coordinates>.
|
||||
CoordinatesPtr coordinates = kml_factory->CreateCoordinates();
|
||||
coordinates->add_latlng(lat, lon);
|
||||
// Create <Point> and give it <coordinates>.
|
||||
PointPtr point = kml_factory->CreatePoint();
|
||||
point->set_coordinates(coordinates); // point takes ownership
|
||||
// Create Placemark
|
||||
PlacemarkPtr placemark = kml_factory->CreatePlacemark();
|
||||
placemark->set_name(QObject::tr("End").toStdString());
|
||||
placemark->set_id("start");
|
||||
placemark->set_geometry(point); // placemark takes ownership
|
||||
|
||||
StylePtr style = kml_factory->CreateStyle();
|
||||
style->set_iconstyle(CreateIconStyle(1, kEndIcon));
|
||||
placemark->set_styleselector(style);
|
||||
return placemark;
|
||||
}
|
||||
|
||||
|
||||
static SchemaPtr CreateSchema(string name) {
|
||||
KmlFactory* kml_factory = KmlFactory::GetFactory();
|
||||
SchemaPtr schema = kml_factory->CreateSchema();
|
||||
@@ -115,40 +183,35 @@ static SchemaPtr CreateSchema(string name) {
|
||||
return schema;
|
||||
}
|
||||
|
||||
static GxSimpleArrayFieldPtr CreateGxSimpleArrayField(string name) {
|
||||
static GxSimpleArrayFieldPtr CreateGxSimpleArrayField(string name, string displayName) {
|
||||
KmlFactory* kml_factory = KmlFactory::GetFactory();
|
||||
GxSimpleArrayFieldPtr field = kml_factory->CreateGxSimpleArrayField();
|
||||
field->set_type("float");
|
||||
field->set_name(name);
|
||||
field->set_displayname(name);
|
||||
field->set_displayname(displayName);
|
||||
return field;
|
||||
}
|
||||
|
||||
static IconStylePtr CreateIconStyle(double scale) {
|
||||
KmlFactory* kml_factory = KmlFactory::GetFactory();
|
||||
IconStyleIconPtr icon = kml_factory->CreateIconStyleIcon();
|
||||
icon->set_href(kDotIcon);
|
||||
IconStylePtr icon_style = kml_factory->CreateIconStyle();
|
||||
icon_style->set_icon(icon);
|
||||
icon_style->set_scale(scale);
|
||||
return icon_style;
|
||||
}
|
||||
|
||||
static LabelStylePtr CreateLabelStyle(double scale) {
|
||||
LabelStylePtr label_style = KmlFactory::GetFactory()->CreateLabelStyle();
|
||||
label_style->set_scale(scale);
|
||||
return label_style;
|
||||
}
|
||||
|
||||
static PairPtr CreatePair(int style_state, double icon_scale) {
|
||||
static PairPtr CreatePair(int style_state, double icon_scale, kmlbase::Color32 color ) {
|
||||
KmlFactory* kml_factory = KmlFactory::GetFactory();
|
||||
PairPtr pair = kml_factory->CreatePair();
|
||||
pair->set_key(style_state);
|
||||
StylePtr style = kml_factory->CreateStyle();
|
||||
style->set_iconstyle(CreateIconStyle(icon_scale));
|
||||
style->set_iconstyle(CreateIconStyle(icon_scale, kDotIcon));
|
||||
// Hide the label in normal style state, visible in highlight.
|
||||
style->set_labelstyle(CreateLabelStyle(
|
||||
style_state == kmldom::STYLESTATE_NORMAL ? 0 : 1 ));
|
||||
|
||||
LineStylePtr lineStyle = kml_factory->CreateLineStyle();
|
||||
lineStyle->set_width(3);
|
||||
lineStyle->set_color(color);
|
||||
style->set_linestyle(lineStyle);
|
||||
pair->set_styleselector(style);
|
||||
return pair;
|
||||
}
|
||||
@@ -167,8 +230,8 @@ static StyleMapPtr CreateStyleMap(const char* id) {
|
||||
KmlFactory* kml_factory = KmlFactory::GetFactory();
|
||||
StyleMapPtr style_map = kml_factory->CreateStyleMap();
|
||||
style_map->set_id(id);
|
||||
style_map->add_pair(CreatePair(kmldom::STYLESTATE_NORMAL, 0.1));
|
||||
style_map->add_pair(CreatePair(kmldom::STYLESTATE_HIGHLIGHT, 0.3));
|
||||
style_map->add_pair(CreatePair(kmldom::STYLESTATE_NORMAL, 0.1, kmlbase::Color32(0xffffffff)));
|
||||
style_map->add_pair(CreatePair(kmldom::STYLESTATE_HIGHLIGHT, 0.3, kmlbase::Color32(0xffffffff)));
|
||||
return style_map;
|
||||
}
|
||||
|
||||
@@ -178,52 +241,90 @@ static StyleMapPtr CreateStyleMap(const char* id) {
|
||||
bool
|
||||
KmlFileReader::writeRideFile(Context *, const RideFile * ride, QFile &file) const
|
||||
{
|
||||
double start_lat = 0.0;
|
||||
double start_lon = 0.0;
|
||||
double end_lat = 0.0;
|
||||
double end_lon = 0.0;
|
||||
|
||||
// Create a new DOM document and setup styles et al
|
||||
kmldom::KmlFactory* kml_factory = kmldom::KmlFactory::GetFactory();
|
||||
kmldom::DocumentPtr document = kml_factory->CreateDocument();
|
||||
const char* kRadioFolderId = "radio-folder-style";
|
||||
document->add_styleselector(CreateRadioFolder(kRadioFolderId));
|
||||
document->set_styleurl(std::string("#") + kRadioFolderId);
|
||||
const char* kStyleMapId = "style-map";
|
||||
document->add_styleselector(CreateStyleMap(kStyleMapId));
|
||||
document->set_name("Golden Cheetah");
|
||||
|
||||
// add the schema elements for each data series
|
||||
SchemaPtr schemadef = CreateSchema("schema");
|
||||
// gx:SimpleArrayField ...
|
||||
if (ride->areDataPresent()->cad)
|
||||
schemadef->add_gx_simplearrayfield(CreateGxSimpleArrayField("cadence"));
|
||||
|
||||
// Note - no all available data series are added here for KML export - further additions on demand
|
||||
// general data series
|
||||
if (ride->areDataPresent()->hr)
|
||||
schemadef->add_gx_simplearrayfield(CreateGxSimpleArrayField("heartrate"));
|
||||
if (ride->areDataPresent()->watts)
|
||||
schemadef->add_gx_simplearrayfield(CreateGxSimpleArrayField("power"));
|
||||
if (ride->areDataPresent()->nm)
|
||||
schemadef->add_gx_simplearrayfield(CreateGxSimpleArrayField("torque"));
|
||||
if (ride->areDataPresent()->headwind)
|
||||
schemadef->add_gx_simplearrayfield(CreateGxSimpleArrayField("headwind"));
|
||||
schemadef->add_gx_simplearrayfield(CreateGxSimpleArrayField("heartrate", QObject::tr("heartrate").toStdString()));
|
||||
if (ride->areDataPresent()->temp)
|
||||
schemadef->add_gx_simplearrayfield(CreateGxSimpleArrayField("temperature", QObject::tr("temperature").toStdString()));
|
||||
|
||||
// run specific series
|
||||
if (ride->isRun()) {
|
||||
if (ride->areDataPresent()->rcad)
|
||||
schemadef->add_gx_simplearrayfield(CreateGxSimpleArrayField("run cadence", QObject::tr("run cadence").toStdString()));
|
||||
if (ride->areDataPresent()->rvert)
|
||||
schemadef->add_gx_simplearrayfield(CreateGxSimpleArrayField("vertical oscillation", QObject::tr("vertical oscillation").toStdString()));
|
||||
if (ride->areDataPresent()->rcontact)
|
||||
schemadef->add_gx_simplearrayfield(CreateGxSimpleArrayField("ground contact time", QObject::tr("ground contact time").toStdString()));
|
||||
} else { // Bike specific
|
||||
if (ride->areDataPresent()->cad)
|
||||
schemadef->add_gx_simplearrayfield(CreateGxSimpleArrayField("cadence", QObject::tr("cadence").toStdString()));
|
||||
if (ride->areDataPresent()->watts)
|
||||
schemadef->add_gx_simplearrayfield(CreateGxSimpleArrayField("power", QObject::tr("power").toStdString()));
|
||||
if (ride->areDataPresent()->nm)
|
||||
schemadef->add_gx_simplearrayfield(CreateGxSimpleArrayField("torque", QObject::tr("torque").toStdString()));
|
||||
if (ride->areDataPresent()->headwind)
|
||||
schemadef->add_gx_simplearrayfield(CreateGxSimpleArrayField("headwind", QObject::tr("headwind").toStdString()));
|
||||
}
|
||||
|
||||
document->add_schema(schemadef);
|
||||
|
||||
// setup trip folder (shown on lhs of google earth
|
||||
FolderPtr folder = kmldom::KmlFactory::GetFactory()->CreateFolder();
|
||||
folder->set_name("Bike Rides");
|
||||
folder->set_name(QObject::tr("Bike Rides").toStdString());
|
||||
document->add_feature(folder);
|
||||
|
||||
// Create a track for the entire ride
|
||||
GxTrackPtr track = CreateGxTrack("Entire Ride");
|
||||
PlacemarkPtr placemark = CreateGxTrackPlacemark(QString("Bike %1")
|
||||
.arg(ride->startTime().toString()).toStdString(), track);
|
||||
GxTrackPtr track = CreateGxTrack(QObject::tr("Entire Activity").toStdString());
|
||||
QString activityType;
|
||||
if (ride->isRun())
|
||||
activityType = QObject::tr("Run %1").arg(ride->startTime().toString());
|
||||
else
|
||||
activityType = QObject::tr("Bike %1").arg(ride->startTime().toString());
|
||||
|
||||
QString description = ride->getTag("Calendar Text", activityType);
|
||||
|
||||
PlacemarkPtr placemark = CreateGxTrackPlacemark(activityType.toStdString(), description.toStdString(), track);
|
||||
folder->add_feature(placemark);
|
||||
|
||||
//
|
||||
// Basic Data -- Lat/Lon/Alt and Timestamp
|
||||
//
|
||||
|
||||
foreach (RideFilePoint *datapoint, ride->dataPoints()) {
|
||||
|
||||
// lots of arsing around with dates
|
||||
// lots of arsing around with dates - use as simple functions a possible to get the right format
|
||||
// since some of the convenience functions for Date/Time and String have problems on e.g. Windows platform
|
||||
// by using pure QT functions - the conversion to expected format works fine for <when> tag
|
||||
QDateTime timestamp(ride->startTime().addSecs(datapoint->secs));
|
||||
string stdctimestamp = timestamp.toString(Qt::ISODate).toStdString() + "Z"; //<< 'Z' fixes crash!
|
||||
kmlbase::DateTime *when = kmlbase::DateTime::Create(stdctimestamp.data());
|
||||
if (datapoint->lat && datapoint->lon) track->add_when(when->GetXsdDateTime());
|
||||
QString s = timestamp.toString(Qt::ISODate) + "Z";
|
||||
if (datapoint->lat && datapoint->lon) {
|
||||
track->add_when(s.toStdString());
|
||||
if (start_lat == 0.0) {
|
||||
start_lat = datapoint->lat;
|
||||
start_lon = datapoint->lon;
|
||||
};
|
||||
end_lat = datapoint->lat;
|
||||
end_lon = datapoint->lon;
|
||||
}
|
||||
}
|
||||
|
||||
// <when> loop through the entire ride
|
||||
@@ -232,33 +333,13 @@ KmlFileReader::writeRideFile(Context *, const RideFile * ride, QFile &file) cons
|
||||
}
|
||||
|
||||
//
|
||||
// Extended Data -- cadence, heartrate, power, torque, headwind
|
||||
// Extended Data -- similar series to schema definition
|
||||
//
|
||||
ExtendedDataPtr extended = CreateExtendedData();
|
||||
track->set_extendeddata(extended);
|
||||
SchemaDataPtr schema = CreateSchemaData("schema");
|
||||
extended->add_schemadata(schema);
|
||||
|
||||
// power
|
||||
if (ride->areDataPresent()->watts) {
|
||||
GxSimpleArrayDataPtr power = CreateGxSimpleArrayData("power");
|
||||
schema->add_gx_simplearraydata(power);
|
||||
|
||||
// now create a GxSimpleArrayData
|
||||
foreach (RideFilePoint *datapoint, ride->dataPoints()) {
|
||||
if (datapoint->lat && datapoint->lon) power->add_gx_value(QString("%1").arg(datapoint->watts).toStdString());
|
||||
}
|
||||
}
|
||||
// cadence
|
||||
if (ride->areDataPresent()->cad) {
|
||||
GxSimpleArrayDataPtr cadence = CreateGxSimpleArrayData("cadence");
|
||||
schema->add_gx_simplearraydata(cadence);
|
||||
|
||||
// now create a GxSimpleArrayData
|
||||
foreach (RideFilePoint *datapoint, ride->dataPoints()) {
|
||||
if (datapoint->lat && datapoint->lon) cadence->add_gx_value(QString("%1").arg(datapoint->cad).toStdString());
|
||||
}
|
||||
}
|
||||
// heartrate
|
||||
if (ride->areDataPresent()->hr) {
|
||||
GxSimpleArrayDataPtr heartrate = CreateGxSimpleArrayData("heartrate");
|
||||
@@ -269,31 +350,108 @@ KmlFileReader::writeRideFile(Context *, const RideFile * ride, QFile &file) cons
|
||||
if (datapoint->lat && datapoint->lon) heartrate->add_gx_value(QString("%1").arg(datapoint->hr).toStdString());
|
||||
}
|
||||
}
|
||||
// torque
|
||||
if (ride->areDataPresent()->nm) {
|
||||
GxSimpleArrayDataPtr torque = CreateGxSimpleArrayData("torque");
|
||||
schema->add_gx_simplearraydata(torque);
|
||||
|
||||
// temperature
|
||||
if (ride->areDataPresent()->temp) {
|
||||
GxSimpleArrayDataPtr temperature = CreateGxSimpleArrayData("temperature");
|
||||
schema->add_gx_simplearraydata(temperature);
|
||||
|
||||
// now create a GxSimpleArrayData
|
||||
foreach (RideFilePoint *datapoint, ride->dataPoints()) {
|
||||
if (datapoint->lat && datapoint->lon) torque->add_gx_value(QString("%1").arg(datapoint->nm).toStdString());
|
||||
if (datapoint->lat && datapoint->lon) temperature->add_gx_value(QString("%1").arg(datapoint->temp).toStdString());
|
||||
}
|
||||
}
|
||||
// headwind
|
||||
if (ride->areDataPresent()->headwind) {
|
||||
GxSimpleArrayDataPtr headwind = CreateGxSimpleArrayData("headwind");
|
||||
schema->add_gx_simplearraydata(headwind);
|
||||
|
||||
// now create a GxSimpleArrayData
|
||||
foreach (RideFilePoint *datapoint, ride->dataPoints()) {
|
||||
if (datapoint->lat && datapoint->lon) headwind->add_gx_value(QString("%1").arg(datapoint->headwind).toStdString());
|
||||
if (ride->isRun()) {
|
||||
|
||||
// running cadence
|
||||
if (ride->areDataPresent()->cad) {
|
||||
GxSimpleArrayDataPtr rcadence = CreateGxSimpleArrayData("running cadence");
|
||||
schema->add_gx_simplearraydata(rcadence);
|
||||
|
||||
// now create a GxSimpleArrayData
|
||||
foreach (RideFilePoint *datapoint, ride->dataPoints()) {
|
||||
if (datapoint->lat && datapoint->lon) rcadence->add_gx_value(QString("%1").arg(datapoint->rcad).toStdString());
|
||||
}
|
||||
}
|
||||
// vertical oscillation
|
||||
if (ride->areDataPresent()->rvert) {
|
||||
GxSimpleArrayDataPtr rvert = CreateGxSimpleArrayData("vertical oscillation");
|
||||
schema->add_gx_simplearraydata(rvert);
|
||||
|
||||
// now create a GxSimpleArrayData
|
||||
foreach (RideFilePoint *datapoint, ride->dataPoints()) {
|
||||
if (datapoint->lat && datapoint->lon) rvert->add_gx_value(QString("%1").arg(datapoint->rvert).toStdString());
|
||||
}
|
||||
}
|
||||
// ground contact time
|
||||
if (ride->areDataPresent()->rcontact) {
|
||||
GxSimpleArrayDataPtr rgct = CreateGxSimpleArrayData("ground contact time");
|
||||
schema->add_gx_simplearraydata(rgct);
|
||||
|
||||
// now create a GxSimpleArrayData
|
||||
foreach (RideFilePoint *datapoint, ride->dataPoints()) {
|
||||
if (datapoint->lat && datapoint->lon) rgct->add_gx_value(QString("%1").arg(datapoint->rcontact).toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else { // Biking,...
|
||||
|
||||
// cadence
|
||||
if (ride->areDataPresent()->cad) {
|
||||
GxSimpleArrayDataPtr cadence = CreateGxSimpleArrayData("cadence");
|
||||
schema->add_gx_simplearraydata(cadence);
|
||||
|
||||
// now create a GxSimpleArrayData
|
||||
foreach (RideFilePoint *datapoint, ride->dataPoints()) {
|
||||
if (datapoint->lat && datapoint->lon) cadence->add_gx_value(QString("%1").arg(datapoint->cad).toStdString());
|
||||
}
|
||||
}
|
||||
// power
|
||||
if (ride->areDataPresent()->watts) {
|
||||
GxSimpleArrayDataPtr power = CreateGxSimpleArrayData("power");
|
||||
schema->add_gx_simplearraydata(power);
|
||||
|
||||
// now create a GxSimpleArrayData
|
||||
foreach (RideFilePoint *datapoint, ride->dataPoints()) {
|
||||
if (datapoint->lat && datapoint->lon) power->add_gx_value(QString("%1").arg(datapoint->watts).toStdString());
|
||||
}
|
||||
}
|
||||
// torque
|
||||
if (ride->areDataPresent()->nm) {
|
||||
GxSimpleArrayDataPtr torque = CreateGxSimpleArrayData("torque");
|
||||
schema->add_gx_simplearraydata(torque);
|
||||
|
||||
// now create a GxSimpleArrayData
|
||||
foreach (RideFilePoint *datapoint, ride->dataPoints()) {
|
||||
if (datapoint->lat && datapoint->lon) torque->add_gx_value(QString("%1").arg(datapoint->nm).toStdString());
|
||||
}
|
||||
}
|
||||
// headwind
|
||||
if (ride->areDataPresent()->headwind) {
|
||||
GxSimpleArrayDataPtr headwind = CreateGxSimpleArrayData("headwind");
|
||||
schema->add_gx_simplearraydata(headwind);
|
||||
|
||||
// now create a GxSimpleArrayData
|
||||
foreach (RideFilePoint *datapoint, ride->dataPoints()) {
|
||||
if (datapoint->lat && datapoint->lon) headwind->add_gx_value(QString("%1").arg(datapoint->headwind).toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// add start and end marker
|
||||
PlacemarkPtr start = CreateStartPlacemark(start_lat, start_lon);
|
||||
folder->add_feature(start);
|
||||
PlacemarkPtr end = CreateEndPlacemark(end_lat, end_lon);
|
||||
folder->add_feature(end);
|
||||
|
||||
// Create KML document
|
||||
kmldom::KmlPtr kml = kml_factory->CreateKml();
|
||||
kml->set_feature(document);
|
||||
|
||||
|
||||
// make sure the google extensions are added in!
|
||||
kmlbase::Attributes gxxmlns22;
|
||||
gxxmlns22.SetValue("gx", "http://www.google.com/kml/ext/2.2");
|
||||
|
||||
Reference in New Issue
Block a user