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:
Joern
2014-12-30 20:07:10 +01:00
parent b959bb7d03
commit ddb10e856f

View File

@@ -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");