sports-alliance / sports-lib Goto Github PK
View Code? Open in Web Editor NEWA Library for processing GPX, TCX, FIT and JSON files from services such as Strava, Movescount, Garmin, Polar etc
License: GNU Affero General Public License v3.0
A Library for processing GPX, TCX, FIT and JSON files from services such as Strava, Movescount, Garmin, Polar etc
License: GNU Affero General Public License v3.0
Hi!
I am trying to parse some tcx-files.
If there is for some reason no "Activity" in it, then the importer.tcx.tx blows up hard, because a null-check is missing.
TypeError: Cannot read property 'startDate' of undefined at /home/felix/example_project/node_modules/@sports-alliance/sports-lib/lib/events/adapters/importers/tcx/importer.tcx.js:83:63
How did the Garmin hackers escape?
They just ransomware.
(Found in the internet)
This will allow to call
stream.getFilteredData()
And be used by the consumer apps
Hi, is there a way to check if the device is paused at each point of the data stream for FIT files? I can get the total time paused but I can't see a way to check at specific times.
Cheers
I wrote a simple node app using this lib to convert TCX files from Endomondo, to GPX so I could import them into FitoTrack.
Here are the original TCX file from Endomondo and the GPX file generated after using the lib.
2020-11-09 12_18_49.0.TCX.TXT
2020-11-09 12_18_49.0.GPX.TXT
Since there's no track type FitoTrack tags it as "Other". Would it be possible to convert TCX <Activity Sport="Running">
to GPX <trk>...<type>Running</type>...</trk>
?
When importing ibi data the parser has different result than when applying the default filters
On the stats the sample numbers for each watch should be available
Used the following functions.
// For GPX you need a string
SportsLib.importFromGPX(inputFile,DOMParser).then((result) => {
// do Stuff with the file
// convert to gpx
const gpxPromise = new EventExporterGPX().getAsString(result);
gpxPromise.then((gpxString) => {
// writes the gpx to file
fs.writeFileSync(`${userPath}/result.gpx`, gpxString, 'utf8');
});
The input GPS looks like this
The output GPX looks like this
It's gone from 1348 trackpoints on the input file to 6 trackpoints on the output file.
It seems to generate the statistics ok, it just doesn't work when it comes to preserving the tracklog data from an Etrex30x GPX file.
Due to several calculations eg adding up distance the result can have many decimal places
eg
1.2348292993
During the Strava compliance tests eg strava reports distance with 1 decimal
1.2
getValue(precise)
?Library throws
TypeError: Cannot read property 'timestamp' of undefined
AT importer.fit.ts:171
File to reproduce the bug: 7b2c46-522856231.zip
Hi, just tryed your code and this seemed strange after installing via npm.
Also it should be "npm install @sports-alliance/sports-lib" in the readme
Hi,
Thanks for your hard work with this, it looks very good.
I'm looking through the imported data from a gpx file though and I can find all the data somewhere except the timestamp for the data point - I can only find the start and end times for an activity. Am I missing something?
Sorry if its obvious and I missed it.
Hi ! Thanks for your lib !
I'm unable to parse your garmin.tcx
sample or a tcx file exported from garmin connect (source Edge 1000) without getting the below error:
(node:30906) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'length' of undefined
at /home/thomas/Documents/.me/projects/quantified-self-lib/src/events/adapters/importers/tcx/importer.tcx.ts:99:33
at Array.reduce (<anonymous>)
at Function.EventImporterTCX.getPoints (/home/thomas/Documents/.me/projects/quantified-self-lib/src/events/adapters/importers/tcx/importer.tcx.ts:96:44)
at /home/thomas/Documents/.me/projects/quantified-self-lib/src/events/adapters/importers/tcx/importer.tcx.ts:74:18
at Array.map (<anonymous>)
at /home/thomas/Documents/.me/projects/quantified-self-lib/src/events/adapters/importers/tcx/importer.tcx.ts:73:67
at Array.map (<anonymous>)
at /home/thomas/Documents/.me/projects/quantified-self-lib/src/events/adapters/importers/tcx/importer.tcx.ts:41:10
at new Promise (<anonymous>)
at Function.EventImporterTCX.getFromXML (/home/thomas/Documents/.me/projects/quantified-self-lib/src/events/adapters/importers/tcx/importer.tcx.ts:38:12)
(node:30906) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:30906) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Thanks for your help! Need your library for sure :)
Hey Dimi!
Small issue while parsing file mytrail_export.gpx (mytrail_export.zip). It produces the following error: TypeError: Cannot read property 'length' of undefined
Error is raised at importer.gpx.ts:49:136
:
const endDate = isActivity ?
new Date(trackOrRoute.trkseg[trackOrRoute.trkseg.length - 1].trkpt[trackOrRoute.trkseg
[trackOrRoute.trkseg.length - 1].trkpt.length // HERE !
- 1].time[0]) :
new Date(startDate.getTime() + samples.length * 1000);
Here a jasmine which reproduce the bug on the file.
it('should parse a MyTrail GPX file', done => {
// Given
const path = __dirname + '/fixtures/others/mytrail_export.gpx';
const gpxString = fs.readFileSync(path).toString();
// When
const eventInterfacePromise = SportsLib.importFromGPX(gpxString);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(event.getFirstActivity().type).toBeDefined();
done();
});
Not sure how to fix it properly. Let me know if you need more investigation from me.
(File is coming from my elevate app testers)
This library has been of great help, but the guide should be clear that in NodeJS usage, another DOMParser (e.g. xmldom) should be included as an argument in 'importFromGPX()'. Had to trawl the importer js files to find out.
In the case of Node JS:
npm i xmldom
import {SportsLib} from '@sports-alliance/sports-lib';
import { DOMParser } from 'xmldom'
// For GPX you need a string
const gpxString = 'Some string from a file etc';
SportsLib.importFromGPX(gpxString, DOMParser).then((event)=>{
...
});
Hello,
Is this library maintained? There (almost) no for long time. Also it looks like the latest release version is not the same as code in the develop branch.
I wanted to submit a PR to fix issue regarding summary first fit files (ref link: https://forums.garmin.com/developer/fit-sdk/b/news-announcements/posts/important-fit-activity-file-message-change) but if the latest version is not released yet I hesitate to do it...
Question:
Hi, is it possible to convert TCX/GPS to FIT? or even just the ability to create a FIT file/stream with this? I've been looking for a JS library to create FIT files. If not, any chance this might be added in future?
Cheers
A max speed of 3.1m/s is detected on this activity:
Should be 33m/s or 119kph. I did it for sure: https://www.youtube.com/watch?v=Lc6SdTDiAj4 :) :)
The problem is in GXParser depend. which DOMParser is not included
(node:2030) UnhandledPromiseRejectionWarning: ReferenceError: DOMParser is not defined
at GXParser (/mnt/c/greyp.workspace/playground/g6simulator/node_modules/gxparser/GXParser.js:40:16)
at /mnt/c/greyp.workspace/playground/g6simulator/node_modules/quantified-self-lib/lib/events/adapters/importers/gpx/importer.gpx.js:34:29
TypeErrorTypeError: Cannot set property 'sessions' of undefined
line 145 of grade-calculator.ts
const currentAltitude = numericAltitudeStream[i + nextIndex] || previousDistance;
should be
const currentAltitude = numericAltitudeStream[i + nextIndex] || previousAltitude;
Hey guys,
I thought I'd contribute with a little code sample.
Hope this helps someone / somehow.
filename: fit-converter.js
'use strict';
import fs from 'fs';
import sportsLibPkg from '@sports-alliance/sports-lib';
import exporterPkg from '@sports-alliance/sports-lib/lib/events/adapters/exporters/exporter.gpx.js'
const { SportsLib } = sportsLibPkg;
const { EventExporterGPX } = exporterPkg;
// Input and output file path
const inputFilePath = '/tmp/test.fit';
const outputGpxFilePath = '/tmp/test.gpx';
// reads the FIT file into memory
const inputFile = fs.readFileSync(inputFilePath, null);
if (inputFile && inputFile.buffer) {
const inputFileBuffer = inputFile.buffer;
// uses lib to read the FIT file
SportsLib.importFromFit(inputFileBuffer).then((event) => {
// convert to gpx
const gpxPromise = new EventExporterGPX().getAsString(event);
gpxPromise.then((gpxString) => {
// writes the gpx to file
fs.writeFileSync(outputGpxFilePath, gpxString, (wError) => {
if (error) {
console.error('Ooops, something went wrong while saving the GPX file, see details below.');
console.error(JSON.stringify(wError));
}
});
// all done, celebrate!
console.log('Converted FIT file to GPX successfully!');
console.log('GPX file saved here: ' + outputGpxFilePath);
}).catch((cError) => {
console.error('Ooops, something went wrong while converting the FIT file, see details below');
console.error(JSON.stringify(cError));
});
});
} else {
console.error('Ooops, could not read the inputFile or it does not exists, see details below');
console.error(JSON.stringify(inputFilePath));
}
to run it:
node fit-converter.js
requirements: a "/tmp" folder with a valid test.fit file in it.
Many thanks for the easy to use lib!
Hi @thomaschampagne I might need your help here.
I am having some calc inconsistency when trying to figure out some averages. Apparently I am doing something wrong, but I cannot figure it out.
I have an array of speed samples example:
[
3.3,
3.3,
3.32,
3.32,
3.22,
3.22,
3.14,
3.14,
3,
3,
3,
3,
3.02,
3.02,
2.96,
2.96,
2.92,
2.92,
2.88,
2.88,
2.88,
2.88,
2.86,
2.86,
2.86,
2.86,
2.86,
2.86,
2.88,
2.88,
2.88,
2.88,
2.86,
2.86,
2.84,
2.84,
2.86,
2.86,
2.88,
2.88,
2.9,
2.9,
2.86,
2.86,
2.84,
2.84,
2.84,
2.84,
2.82,
2.82,
2.8,
2.8,
2.64,
2.64,
0,
0,
2.22,
2.22,
2.3,
2.3,
2.4,
2.4,
2.44,
2.44,
2.58,
2.58,
2.6,
2.6,
2.62,
2.62,
2.62,
2.62,
2.58,
2.58,
2.54,
2.54,
2.52,
2.52,
2.5,
2.5,
2.5,
2.5,
2.5,
2.5,
2.48,
2.48,
2.46,
2.46,
2.44,
2.44,
2.44,
2.44,
2.42,
2.42,
2.42,
2.42,
2.42,
2.42,
2.42,
2.42
]
Now in order to find the average speed I do
[
3.3,
3.3,
3.32,
3.32,
3.22,
3.22,
3.14,
3.14,
3,
3,
3,
3,
3.02,
3.02,
2.96,
2.96,
2.92,
2.92,
2.88,
2.88,
2.88,
2.88,
2.86,
2.86,
2.86,
2.86,
2.86,
2.86,
2.88,
2.88,
2.88,
2.88,
2.86,
2.86,
2.84,
2.84,
2.86,
2.86,
2.88,
2.88,
2.9,
2.9,
2.86,
2.86,
2.84,
2.84,
2.84,
2.84,
2.82,
2.82,
2.8,
2.8,
2.64,
2.64,
0,
0,
2.22,
2.22,
2.3,
2.3,
2.4,
2.4,
2.44,
2.44,
2.58,
2.58,
2.6,
2.6,
2.62,
2.62,
2.62,
2.62,
2.58,
2.58,
2.54,
2.54,
2.52,
2.52,
2.5,
2.5,
2.5,
2.5,
2.5,
2.5,
2.48,
2.48,
2.46,
2.46,
2.44,
2.44,
2.44,
2.44,
2.42,
2.42,
2.42,
2.42,
2.42,
2.42,
2.42,
2.42
].reduce(function(acc, val) { return acc + val; }, 0)/100
The result is: (m/s)
2.670800000000001
Now to convert to pace I do
1000/2.670800000000001 = 374.41964954320787
So 374.41964954320787 seconds
Now if I first convert that array of speed values to pace with the same conversion like
speedArray.map(v => 1000/v)
I get this array
[
303.03030303030306,
303.03030303030306,
301.20481927710847,
301.20481927710847,
310.55900621118013,
310.55900621118013,
318.4713375796178,
318.4713375796178,
333.3333333333333,
333.3333333333333,
333.3333333333333,
333.3333333333333,
331.12582781456956,
331.12582781456956,
337.83783783783787,
337.83783783783787,
342.4657534246575,
342.4657534246575,
347.22222222222223,
347.22222222222223,
347.22222222222223,
347.22222222222223,
349.65034965034965,
349.65034965034965,
349.65034965034965,
349.65034965034965,
349.65034965034965,
349.65034965034965,
347.22222222222223,
347.22222222222223,
347.22222222222223,
347.22222222222223,
349.65034965034965,
349.65034965034965,
352.11267605633805,
352.11267605633805,
349.65034965034965,
349.65034965034965,
347.22222222222223,
347.22222222222223,
344.82758620689657,
344.82758620689657,
349.65034965034965,
349.65034965034965,
352.11267605633805,
352.11267605633805,
352.11267605633805,
352.11267605633805,
354.6099290780142,
354.6099290780142,
357.14285714285717,
357.14285714285717,
378.78787878787875,
378.78787878787875,
Infinity,
Infinity,
450.45045045045043,
450.45045045045043,
434.7826086956522,
434.7826086956522,
416.6666666666667,
416.6666666666667,
409.8360655737705,
409.8360655737705,
387.5968992248062,
387.5968992248062,
384.6153846153846,
384.6153846153846,
381.6793893129771,
381.6793893129771,
381.6793893129771,
381.6793893129771,
387.5968992248062,
387.5968992248062,
393.7007874015748,
393.7007874015748,
396.8253968253968,
396.8253968253968,
400,
400,
400,
400,
400,
400,
403.2258064516129,
403.2258064516129,
406.5040650406504,
406.5040650406504,
409.8360655737705,
409.8360655737705,
409.8360655737705,
409.8360655737705,
413.22314049586777,
413.22314049586777,
413.22314049586777,
413.22314049586777,
413.22314049586777,
413.22314049586777,
413.22314049586777,
413.22314049586777
]
Now running the save AVG calculation on that
Like this .filter(v => v !== Infinity).reduce(function(acc, val) { return acc + val; }, 0)/100
The AVG pace of those samples is:
362.8961108433509
Which is not the same as converting the AVG SPEED to Pace
What can I be doing wrong? (Working on GAP)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.