diff --git a/gpx/.gitignore b/gpx/.gitignore deleted file mode 100644 index 76add878f..000000000 --- a/gpx/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules -dist \ No newline at end of file diff --git a/gpx/package-lock.json b/gpx/package-lock.json deleted file mode 100644 index dde856dd3..000000000 --- a/gpx/package-lock.json +++ /dev/null @@ -1,1829 +0,0 @@ -{ - "name": "gpx", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "gpx", - "version": "1.0.0", - "hasInstallScript": true, - "dependencies": { - "fast-xml-parser": "^4.5.0", - "immer": "^10.1.1" - }, - "devDependencies": { - "@types/geojson": "^7946.0.14", - "@types/node": "^20.16.10", - "@typescript-eslint/parser": "^8.22.0", - "prettier": "^3.4.2", - "ts-node": "^10.9.2", - "typescript": "^5.6.2" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", - "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/core": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", - "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", - "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "9.19.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.19.0.tgz", - "integrity": "sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", - "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@eslint/core": "^0.10.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@types/geojson": { - "version": "7946.0.14", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", - "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==", - "dev": true - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@types/node": { - "version": "20.16.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.10.tgz", - "integrity": "sha512-vQUKgWTjEIRFCvK6CyriPH3MZYiYlNy0fKiEYHWbcoWLEgs4opurGGKlebrTLqdSMIbXImH6XExNiIyNUv3WpA==", - "dev": true, - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.22.0.tgz", - "integrity": "sha512-MqtmbdNEdoNxTPzpWiWnqNac54h8JDAmkWtJExBVVnSrSmi9z+sZUt0LfKqk9rjqmKOIeRhO4fHHJ1nQIjduIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.22.0", - "@typescript-eslint/types": "8.22.0", - "@typescript-eslint/typescript-estree": "8.22.0", - "@typescript-eslint/visitor-keys": "8.22.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.22.0.tgz", - "integrity": "sha512-/lwVV0UYgkj7wPSw0o8URy6YI64QmcOdwHuGuxWIYznO6d45ER0wXUbksr9pYdViAofpUCNJx/tAzNukgvaaiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.22.0", - "@typescript-eslint/visitor-keys": "8.22.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.22.0.tgz", - "integrity": "sha512-0S4M4baNzp612zwpD4YOieP3VowOARgK2EkN/GBn95hpyF8E2fbMT55sRHWBq+Huaqk3b3XK+rxxlM8sPgGM6A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.22.0.tgz", - "integrity": "sha512-SJX99NAS2ugGOzpyhMza/tX+zDwjvwAtQFLsBo3GQxiGcvaKlqGBkmZ+Y1IdiSi9h4Q0Lr5ey+Cp9CGWNY/F/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.22.0", - "@typescript-eslint/visitor-keys": "8.22.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.22.0.tgz", - "integrity": "sha512-AWpYAXnUgvLNabGTy3uBylkgZoosva/miNd1I8Bz3SjotmQPbVqhO4Cczo8AsZ44XVErEBPr/CRSgaj8sG7g0w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.22.0", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peer": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0", - "peer": true - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.19.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.19.0.tgz", - "integrity": "sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.10.0", - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.19.0", - "@eslint/plugin-kit": "^0.2.5", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.1", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.2.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", - "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "acorn": "^8.14.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "peer": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/fast-xml-parser": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", - "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - }, - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - } - ], - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/fastq": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", - "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", - "dev": true, - "license": "ISC", - "peer": true - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/immer": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", - "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC", - "peer": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", - "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/semver": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", - "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-api-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", - "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/gpx/package.json b/gpx/package.json deleted file mode 100644 index 3f6318aa1..000000000 --- a/gpx/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "gpx", - "version": "1.0.0", - "type": "module", - "exports": "./dist/index.js", - "types": "dist/index.d.ts", - "repository": { - "type": "git", - "url": "https://github.com/gpxstudio/gpx.studio.git", - "directory": "gpx" - }, - "private": true, - "dependencies": { - "fast-xml-parser": "^4.5.0", - "immer": "^10.1.1" - }, - "devDependencies": { - "@types/geojson": "^7946.0.14", - "@types/node": "^20.16.10", - "@typescript-eslint/parser": "^8.22.0", - "prettier": "^3.4.2", - "ts-node": "^10.9.2", - "typescript": "^5.6.2" - }, - "scripts": { - "build": "tsc", - "postinstall": "npm run build", - "lint": "prettier --check . --config ../.prettierrc && eslint .", - "format": "prettier --write . --config ../.prettierrc" - } -} diff --git a/gpx/src/gpx.ts b/gpx/src/gpx.ts deleted file mode 100644 index b7ab9da6b..000000000 --- a/gpx/src/gpx.ts +++ /dev/null @@ -1,1894 +0,0 @@ -import { ramerDouglasPeucker } from './simplify'; -import { GPXStatistics, GPXStatisticsGroup, TrackPointLocalStatistics } from './statistics'; -import { - Coordinates, - GPXFileAttributes, - GPXFileType, - LineStyleExtension, - Link, - Metadata, - RouteType, - TrackExtensions, - TrackPointExtensions, - TrackPointType, - TrackSegmentType, - TrackType, - WaypointType, -} from './types'; -import { immerable, isDraft, original, freeze } from 'immer'; - -function cloneJSON(obj: T): T { - if (obj === undefined) { - return undefined; - } - if (obj === null || typeof obj !== 'object') { - return null; - } - return JSON.parse(JSON.stringify(obj)); -} - -// An abstract class that groups functions that need to be computed recursively in the GPX file hierarchy -export abstract class GPXTreeElement> { - _data: { [key: string]: any } = {}; - - abstract isLeaf(): boolean; - abstract get children(): Array; - - abstract getNumberOfTrackPoints(): number; - abstract getStartTimestamp(): Date | undefined; - abstract getEndTimestamp(): Date | undefined; - abstract getSegments(): TrackSegment[]; - abstract getTrackPoints(): TrackPoint[]; - - abstract toGeoJSON(): - | GeoJSON.Feature - | GeoJSON.Feature[] - | GeoJSON.FeatureCollection - | GeoJSON.FeatureCollection[]; - - // Producers - abstract _reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date); -} - -export type AnyGPXTreeElement = GPXTreeElement>; - -// An abstract class that can be extended to facilitate functions working similarly with Tracks and TrackSegments -abstract class GPXTreeNode> extends GPXTreeElement { - isLeaf(): boolean { - return false; - } - - getNumberOfTrackPoints(): number { - return this.children.reduce((acc, child) => acc + child.getNumberOfTrackPoints(), 0); - } - - getStartTimestamp(): Date | undefined { - if (this.children.length === 0) { - return undefined; - } - return this.children[0].getStartTimestamp(); - } - - getEndTimestamp(): Date | undefined { - if (this.children.length === 0) { - return undefined; - } - return this.children[this.children.length - 1].getEndTimestamp(); - } - - getSegments(): TrackSegment[] { - return this.children.flatMap((child) => child.getSegments()); - } - - getTrackPoints(): TrackPoint[] { - return this.children.flatMap((child) => child.getTrackPoints()); - } - - // Producers - _reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date) { - let og = getOriginal(this); - if (!originalNextTimestamp && !newPreviousTimestamp) { - originalNextTimestamp = og.getEndTimestamp(); - newPreviousTimestamp = og.getStartTimestamp(); - } - - this.children.reverse(); - - for (let i = 0; i < og.children.length; i++) { - let originalStartTimestamp = - og.children[og.children.length - i - 1].getStartTimestamp(); - - this.children[i]._reverse(originalNextTimestamp, newPreviousTimestamp); - - originalNextTimestamp = originalStartTimestamp; - newPreviousTimestamp = this.children[i].getEndTimestamp(); - } - } -} - -// An abstract class that TrackSegment extends to implement the GPXTreeElement interface -abstract class GPXTreeLeaf extends GPXTreeElement { - isLeaf(): boolean { - return true; - } - - get children(): Array { - return []; - } -} - -// A class that represents a GPX file -export class GPXFile extends GPXTreeNode { - [immerable] = true; - - attributes: GPXFileAttributes; - metadata: Metadata; - wpt: Waypoint[]; - trk: Track[]; - rte: RouteType[]; - - constructor(gpx?: (GPXFileType & { _data?: any }) | GPXFile) { - super(); - if (gpx) { - this.attributes = gpx.attributes; - this.metadata = gpx.metadata ?? {}; - this.metadata.author = { - name: 'gpx.studio', - link: { - attributes: { - href: 'https://gpx.studio', - }, - }, - }; - this.wpt = gpx.wpt - ? gpx.wpt.map((waypoint, index) => new Waypoint(waypoint, index)) - : []; - this.trk = gpx.trk ? gpx.trk.map((track) => new Track(track)) : []; - if (gpx.rte && gpx.rte.length > 0) { - this.trk = this.trk.concat(gpx.rte.map((route) => convertRouteToTrack(route))); - } - if (gpx.hasOwnProperty('_data')) { - this._data = gpx._data; - } - if (!this._data.hasOwnProperty('style')) { - let style = this.getStyle(); - let fileStyle = {}; - if (style.color.length === 1) { - fileStyle['gpx_style:color'] = style.color[0]; - } - if (style.opacity.length === 1) { - fileStyle['gpx_style:opacity'] = style.opacity[0]; - } - if (style.width.length === 1) { - fileStyle['gpx_style:width'] = style.width[0]; - } - if (Object.keys(fileStyle).length > 0) { - this.setStyle(fileStyle); - } - } - } else { - this.attributes = {}; - this.metadata = {}; - this.wpt = []; - this.trk = [new Track()]; - } - - this.trk.forEach((track, trackIndex) => { - track._data['trackIndex'] = trackIndex; - track.trkseg.forEach((segment, segmentIndex) => { - segment._data['trackIndex'] = trackIndex; - segment._data['segmentIndex'] = segmentIndex; - }); - }); - } - - get children(): Array { - return this.trk; - } - - getSegment(trackIndex: number, segmentIndex: number): TrackSegment { - return this.trk[trackIndex].children[segmentIndex]; - } - - forEachSegment( - callback: (segment: TrackSegment, trackIndex: number, segmentIndex: number) => void - ) { - this.trk.forEach((track, trackIndex) => { - track.children.forEach((segment, segmentIndex) => { - callback(segment, trackIndex, segmentIndex); - }); - }); - } - - getStatistics(): GPXStatisticsGroup { - let statistics = new GPXStatisticsGroup(); - this.forEachSegment((segment) => { - statistics.add(segment.getStatistics()); - }); - return statistics; - } - - getStyle(defaultColor?: string): MergedLineStyles { - const style = this.trk - .map((track) => track.getStyle()) - .reduce( - (acc, style) => { - if ( - style && - style['gpx_style:color'] && - !acc.color.includes(style['gpx_style:color']) - ) { - acc.color.push(style['gpx_style:color']); - } - if ( - style && - style['gpx_style:opacity'] && - !acc.opacity.includes(style['gpx_style:opacity']) - ) { - acc.opacity.push(style['gpx_style:opacity']); - } - if ( - style && - style['gpx_style:width'] && - !acc.width.includes(style['gpx_style:width']) - ) { - acc.width.push(style['gpx_style:width']); - } - return acc; - }, - { - color: [], - opacity: [], - width: [], - } - ); - if (style.color.length === 0 && defaultColor) { - style.color.push(defaultColor); - } - return style; - } - - clone(): GPXFile { - return new GPXFile({ - attributes: cloneJSON(this.attributes), - metadata: cloneJSON(this.metadata), - wpt: this.wpt.map((waypoint) => waypoint.clone()), - trk: this.trk.map((track) => track.clone()), - rte: [], - _data: cloneJSON(this._data), - }); - } - - toGeoJSON(): GeoJSON.FeatureCollection { - return { - type: 'FeatureCollection', - features: this.children.flatMap((child) => child.toGeoJSON()), - }; - } - - toGPXFileType(exclude: string[] = []): GPXFileType { - let file: GPXFileType = { - attributes: cloneJSON(this.attributes), - metadata: {}, - wpt: this.wpt.map((wpt) => wpt.toWaypointType(exclude)), - trk: this.trk.map((track) => track.toTrackType(exclude)), - rte: [], - }; - if (this.metadata) { - if (this.metadata.name) { - file.metadata.name = this.metadata.name; - } - if (this.metadata.desc) { - file.metadata.desc = this.metadata.desc; - } - if (this.metadata.author) { - file.metadata.author = cloneJSON(this.metadata.author); - } - if (this.metadata.link) { - file.metadata.link = cloneJSON(this.metadata.link); - } - if (this.metadata.time && !exclude.includes('time')) { - file.metadata.time = this.metadata.time; - } - } - return file; - } - - // Producers - replaceTracks(start: number, end: number, tracks: Track[]) { - if (this._data.style) { - tracks.forEach((track) => track.setStyle(this._data.style, false)); - } - this.trk.splice(start, end - start + 1, ...tracks); - } - - replaceTrackSegments(trackIndex: number, start: number, end: number, segments: TrackSegment[]) { - this.trk[trackIndex].replaceTrackSegments(start, end, segments); - } - - replaceTrackPoints( - trackIndex: number, - segmentIndex: number, - start: number, - end: number, - points: TrackPoint[], - speed?: number, - startTime?: Date - ) { - this.trk[trackIndex].replaceTrackPoints(segmentIndex, start, end, points, speed, startTime); - } - - replaceWaypoints(start: number, end: number, waypoints: Waypoint[]) { - this.wpt.splice(start, end - start + 1, ...waypoints); - } - - reverse() { - return this._reverse(this.getEndTimestamp(), this.getStartTimestamp()); - } - - reverseTrack(trackIndex: number) { - this.trk[trackIndex]._reverse(); - } - - reverseTrackSegment(trackIndex: number, segmentIndex: number) { - this.trk[trackIndex].reverseTrackSegment(segmentIndex); - } - - roundTrip() { - this.trk.forEach((track) => { - track.roundTrip(); - }); - } - - roundTripTrack(trackIndex: number) { - this.trk[trackIndex].roundTrip(); - } - - roundTripTrackSegment(trackIndex: number, segmentIndex: number) { - this.trk[trackIndex].roundTripTrackSegment(segmentIndex); - } - - crop(start: number, end: number, trackIndices?: number[], segmentIndices?: number[]) { - let i = 0; - let trackIndex = 0; - while (i < this.trk.length) { - let length = this.trk[i].getNumberOfTrackPoints(); - if (trackIndices === undefined || trackIndices.includes(trackIndex)) { - if (start >= length || end < 0) { - this.trk.splice(i, 1); - } else { - if (start > 0 || end < length - 1) { - this.trk[i].crop( - Math.max(0, start), - Math.min(length - 1, end), - segmentIndices - ); - } - i++; - } - start -= length; - end -= length; - } else { - i++; - } - trackIndex++; - } - } - - clean( - bounds: [Coordinates, Coordinates], - inside: boolean, - deleteTrackPoints: boolean, - deleteWaypoints: boolean, - trackIndices?: number[], - segmentIndices?: number[], - waypointIndices?: number[] - ) { - if (deleteTrackPoints) { - let i = 0; - let trackIndex = 0; - while (i < this.trk.length) { - if (trackIndices === undefined || trackIndices.includes(trackIndex)) { - this.trk[i].clean(bounds, inside, segmentIndices); - if (this.trk[i].getNumberOfTrackPoints() === 0) { - this.trk.splice(i, 1); - } else { - i++; - } - } else { - i++; - } - trackIndex++; - } - } - if (deleteWaypoints) { - let og = getOriginal(this); // Read as much as possible from the original object because it is faster - let wpt = og.wpt.filter((point, waypointIndex) => { - if (waypointIndices === undefined || waypointIndices.includes(waypointIndex)) { - let inBounds = - point.attributes.lat >= bounds[0].lat && - point.attributes.lat <= bounds[1].lat && - point.attributes.lon >= bounds[0].lon && - point.attributes.lon <= bounds[1].lon; - return inBounds !== inside; - } else { - return true; - } - }); - this.wpt = freeze(wpt); // Pre-freeze the array, faster as well - } - } - - changeTimestamps( - startTime: Date, - speed: number, - ratio: number, - trackIndex?: number, - segmentIndex?: number - ) { - let lastPoint = undefined; - this.trk.forEach((track, index) => { - if (trackIndex === undefined || trackIndex === index) { - track.changeTimestamps(startTime, speed, ratio, lastPoint, segmentIndex); - } - }); - } - - createArtificialTimestamps( - startTime: Date, - totalTime: number, - trackIndex?: number, - segmentIndex?: number - ) { - let lastPoint = undefined; - this.trk.forEach((track, index) => { - if (trackIndex === undefined || trackIndex === index) { - track.createArtificialTimestamps(startTime, totalTime, lastPoint, segmentIndex); - } - }); - } - - addElevation( - elevations: number[], - trackIndices?: number[], - segmentIndices?: number[], - waypointIndices?: number[] - ) { - let index = 0; - this.trk.forEach((track, trackIndex) => { - if (trackIndices === undefined || trackIndices.includes(trackIndex)) { - track.trkseg.forEach((segment, segmentIndex) => { - if (segmentIndices === undefined || segmentIndices.includes(segmentIndex)) { - segment.trkpt.forEach((point) => { - point.ele = elevations[index++]; - }); - } - }); - } - }); - this.wpt.forEach((waypoint, waypointIndex) => { - if (waypointIndices === undefined || waypointIndices.includes(waypointIndex)) { - waypoint.ele = elevations[index++]; - } - }); - elevations.splice(0, index); - } - - setStyle(style: LineStyleExtension) { - this.trk.forEach((track) => { - track.setStyle(style); - }); - if (!this._data.style) { - this._data.style = {}; - } - if (style['gpx_style:color']) { - this._data.style.color = style['gpx_style:color'].replace('#', ''); - } - if (style['gpx_style:opacity']) { - this._data.style.opacity = style['gpx_style:opacity']; - } - if (style['gpx_style:width']) { - this._data.style.width = style['gpx_style:width']; - } - } - - setHidden(hidden: boolean, trackIndices?: number[], segmentIndices?: number[]) { - let allHidden = hidden; - this.trk.forEach((track, index) => { - if (trackIndices === undefined || trackIndices.includes(index)) { - track.setHidden(hidden, segmentIndices); - } else { - allHidden = allHidden && track._data.hidden === true; - } - }); - this.wpt.forEach((waypoint) => { - if (trackIndices === undefined && segmentIndices === undefined) { - waypoint.setHidden(hidden); - } else { - allHidden = allHidden && waypoint._data.hidden === true; - } - }); - - if (trackIndices === undefined && segmentIndices === undefined) { - this._data.hiddenWpt = hidden; - } - - this._data.hidden = allHidden; - } - - setHiddenWaypoints(hidden: boolean, waypointIndices?: number[]) { - let allHiddenWpt = hidden; - this.wpt.forEach((waypoint, index) => { - if (waypointIndices === undefined || waypointIndices.includes(index)) { - waypoint.setHidden(hidden); - } else { - allHiddenWpt = allHiddenWpt && waypoint._data.hidden === true; - } - }); - - let allHiddenTrk = true; - this.trk.forEach((track) => { - allHiddenTrk = allHiddenTrk && track._data.hidden === true; - }); - - this._data.hiddenWpt = allHiddenWpt; - this._data.hidden = allHiddenTrk && allHiddenWpt; - } -} - -// A class that represents a Track in a GPX file -export class Track extends GPXTreeNode { - [immerable] = true; - - name?: string; - cmt?: string; - desc?: string; - src?: string; - link?: Link; - type?: string; - extensions?: TrackExtensions; - trkseg: TrackSegment[]; - - constructor(track?: (TrackType & { _data?: any }) | Track) { - super(); - if (track) { - this.name = track.name; - this.cmt = track.cmt; - this.desc = track.desc; - this.src = track.src; - this.link = track.link; - this.type = track.type; - this.trkseg = track.trkseg ? track.trkseg.map((seg) => new TrackSegment(seg)) : []; - this.extensions = track.extensions; - if (track.hasOwnProperty('_data')) { - this._data = track._data; - } - } else { - this.trkseg = [new TrackSegment()]; - } - } - - get children(): Array { - return this.trkseg; - } - - clone(): Track { - return new Track({ - name: this.name, - cmt: this.cmt, - desc: this.desc, - src: this.src, - link: cloneJSON(this.link), - type: this.type, - extensions: cloneJSON(this.extensions), - trkseg: this.trkseg.map((seg) => seg.clone()), - _data: cloneJSON(this._data), - }); - } - - getStyle(): LineStyleExtension | undefined { - if (this.extensions && this.extensions['gpx_style:line']) { - if (this.extensions['gpx_style:line']['gpx_style:color']) { - return { - ...this.extensions['gpx_style:line'], - ['gpx_style:color']: `#${this.extensions['gpx_style:line']['gpx_style:color']}`, - }; - } - return this.extensions['gpx_style:line']; - } - return undefined; - } - - getValidStyle(): LineStyleExtension | undefined { - if (this.extensions && this.extensions['gpx_style:line']) { - return { - 'gpx_style:color': this.extensions['gpx_style:line']['gpx_style:color'], - 'gpx_style:opacity': this.extensions['gpx_style:line']['gpx_style:opacity'], - 'gpx_style:width': this.extensions['gpx_style:line']['gpx_style:width'], - }; - } - return undefined; - } - - toGeoJSON(): GeoJSON.Feature[] { - return this.children.map((child) => { - let geoJSON = child.toGeoJSON(); - if (this.extensions && this.extensions['gpx_style:line']) { - if (this.extensions['gpx_style:line']['gpx_style:color']) { - geoJSON.properties['color'] = - `#${this.extensions['gpx_style:line']['gpx_style:color']}`; - } - if (this.extensions['gpx_style:line']['gpx_style:opacity']) { - geoJSON.properties['opacity'] = - this.extensions['gpx_style:line']['gpx_style:opacity']; - } - if (this.extensions['gpx_style:line']['gpx_style:width']) { - geoJSON.properties['width'] = - this.extensions['gpx_style:line']['gpx_style:width']; - } - } - return geoJSON; - }); - } - - toTrackType(exclude: string[] = []): TrackType { - return { - name: this.name, - cmt: this.cmt, - desc: this.desc, - src: this.src, - link: this.link, - type: this.type, - extensions: - this.extensions && this.extensions['gpx_style:line'] - ? { - 'gpx_style:line': this.getValidStyle(), - } - : undefined, - trkseg: this.trkseg.map((seg) => seg.toTrackSegmentType(exclude)), - }; - } - - // Producers - replaceTrackSegments(start: number, end: number, segments: TrackSegment[]) { - this.trkseg.splice(start, end - start + 1, ...segments); - } - - replaceTrackPoints( - segmentIndex: number, - start: number, - end: number, - points: TrackPoint[], - speed?: number, - startTime?: Date - ) { - this.trkseg[segmentIndex].replaceTrackPoints(start, end, points, speed, startTime); - } - - reverseTrackSegment(segmentIndex: number) { - this.trkseg[segmentIndex]._reverse( - this.trkseg[segmentIndex].getEndTimestamp(), - this.trkseg[segmentIndex].getStartTimestamp() - ); - } - - roundTrip() { - this.trkseg.forEach((segment) => { - segment.roundTrip(); - }); - } - - roundTripTrackSegment(segmentIndex: number) { - this.trkseg[segmentIndex].roundTrip(); - } - - crop(start: number, end: number, segmentIndices?: number[]) { - let i = 0; - let segmentIndex = 0; - while (i < this.trkseg.length) { - let length = this.trkseg[i].getNumberOfTrackPoints(); - if (segmentIndices === undefined || segmentIndices.includes(segmentIndex)) { - if (start >= length || end < 0) { - this.trkseg.splice(i, 1); - } else { - if (start > 0 || end < length - 1) { - this.trkseg[i].crop(Math.max(0, start), Math.min(length - 1, end)); - } - i++; - } - start -= length; - end -= length; - } else { - i++; - } - segmentIndex++; - } - } - - clean(bounds: [Coordinates, Coordinates], inside: boolean, segmentIndices?: number[]) { - let i = 0; - let segmentIndex = 0; - while (i < this.trkseg.length) { - if (segmentIndices === undefined || segmentIndices.includes(segmentIndex)) { - this.trkseg[i].clean(bounds, inside); - if (this.trkseg[i].getNumberOfTrackPoints() === 0) { - this.trkseg.splice(i, 1); - } else { - i++; - } - } else { - i++; - } - segmentIndex++; - } - } - - changeTimestamps( - startTime: Date, - speed: number, - ratio: number, - lastPoint?: TrackPoint, - segmentIndex?: number - ) { - this.trkseg.forEach((segment, index) => { - if (segmentIndex === undefined || segmentIndex === index) { - segment.changeTimestamps(startTime, speed, ratio, lastPoint); - if (segment.trkpt.length > 0) { - lastPoint = segment.trkpt[segment.trkpt.length - 1]; - } - } - }); - } - - createArtificialTimestamps( - startTime: Date, - totalTime: number, - lastPoint: TrackPoint | undefined, - segmentIndex?: number - ) { - this.trkseg.forEach((segment, index) => { - if (segmentIndex === undefined || segmentIndex === index) { - segment.createArtificialTimestamps(startTime, totalTime, lastPoint); - if (segment.trkpt.length > 0) { - lastPoint = segment.trkpt[segment.trkpt.length - 1]; - } - } - }); - } - - setStyle(style: LineStyleExtension, force: boolean = true) { - if (!this.extensions) { - this.extensions = {}; - } - if (!this.extensions['gpx_style:line']) { - this.extensions['gpx_style:line'] = {}; - } - if ( - style['gpx_style:color'] !== undefined && - (force || this.extensions['gpx_style:line']['gpx_style:color'] === undefined) - ) { - this.extensions['gpx_style:line']['gpx_style:color'] = style['gpx_style:color'].replace( - '#', - '' - ); - } - if ( - style['gpx_style:opacity'] !== undefined && - (force || this.extensions['gpx_style:line']['gpx_style:opacity'] === undefined) - ) { - this.extensions['gpx_style:line']['gpx_style:opacity'] = style['gpx_style:opacity']; - } - if ( - style['gpx_style:width'] !== undefined && - (force || this.extensions['gpx_style:line']['gpx_style:width'] === undefined) - ) { - this.extensions['gpx_style:line']['gpx_style:width'] = style['gpx_style:width']; - } - } - - setHidden(hidden: boolean, segmentIndices?: number[]) { - let allHidden = hidden; - this.trkseg.forEach((segment, index) => { - if (segmentIndices === undefined || segmentIndices.includes(index)) { - segment.setHidden(hidden); - } else { - allHidden = allHidden && segment._data.hidden === true; - } - }); - this._data.hidden = allHidden; - } -} - -// A class that represents a TrackSegment in a GPX file -export class TrackSegment extends GPXTreeLeaf { - [immerable] = true; - - trkpt: TrackPoint[]; - - constructor(segment?: (TrackSegmentType & { _data?: any }) | TrackSegment) { - super(); - if (segment) { - this.trkpt = segment.trkpt.map((point, index) => new TrackPoint(point, index)); - if (segment.hasOwnProperty('_data')) { - this._data = segment._data; - } - } else { - this.trkpt = []; - } - } - - _computeStatistics(): GPXStatistics { - let statistics = new GPXStatistics(); - - statistics.global.length = this.trkpt.length; - statistics.local.points = this.trkpt.slice(0); - statistics.local.data = this.trkpt.map(() => new TrackPointLocalStatistics()); - - const points = this.trkpt; - for (let i = 0; i < points.length; i++) { - // distance - let dist = 0; - if (i > 0) { - dist = distance(points[i - 1].getCoordinates(), points[i].getCoordinates()) / 1000; - - statistics.global.distance.total += dist; - } - - statistics.local.data[i].distance.total = statistics.global.distance.total; - - // time - if (points[i].time === undefined) { - statistics.local.data[i].time.total = 0; - } else { - if (statistics.global.time.start === undefined) { - statistics.global.time.start = points[i].time; - } - statistics.global.time.end = points[i].time; - statistics.local.data[i].time.total = - (points[i].time.getTime() - statistics.global.time.start.getTime()) / 1000; - } - - // speed - let speed = 0; - if (i > 0 && points[i - 1].time !== undefined && points[i].time !== undefined) { - const time = (points[i].time.getTime() - points[i - 1].time.getTime()) / 1000; - speed = dist / (time / 3600); - - if (speed >= 0.5 && speed <= 1500) { - statistics.global.distance.moving += dist; - statistics.global.time.moving += time; - } - } - - statistics.local.data[i].distance.moving = statistics.global.distance.moving; - statistics.local.data[i].time.moving = statistics.global.time.moving; - - // bounds - statistics.global.bounds.southWest.lat = Math.min( - statistics.global.bounds.southWest.lat, - points[i].attributes.lat - ); - statistics.global.bounds.southWest.lon = Math.min( - statistics.global.bounds.southWest.lon, - points[i].attributes.lon - ); - statistics.global.bounds.northEast.lat = Math.max( - statistics.global.bounds.northEast.lat, - points[i].attributes.lat - ); - statistics.global.bounds.northEast.lon = Math.max( - statistics.global.bounds.northEast.lon, - points[i].attributes.lon - ); - - // extensions - if (points[i].extensions) { - if ( - points[i].extensions['gpxtpx:TrackPointExtension'] && - points[i].extensions['gpxtpx:TrackPointExtension']['gpxtpx:atemp'] - ) { - let atemp = points[i].extensions['gpxtpx:TrackPointExtension']['gpxtpx:atemp']; - statistics.global.atemp.avg = - (statistics.global.atemp.count * statistics.global.atemp.avg + atemp) / - (statistics.global.atemp.count + 1); - statistics.global.atemp.count++; - } - if ( - points[i].extensions['gpxtpx:TrackPointExtension'] && - points[i].extensions['gpxtpx:TrackPointExtension']['gpxtpx:hr'] - ) { - let hr = points[i].extensions['gpxtpx:TrackPointExtension']['gpxtpx:hr']; - statistics.global.hr.avg = - (statistics.global.hr.count * statistics.global.hr.avg + hr) / - (statistics.global.hr.count + 1); - statistics.global.hr.count++; - } - if ( - points[i].extensions['gpxtpx:TrackPointExtension'] && - points[i].extensions['gpxtpx:TrackPointExtension']['gpxtpx:cad'] - ) { - let cad = points[i].extensions['gpxtpx:TrackPointExtension']['gpxtpx:cad']; - statistics.global.cad.avg = - (statistics.global.cad.count * statistics.global.cad.avg + cad) / - (statistics.global.cad.count + 1); - statistics.global.cad.count++; - } - if ( - points[i].extensions['gpxpx:PowerExtension'] && - points[i].extensions['gpxpx:PowerExtension']['gpxpx:PowerInWatts'] - ) { - let power = points[i].extensions['gpxpx:PowerExtension']['gpxpx:PowerInWatts']; - statistics.global.power.avg = - (statistics.global.power.count * statistics.global.power.avg + power) / - (statistics.global.power.count + 1); - statistics.global.power.count++; - } - } - - if ( - i > 0 && - points[i - 1].extensions && - points[i - 1].extensions['gpxtpx:TrackPointExtension'] && - points[i - 1].extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions'] - ) { - Object.entries( - points[i - 1].extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions'] - ).forEach(([key, value]) => { - if (statistics.global.extensions[key] === undefined) { - statistics.global.extensions[key] = {}; - } - if (statistics.global.extensions[key][value] === undefined) { - statistics.global.extensions[key][value] = 0; - } - statistics.global.extensions[key][value] += dist; - }); - } - } - - this._elevationComputation(statistics); - - statistics.global.time.total = - statistics.global.time.start && statistics.global.time.end - ? (statistics.global.time.end.getTime() - statistics.global.time.start.getTime()) / - 1000 - : 0; - statistics.global.speed.total = - statistics.global.time.total > 0 - ? statistics.global.distance.total / (statistics.global.time.total / 3600) - : 0; - statistics.global.speed.moving = - statistics.global.time.moving > 0 - ? statistics.global.distance.moving / (statistics.global.time.moving / 3600) - : 0; - - timeWindowSmoothing( - points, - 10000, - (start, end) => - points[start].time && points[end].time - ? (3600 * - (statistics.local.data[end].distance.total - - statistics.local.data[start].distance.total)) / - Math.max( - (points[end].time.getTime() - points[start].time.getTime()) / 1000, - 1 - ) - : undefined, - (value, index) => { - statistics.local.data[index].speed = value; - } - ); - - return statistics; - } - - _elevationComputation(statistics: GPXStatistics) { - let simplified = ramerDouglasPeucker( - this.trkpt, - 20, - getElevationDistanceFunction(statistics) - ); - - for (let i = 0; i < simplified.length - 1; i++) { - let start = simplified[i].point._data.index; - let end = simplified[i + 1].point._data.index; - - let cumulEle = 0; - let currentStart = start; - let currentEnd = start; - let prevSmoothedEle = 0; - distanceWindowSmoothing( - start, - end + 1, - statistics, - 0.1, - (s, e) => { - for (let i = currentStart; i < s; i++) { - cumulEle -= this.trkpt[i].ele ?? 0; - } - for (let i = currentEnd; i <= e; i++) { - cumulEle += this.trkpt[i].ele ?? 0; - } - currentStart = s; - currentEnd = e + 1; - return cumulEle / (e - s + 1); - }, - (smoothedEle, j) => { - if (j === start) { - smoothedEle = this.trkpt[start].ele ?? 0; - prevSmoothedEle = smoothedEle; - } else if (j === end) { - smoothedEle = this.trkpt[end].ele ?? 0; - } - const ele = smoothedEle - prevSmoothedEle; - if (ele > 0) { - statistics.global.elevation.gain += ele; - } else if (ele < 0) { - statistics.global.elevation.loss -= ele; - } - prevSmoothedEle = smoothedEle; - if (j < end) { - statistics.local.data[j].elevation.gain = statistics.global.elevation.gain; - statistics.local.data[j].elevation.loss = statistics.global.elevation.loss; - } - } - ); - } - if (statistics.global.length > 0) { - statistics.local.data[statistics.global.length - 1].elevation.gain = - statistics.global.elevation.gain; - statistics.local.data[statistics.global.length - 1].elevation.loss = - statistics.global.elevation.loss; - } - - for (let i = 0; i < simplified.length - 1; i++) { - let start = simplified[i].point._data.index; - let end = simplified[i + 1].point._data.index; - let dist = - statistics.local.data[end].distance.total - - statistics.local.data[start].distance.total; - let ele = (simplified[i + 1].point.ele ?? 0) - (simplified[i].point.ele ?? 0); - for (let j = start; j < end + (i + 1 === simplified.length - 1 ? 1 : 0); j++) { - statistics.local.data[j].slope.segment = (0.1 * ele) / dist; - statistics.local.data[j].slope.length = dist; - } - } - - distanceWindowSmoothing( - 0, - this.trkpt.length, - statistics, - 0.05, - (start, end) => { - const ele = this.trkpt[end].ele - this.trkpt[start].ele || 0; - const dist = - statistics.local.data[end].distance.total - - statistics.local.data[start].distance.total; - return dist > 0 ? (0.1 * ele) / dist : 0; - }, - (value, index) => { - statistics.local.data[index].slope.at = value; - } - ); - } - - getNumberOfTrackPoints(): number { - return this.trkpt.length; - } - - getStartTimestamp(): Date | undefined { - if (this.trkpt.length === 0) { - return undefined; - } - return this.trkpt[0].time; - } - - getEndTimestamp(): Date | undefined { - if (this.trkpt.length === 0) { - return undefined; - } - return this.trkpt[this.trkpt.length - 1].time; - } - - getStatistics(): GPXStatistics { - return this._computeStatistics(); - } - - getSegments(): TrackSegment[] { - return [this]; - } - - getTrackPoints(): TrackPoint[] { - return this.trkpt; - } - - toGeoJSON(): GeoJSON.Feature { - return { - type: 'Feature', - geometry: { - type: 'LineString', - coordinates: this.trkpt.map((point) => [ - point.attributes.lon, - point.attributes.lat, - ]), - }, - properties: {}, - }; - } - - toTrackSegmentType(exclude: string[] = []): TrackSegmentType { - return { - trkpt: this.trkpt.map((point) => point.toTrackPointType(exclude)), - }; - } - - clone(): TrackSegment { - return new TrackSegment({ - trkpt: this.trkpt.map((point) => point.clone()), - _data: cloneJSON(this._data), - }); - } - - // Producers - replaceTrackPoints( - start: number, - end: number, - points: TrackPoint[], - speed?: number, - startTime?: Date, - removeGaps?: boolean - ) { - let og = getOriginal(this); // Read as much as possible from the original object because it is faster - let trkpt = og.trkpt.slice(); - - if (speed !== undefined || (trkpt.length > 0 && trkpt[0].time !== undefined)) { - // Must handle timestamps (either segment has timestamps or the new points will have timestamps) - if (start > 0 && trkpt[0].time === undefined) { - // Add timestamps to points before [start, end] because they are missing - trkpt.splice( - 0, - 0, - ...withTimestamps(trkpt.splice(0, start), speed, undefined, startTime) - ); - } - if (points.length > 0) { - // Adapt timestamps of the new points - let last = start > 0 ? trkpt[start - 1] : undefined; - if ( - points[0].time === undefined || - (points.length > 1 && points[1].time === undefined) - ) { - // Add timestamps to the new points because they are missing - points = withTimestamps(points, speed, last, startTime); - } else if (last !== undefined && points[0].time < last.time) { - // Adapt timestamps of the new points because they are too early - points = withShiftedAndCompressedTimestamps(points, speed, 1, last); - } else if (last !== undefined && removeGaps) { - // Remove gaps between the new points and the previous point - if ( - last.getLatitude() === points[0].getLatitude() && - last.getLongitude() === points[0].getLongitude() - ) { - // Same point, make the new points start at its timestamp and remove the first point - if (points[0].time > last.time) { - points = withShiftedAndCompressedTimestamps( - points, - speed, - 1, - last - ).slice(1); - } - } else { - // Different points, make the new points start one second after the previous point - if (points[0].time.getTime() - last.time.getTime() > 1000) { - let artificialLast = points[0].clone(); - artificialLast.time = new Date(last.time.getTime() + 1000); - points = withShiftedAndCompressedTimestamps( - points, - speed, - 1, - artificialLast - ); - } - } - } - } - if (end < trkpt.length - 1) { - // Adapt timestamps of points after [start, end] - let last = - points.length > 0 - ? points[points.length - 1] - : start > 0 - ? trkpt[start - 1] - : undefined; - if (trkpt[end + 1].time === undefined) { - // Add timestamps to points after [start, end] because they are missing - trkpt.splice( - end + 1, - 0, - ...withTimestamps(trkpt.splice(end + 1), speed, last, startTime) - ); - } else if (last !== undefined && trkpt[end + 1].time < last.time) { - // Adapt timestamps of points after [start, end] because they are too early - trkpt.splice( - end + 1, - 0, - ...withShiftedAndCompressedTimestamps(trkpt.splice(end + 1), speed, 1, last) - ); - } - } - } - - trkpt.splice(start, end - start + 1, ...points); - this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well - } - - _reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date) { - let og = getOriginal(this); // Read as much as possible from the original object because it is faster - let originalStartTimestamp = og.getStartTimestamp(); - let originalEndTimestamp = og.getEndTimestamp(); - if (!newPreviousTimestamp) { - newPreviousTimestamp = originalStartTimestamp; - } - if (newPreviousTimestamp && originalEndTimestamp && !originalNextTimestamp) { - originalNextTimestamp = new Date( - newPreviousTimestamp.getTime() + - originalEndTimestamp.getTime() - - originalStartTimestamp.getTime() - ); - } - if ( - originalNextTimestamp !== undefined && - newPreviousTimestamp !== undefined && - originalEndTimestamp !== undefined - ) { - let newStartTimestamp = new Date( - newPreviousTimestamp.getTime() + - originalNextTimestamp.getTime() - - originalEndTimestamp.getTime() - ); - - let trkpt = og.trkpt.map( - (point, i) => - new TrackPoint({ - attributes: cloneJSON(point.attributes), - ele: point.ele, - time: new Date( - newStartTimestamp.getTime() + - (originalEndTimestamp.getTime() - og.trkpt[i].time.getTime()) - ), - extensions: cloneJSON(point.extensions), - _data: cloneJSON(point._data), - }) - ); - - trkpt.reverse(); - - this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well - } else { - this.trkpt.reverse(); - } - } - - roundTrip() { - let og = getOriginal(this); // Read as much as possible from the original object because it is faster - let newSegment = og.clone(); - newSegment._reverse(newSegment.getEndTimestamp(), newSegment.getEndTimestamp()); - this.replaceTrackPoints(this.trkpt.length, this.trkpt.length, newSegment.trkpt); - } - - crop(start: number, end: number) { - this.trkpt = this.trkpt.slice(start, end + 1); - } - - clean(bounds: [Coordinates, Coordinates], inside: boolean) { - let og = getOriginal(this); // Read as much as possible from the original object because it is faster - let trkpt = og.trkpt.filter((point) => { - let inBounds = - point.attributes.lat >= bounds[0].lat && - point.attributes.lat <= bounds[1].lat && - point.attributes.lon >= bounds[0].lon && - point.attributes.lon <= bounds[1].lon; - return inBounds !== inside; - }); - this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well - } - - changeTimestamps(startTime: Date, speed: number, ratio: number, lastPoint?: TrackPoint) { - if (lastPoint === undefined && this.trkpt.length > 0) { - lastPoint = this.trkpt[0].clone(); - lastPoint.time = startTime; - } - - let og = getOriginal(this); // Read as much as possible from the original object because it is faster - if (og.trkpt.length > 0 && og.trkpt[0].time === undefined) { - let trkpt = withTimestamps(og.trkpt, speed, lastPoint, startTime); - this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well - } else { - let trkpt = withShiftedAndCompressedTimestamps(og.trkpt, speed, ratio, lastPoint); - this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well - } - } - - createArtificialTimestamps( - startTime: Date, - totalTime: number, - lastPoint: TrackPoint | undefined - ) { - let og = getOriginal(this); // Read as much as possible from the original object because it is faster - let statistics = og._computeStatistics(); - let trkpt = withArtificialTimestamps(og.trkpt, totalTime, lastPoint, startTime, statistics); - this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well - } - - setHidden(hidden: boolean) { - this._data.hidden = hidden; - } -} - -const emptyExtensions: Record = {}; -export class TrackPoint { - [immerable] = true; - - attributes: Coordinates; - ele?: number; - time?: Date; - extensions?: TrackPointExtensions; - - _data: { [key: string]: any } = {}; - - constructor(point: (TrackPointType & { _data?: any }) | TrackPoint, index?: number) { - this.attributes = point.attributes; - this.ele = point.ele; - this.time = point.time; - this.extensions = point.extensions; - if (point.hasOwnProperty('_data')) { - this._data = point._data; - } - if (index !== undefined) { - this._data.index = index; - } - } - - getCoordinates(): Coordinates { - return this.attributes; - } - - setCoordinates(coordinates: Coordinates): void { - this.attributes = coordinates; - } - - getLatitude(): number { - return this.attributes.lat; - } - - getLongitude(): number { - return this.attributes.lon; - } - - getTemperature(): number { - return this.extensions && - this.extensions['gpxtpx:TrackPointExtension'] && - this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:atemp'] - ? this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:atemp'] - : undefined; - } - - getHeartRate(): number { - return this.extensions && - this.extensions['gpxtpx:TrackPointExtension'] && - this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:hr'] - ? this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:hr'] - : undefined; - } - - getCadence(): number { - return this.extensions && - this.extensions['gpxtpx:TrackPointExtension'] && - this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:cad'] - ? this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:cad'] - : undefined; - } - - getPower(): number { - return this.extensions && - this.extensions['gpxpx:PowerExtension'] && - this.extensions['gpxpx:PowerExtension']['gpxpx:PowerInWatts'] - ? this.extensions['gpxpx:PowerExtension']['gpxpx:PowerInWatts'] - : undefined; - } - - setExtension(key: string, value: string) { - if (!this.extensions) { - this.extensions = {}; - } - if (!this.extensions['gpxtpx:TrackPointExtension']) { - this.extensions['gpxtpx:TrackPointExtension'] = {}; - } - if (!this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions']) { - this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions'] = {}; - } - this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions'][key] = value; - } - - setExtensions(extensions: Record) { - Object.entries(extensions).forEach(([key, value]) => { - this.setExtension(key, value); - }); - } - - getExtensions(): Record { - return this.extensions && - this.extensions['gpxtpx:TrackPointExtension'] && - this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions'] - ? this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions'] - : emptyExtensions; - } - - toTrackPointType(exclude: string[] = []): TrackPointType { - let trkpt: TrackPointType = { - attributes: this.attributes, - ele: this.ele, - }; - if (!exclude.includes('time')) { - trkpt = { ...trkpt, time: this.time }; - } - if (this.extensions) { - trkpt = { - ...trkpt, - extensions: { - 'gpxtpx:TrackPointExtension': {}, - 'gpxpx:PowerExtension': {}, - }, - }; - if ( - this.extensions['gpxtpx:TrackPointExtension'] && - this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:atemp'] && - !exclude.includes('atemp') - ) { - trkpt.extensions['gpxtpx:TrackPointExtension']['gpxtpx:atemp'] = - this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:atemp']; - } - if ( - this.extensions['gpxtpx:TrackPointExtension'] && - this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:hr'] && - !exclude.includes('hr') - ) { - trkpt.extensions['gpxtpx:TrackPointExtension']['gpxtpx:hr'] = - this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:hr']; - } - if ( - this.extensions['gpxtpx:TrackPointExtension'] && - this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:cad'] && - !exclude.includes('cad') - ) { - trkpt.extensions['gpxtpx:TrackPointExtension']['gpxtpx:cad'] = - this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:cad']; - } - if ( - this.extensions['gpxpx:PowerExtension'] && - this.extensions['gpxpx:PowerExtension']['gpxpx:PowerInWatts'] && - !exclude.includes('power') - ) { - trkpt.extensions['gpxpx:PowerExtension']['gpxpx:PowerInWatts'] = - this.extensions['gpxpx:PowerExtension']['gpxpx:PowerInWatts']; - } - if ( - this.extensions['gpxtpx:TrackPointExtension'] && - this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions'] && - !exclude.includes('extensions') - ) { - trkpt.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions'] = {}; - Object.entries( - this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions'] - ).forEach(([key, value]) => { - trkpt.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions'][key] = - value; - }); - } - } - return trkpt; - } - - clone(): TrackPoint { - return new TrackPoint({ - attributes: { - lat: this.attributes.lat, - lon: this.attributes.lon, - }, - ele: this.ele, - time: this.time ? new Date(this.time.getTime()) : undefined, - extensions: this.extensions ? cloneJSON(this.extensions) : undefined, - _data: { - index: this._data?.index, - anchor: this._data?.anchor, - zoom: this._data?.zoom, - }, - }); - } -} - -export class Waypoint { - [immerable] = true; - - attributes: Coordinates; - ele?: number; - time?: Date; - name?: string; - cmt?: string; - desc?: string; - link?: Link; - sym?: string; - type?: string; - _data: { [key: string]: any } = {}; - - constructor(waypoint: (WaypointType & { _data?: any }) | Waypoint, index?: number) { - this.attributes = waypoint.attributes; - this.ele = waypoint.ele; - this.time = waypoint.time; - this.name = waypoint.name === '' ? undefined : waypoint.name; - this.cmt = waypoint.cmt === '' ? undefined : waypoint.cmt; - this.desc = waypoint.desc === '' ? undefined : waypoint.desc; - this.link = - !waypoint.link || - !waypoint.link.attributes || - !waypoint.link.attributes.href || - waypoint.link.attributes.href === '' - ? undefined - : waypoint.link; - this.sym = waypoint.sym === '' ? undefined : waypoint.sym; - this.type = waypoint.type === '' ? undefined : waypoint.type; - if (waypoint.hasOwnProperty('_data')) { - this._data = waypoint._data; - } - if (index !== undefined) { - this._data.index = index; - } - } - - getCoordinates(): Coordinates { - return this.attributes; - } - - setCoordinates(coordinates: Coordinates): void { - this.attributes = coordinates; - } - - getLatitude(): number { - return this.attributes.lat; - } - - getLongitude(): number { - return this.attributes.lon; - } - - toWaypointType(exclude: string[] = []): WaypointType { - if (!exclude.includes('time')) { - return { - attributes: this.attributes, - ele: this.ele, - time: this.time, - name: this.name, - cmt: this.cmt, - desc: this.desc, - link: this.link, - sym: this.sym, - type: this.type, - }; - } else { - return { - attributes: this.attributes, - ele: this.ele, - name: this.name, - cmt: this.cmt, - desc: this.desc, - link: this.link, - sym: this.sym, - type: this.type, - }; - } - } - - clone(): Waypoint { - return new Waypoint({ - attributes: { - lat: this.attributes.lat, - lon: this.attributes.lon, - }, - ele: this.ele, - time: this.time ? new Date(this.time.getTime()) : undefined, - name: this.name, - cmt: this.cmt, - desc: this.desc, - link: cloneJSON(this.link), - sym: this.sym, - type: this.type, - }); - } - - equals(other: Waypoint): boolean { - if ( - this.attributes.lat !== other.attributes.lat || - this.attributes.lon !== other.attributes.lon || - this.ele !== other.ele || - this.name !== other.name || - this.cmt !== other.cmt || - this.desc !== other.desc || - this.sym !== other.sym || - this.type !== other.type - ) { - return false; - } - - if ( - (this.time === undefined && other.time !== undefined) || - (this.time !== undefined && other.time === undefined) || - (this.time !== undefined && - other.time !== undefined && - this.time.getTime() !== other.time.getTime()) - ) { - return false; - } - - if (JSON.stringify(this.link) !== JSON.stringify(other.link)) { - return false; - } - - return true; - } - - // Producers - setHidden(hidden: boolean) { - this._data.hidden = hidden; - } -} - -const earthRadius = 6371008.8; -export function distance( - coord1: TrackPoint | Coordinates, - coord2: TrackPoint | Coordinates -): number { - if (coord1 instanceof TrackPoint) { - coord1 = coord1.getCoordinates(); - } - if (coord2 instanceof TrackPoint) { - coord2 = coord2.getCoordinates(); - } - const rad = Math.PI / 180; - const lat1 = coord1.lat * rad; - const lat2 = coord2.lat * rad; - const dLat = lat2 - lat1; - const dLon = (coord2.lon - coord1.lon) * rad; - - // Haversine formula - better numerical stability for small distances - const a = - Math.sin(dLat / 2) * Math.sin(dLat / 2) + - Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon / 2) * Math.sin(dLon / 2); - const c = 2 * Math.asin(Math.sqrt(Math.min(a, 1))); - return earthRadius * c; -} - -export function getElevationDistanceFunction(statistics: GPXStatistics) { - // x-coordinates are given by: statistics.local.distance.total[point._data.index] * 1000 - // y-coordinates are given by: point.ele - // Compute the distance between point3 and the line defined by point1 and point2 - return (point1: TrackPoint, point2: TrackPoint, point3: TrackPoint) => { - if (point1.ele === undefined || point2.ele === undefined || point3.ele === undefined) { - return 0; - } - let x1 = statistics.local.data[point1._data.index].distance.total * 1000; - let x2 = statistics.local.data[point2._data.index].distance.total * 1000; - let x3 = statistics.local.data[point3._data.index].distance.total * 1000; - let y1 = point1.ele; - let y2 = point2.ele; - let y3 = point3.ele; - - let dist = Math.sqrt(Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2)); - if (dist === 0) { - return Math.sqrt(Math.pow(x3 - x1, 2) + Math.pow(y3 - y1, 2)); - } - - return Math.abs((y2 - y1) * x3 - (x2 - x1) * y3 + x2 * y1 - y2 * x1) / dist; - }; -} - -function windowSmoothing( - left: number, - right: number, - distance: (index1: number, index2: number) => number, - window: number, - compute: (start: number, end: number) => number, - callback: (value: number, index: number) => void -): void { - let start = left; - for (var i = left; i < right; i++) { - while (start + 1 < i && distance(start, i) > window) { - start++; - } - let end = Math.min(i + 2, right); - while (end < right && distance(i, end) <= window) { - end++; - } - callback(compute(start, end - 1), i); - } -} - -function distanceWindowSmoothing( - left: number, - right: number, - statistics: GPXStatistics, - window: number, - compute: (start: number, end: number) => number, - callback: (value: number, index: number) => void -): void { - windowSmoothing( - left, - right, - (index1, index2) => - statistics.local.data[index2].distance.total - - statistics.local.data[index1].distance.total, - window, - compute, - callback - ); -} - -function timeWindowSmoothing( - points: TrackPoint[], - window: number, - compute: (start: number, end: number) => number, - callback: (value: number, index: number) => void -): void { - windowSmoothing( - 0, - points.length, - (index1, index2) => - points[index2].time?.getTime() - points[index1].time?.getTime() || 2 * window, - window, - compute, - callback - ); -} - -function withTimestamps( - points: TrackPoint[], - speed: number, - lastPoint: TrackPoint | undefined, - startTime?: Date -): TrackPoint[] { - let last = lastPoint; - if (last === undefined) { - last = points[0].clone(); - last.time = startTime; - } else if (last.time === undefined) { - last.time = startTime; - } - return points.map((point) => { - let time = getTimestamp(last, point, speed); - last = point.clone(); - last.time = time; - return last; - }); -} - -function withShiftedAndCompressedTimestamps( - points: TrackPoint[], - speed: number, - ratio: number, - lastPoint: TrackPoint -): TrackPoint[] { - let start = getTimestamp(lastPoint, points[0], speed); - let last = points[0]; - return points.map((point) => { - let pt = point.clone(); - if (point.time === undefined) { - pt.time = getTimestamp(last, point, speed); - } else { - pt.time = new Date( - start.getTime() + ratio * (point.time.getTime() - points[0].time.getTime()) - ); - } - last = pt; - return pt; - }); -} - -function withArtificialTimestamps( - points: TrackPoint[], - totalTime: number, - lastPoint: TrackPoint | undefined, - startTime: Date, - statistics: GPXStatistics -): TrackPoint[] { - let weight = []; - let totalWeight = 0; - - for (let i = 0; i < points.length - 1; i++) { - let dist = distance(points[i].getCoordinates(), points[i + 1].getCoordinates()); - let w = dist * (0.5 + 1 / (1 + Math.exp(-0.2 * statistics.local.data[i].slope.at))); - weight.push(w); - totalWeight += w; - } - - let last = lastPoint; - return points.map((point, i) => { - let pt = point.clone(); - if (i === 0) { - pt.time = lastPoint?.time ?? startTime; - } else { - pt.time = new Date( - last.time.getTime() + (totalTime * 1000 * weight[i - 1]) / totalWeight - ); - } - last = pt; - return pt; - }); -} - -function getTimestamp(a: TrackPoint, b: TrackPoint, speed: number): Date { - if (a.time === undefined) { - return undefined; - } else if (speed === undefined) { - return new Date(a.time.getTime() + 1000); - } - let dist = distance(a.getCoordinates(), b.getCoordinates()) / 1000; - return new Date(a.time.getTime() + (1000 * 3600 * dist) / speed); -} - -function getOriginal(obj: any): any { - while (isDraft(obj)) { - obj = original(obj); - } - return obj; -} - -export type MergedLineStyles = { - color: string[]; - opacity: number[]; - width: number[]; -}; - -function convertRouteToTrack(route: RouteType): Track { - const track = new Track({ - name: route.name, - cmt: route.cmt, - desc: route.desc, - src: route.src, - link: route.link, - type: route.type, - extensions: route.extensions, - trkseg: [], - }); - - if (route.rtept) { - const segment = new TrackSegment(); - - route.rtept.forEach((rpt) => { - if ( - rpt.extensions && - rpt.extensions['gpxx:RoutePointExtension'] && - rpt.extensions['gpxx:RoutePointExtension']['gpxx:rpt'] - ) { - rpt.extensions['gpxx:RoutePointExtension']['gpxx:rpt'].forEach((rptExtension) => { - segment.trkpt.push( - new TrackPoint({ - attributes: rptExtension.attributes, - }) - ); - }); - } else { - segment.trkpt.push( - new TrackPoint({ - attributes: rpt.attributes, - ele: rpt.ele, - time: rpt.time, - }) - ); - } - }); - - track.trkseg.push(segment); - } - - return track; -} diff --git a/gpx/src/index.ts b/gpx/src/index.ts deleted file mode 100644 index 984e6893b..000000000 --- a/gpx/src/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './gpx'; -export * from './statistics'; -export { Coordinates, LineStyleExtension, WaypointType } from './types'; -export { parseGPX, buildGPX } from './io'; -export * from './simplify'; diff --git a/gpx/src/io.ts b/gpx/src/io.ts deleted file mode 100644 index 0f7e9f452..000000000 --- a/gpx/src/io.ts +++ /dev/null @@ -1,180 +0,0 @@ -import { XMLParser, XMLBuilder } from 'fast-xml-parser'; -import { GPXFileType } from './types'; -import { GPXFile } from './gpx'; - -const attributesWithNamespace = { - RoutePointExtension: 'gpxx:RoutePointExtension', - rpt: 'gpxx:rpt', - TrackPointExtension: 'gpxtpx:TrackPointExtension', - PowerExtension: 'gpxpx:PowerExtension', - atemp: 'gpxtpx:atemp', - hr: 'gpxtpx:hr', - cad: 'gpxtpx:cad', - Extensions: 'gpxtpx:Extensions', - PowerInWatts: 'gpxpx:PowerInWatts', - power: 'gpxpx:PowerExtension', - line: 'gpx_style:line', - color: 'gpx_style:color', - opacity: 'gpx_style:opacity', - width: 'gpx_style:width', -}; - -const floatPatterns = [ - /[-+]?\d*\.\d+$/, // decimal - /[-+]?\d+$/, // integer -]; -function safeParseFloat(value: string): number { - const parsed = parseFloat(value); - if (!isNaN(parsed)) { - return parsed; - } - for (const pattern of floatPatterns) { - const match = value.match(pattern); - if (match) { - return parseFloat(match[0]); - } - } - return 0.0; -} - -export function parseGPX(gpxData: string): GPXFile { - const parser = new XMLParser({ - ignoreAttributes: false, - attributeNamePrefix: '', - attributesGroupName: 'attributes', - removeNSPrefix: true, - isArray(name: string) { - return ( - name === 'trk' || - name === 'trkseg' || - name === 'trkpt' || - name === 'wpt' || - name === 'rte' || - name === 'rtept' || - name === 'gpxx:rpt' - ); - }, - attributeValueProcessor(attrName, attrValue, jPath) { - if (attrName === 'lat' || attrName === 'lon') { - return safeParseFloat(attrValue); - } - return attrValue; - }, - transformTagName(tagName: string) { - if (attributesWithNamespace[tagName]) { - return attributesWithNamespace[tagName]; - } - return tagName; - }, - parseTagValue: false, - tagValueProcessor(tagName, tagValue, jPath, hasAttributes, isLeafNode) { - if (isLeafNode) { - if (tagName === 'ele') { - return safeParseFloat(tagValue); - } - - if (tagName === 'time') { - return new Date(tagValue); - } - - if ( - tagName === 'gpxtpx:atemp' || - tagName === 'gpxtpx:hr' || - tagName === 'gpxtpx:cad' || - tagName === 'gpxpx:PowerInWatts' || - tagName === 'gpx_style:opacity' || - tagName === 'gpx_style:width' - ) { - return safeParseFloat(tagValue); - } - - if (tagName === 'gpxpx:PowerExtension') { - // Finish the transformation of the simple tag to the more complex tag - // Note that this only targets the transformed tag, since it must be a leaf node - return { - 'gpxpx:PowerInWatts': safeParseFloat(tagValue), - }; - } - } - - return tagValue; - }, - }); - - const parsed: GPXFileType = parser.parse(gpxData).gpx; - - // @ts-ignore - if (parsed.metadata === '') { - parsed.metadata = {}; - } - - return new GPXFile(parsed); -} - -export function buildGPX(file: GPXFile, exclude: string[]): string { - const gpx = file.toGPXFileType(exclude); - - let lastDate = undefined; - const builder = new XMLBuilder({ - format: true, - ignoreAttributes: false, - attributeNamePrefix: '', - attributesGroupName: 'attributes', - suppressEmptyNode: true, - tagValueProcessor: (tagName: string, tagValue: unknown): string | undefined => { - if (tagValue instanceof Date) { - if (isNaN(tagValue.getTime())) { - return lastDate?.toISOString(); - } - lastDate = tagValue; - return tagValue.toISOString(); - } - return tagValue.toString(); - }, - }); - - if (!gpx.attributes) gpx.attributes = {}; - gpx.attributes['creator'] = gpx.attributes['creator'] ?? 'https://gpx.studio'; - gpx.attributes['version'] = '1.1'; - gpx.attributes['xmlns'] = 'http://www.topografix.com/GPX/1/1'; - gpx.attributes['xmlns:xsi'] = 'http://www.w3.org/2001/XMLSchema-instance'; - gpx.attributes['xsi:schemaLocation'] = - 'http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd http://www.garmin.com/xmlschemas/PowerExtension/v1 http://www.garmin.com/xmlschemas/PowerExtensionv1.xsd http://www.topografix.com/GPX/gpx_style/0/2 http://www.topografix.com/GPX/gpx_style/0/2/gpx_style.xsd'; - gpx.attributes['xmlns:gpxtpx'] = 'http://www.garmin.com/xmlschemas/TrackPointExtension/v1'; - gpx.attributes['xmlns:gpxx'] = 'http://www.garmin.com/xmlschemas/GpxExtensions/v3'; - gpx.attributes['xmlns:gpxpx'] = 'http://www.garmin.com/xmlschemas/PowerExtension/v1'; - gpx.attributes['xmlns:gpx_style'] = 'http://www.topografix.com/GPX/gpx_style/0/2'; - - if (gpx.trk.length === 1 && (gpx.trk[0].name === undefined || gpx.trk[0].name === '')) { - gpx.trk[0].name = gpx.metadata.name; - } - - return builder.build({ - '?xml': { - attributes: { - version: '1.0', - encoding: 'UTF-8', - }, - }, - gpx: removeEmptyElements(gpx), - }); -} - -function removeEmptyElements(obj: GPXFileType): GPXFileType { - for (const key in obj) { - if ( - obj[key] === null || - obj[key] === undefined || - obj[key] === '' || - (Array.isArray(obj[key]) && obj[key].length === 0) - ) { - delete obj[key]; - } else if (typeof obj[key] === 'object' && !(obj[key] instanceof Date)) { - removeEmptyElements(obj[key]); - if (Object.keys(obj[key]).length === 0) { - delete obj[key]; - } - } - } - return obj; -} diff --git a/gpx/src/simplify.ts b/gpx/src/simplify.ts deleted file mode 100644 index 7669b3cd3..000000000 --- a/gpx/src/simplify.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { TrackPoint } from './gpx'; -import { Coordinates } from './types'; - -export type SimplifiedTrackPoint = { point: TrackPoint; distance?: number }; - -export function ramerDouglasPeucker( - points: TrackPoint[], - epsilon: number = 50, - measure: (a: TrackPoint, b: TrackPoint, c: TrackPoint) => number = crossarcDistance -): SimplifiedTrackPoint[] { - if (points.length == 0) { - return []; - } else if (points.length == 1) { - return [ - { - point: points[0], - }, - ]; - } - - let simplified = [ - { - point: points[0], - }, - ]; - ramerDouglasPeuckerRecursive(points, epsilon, measure, 0, points.length - 1, simplified); - simplified.push({ - point: points[points.length - 1], - }); - return simplified; -} - -function ramerDouglasPeuckerRecursive( - points: TrackPoint[], - epsilon: number, - measure: (a: TrackPoint, b: TrackPoint, c: TrackPoint) => number, - start: number, - end: number, - simplified: SimplifiedTrackPoint[] -) { - let largest = { - index: 0, - distance: 0, - }; - - for (let i = start + 1; i < end; i++) { - let distance = measure(points[start], points[end], points[i]); - if (distance > largest.distance) { - largest.index = i; - largest.distance = distance; - } - } - - if (largest.distance > epsilon && largest.index != 0) { - ramerDouglasPeuckerRecursive(points, epsilon, measure, start, largest.index, simplified); - simplified.push({ point: points[largest.index], distance: largest.distance }); - ramerDouglasPeuckerRecursive(points, epsilon, measure, largest.index, end, simplified); - } -} - -export function crossarcDistance( - point1: TrackPoint | Coordinates, - point2: TrackPoint | Coordinates, - point3: TrackPoint | Coordinates -): number { - return crossarc( - point1 instanceof TrackPoint ? point1.getCoordinates() : point1, - point2 instanceof TrackPoint ? point2.getCoordinates() : point2, - point3 instanceof TrackPoint ? point3.getCoordinates() : point3 - ); -} - -const metersPerLatitudeDegree = 111320; - -function getMetersPerLongitudeDegree(latitude: number): number { - return Math.cos((latitude * Math.PI) / 180) * metersPerLatitudeDegree; -} - -function crossarc(coord1: Coordinates, coord2: Coordinates, coord3: Coordinates): number { - // Calculates the perpendicular distance in meters - // between a line segment (defined by p1 and p2) and a third point, p3. - // Uses simple planar geometry (ignores earth curvature). - - // Convert to meters using approximate scaling - const metersPerLongitudeDegree = getMetersPerLongitudeDegree(coord1.lat); - - const x1 = coord1.lon * metersPerLongitudeDegree; - const y1 = coord1.lat * metersPerLatitudeDegree; - const x2 = coord2.lon * metersPerLongitudeDegree; - const y2 = coord2.lat * metersPerLatitudeDegree; - const x3 = coord3.lon * metersPerLongitudeDegree; - const y3 = coord3.lat * metersPerLatitudeDegree; - - const dx = x2 - x1; - const dy = y2 - y1; - const segmentLengthSquared = dx * dx + dy * dy; - - if (segmentLengthSquared === 0) { - // p1 and p2 are the same point - return Math.sqrt((x3 - x1) * (x3 - x1) + (y3 - y1) * (y3 - y1)); - } - - // Project p3 onto the line defined by p1-p2 - const t = Math.max(0, Math.min(1, ((x3 - x1) * dx + (y3 - y1) * dy) / segmentLengthSquared)); - - // Find the closest point on the segment - const projX = x1 + t * dx; - const projY = y1 + t * dy; - - // Return distance from p3 to the projected point - return Math.sqrt((x3 - projX) * (x3 - projX) + (y3 - projY) * (y3 - projY)); -} - -export function projectedPoint( - point1: TrackPoint, - point2: TrackPoint, - point3: TrackPoint | Coordinates -): Coordinates { - return projected( - point1.getCoordinates(), - point2.getCoordinates(), - point3 instanceof TrackPoint ? point3.getCoordinates() : point3 - ); -} - -function projected(coord1: Coordinates, coord2: Coordinates, coord3: Coordinates): Coordinates { - // Calculates the point on the line segment defined by p1 and p2 - // that is closest to the third point, p3. - // Uses simple planar geometry (ignores earth curvature). - - // Convert to meters using approximate scaling - const metersPerLongitudeDegree = getMetersPerLongitudeDegree(coord1.lat); - - const x1 = coord1.lon * metersPerLongitudeDegree; - const y1 = coord1.lat * metersPerLatitudeDegree; - const x2 = coord2.lon * metersPerLongitudeDegree; - const y2 = coord2.lat * metersPerLatitudeDegree; - const x3 = coord3.lon * metersPerLongitudeDegree; - const y3 = coord3.lat * metersPerLatitudeDegree; - - const dx = x2 - x1; - const dy = y2 - y1; - const segmentLengthSquared = dx * dx + dy * dy; - - if (segmentLengthSquared === 0) { - // p1 and p2 are the same point - return coord1; - } - - // Project p3 onto the line defined by p1-p2 - const t = Math.max(0, Math.min(1, ((x3 - x1) * dx + (y3 - y1) * dy) / segmentLengthSquared)); - - // Find the closest point on the segment - const projX = x1 + t * dx; - const projY = y1 + t * dy; - - // Convert back to degrees - return { - lat: projY / metersPerLatitudeDegree, - lon: projX / metersPerLongitudeDegree, - }; -} diff --git a/gpx/src/statistics.ts b/gpx/src/statistics.ts deleted file mode 100644 index f93d99188..000000000 --- a/gpx/src/statistics.ts +++ /dev/null @@ -1,391 +0,0 @@ -import { TrackPoint } from './gpx'; -import { Coordinates } from './types'; - -export class GPXGlobalStatistics { - length: number; - distance: { - moving: number; - total: number; - }; - time: { - start: Date | undefined; - end: Date | undefined; - moving: number; - total: number; - }; - speed: { - moving: number; - total: number; - }; - elevation: { - gain: number; - loss: number; - }; - bounds: { - southWest: Coordinates; - northEast: Coordinates; - }; - atemp: { - avg: number; - count: number; - }; - hr: { - avg: number; - count: number; - }; - cad: { - avg: number; - count: number; - }; - power: { - avg: number; - count: number; - }; - extensions: Record>; - - constructor() { - this.length = 0; - this.distance = { - moving: 0, - total: 0, - }; - this.time = { - start: undefined, - end: undefined, - moving: 0, - total: 0, - }; - this.speed = { - moving: 0, - total: 0, - }; - this.elevation = { - gain: 0, - loss: 0, - }; - this.bounds = { - southWest: { - lat: 90, - lon: 180, - }, - northEast: { - lat: -90, - lon: -180, - }, - }; - this.atemp = { - avg: 0, - count: 0, - }; - this.hr = { - avg: 0, - count: 0, - }; - this.cad = { - avg: 0, - count: 0, - }; - this.power = { - avg: 0, - count: 0, - }; - this.extensions = {}; - } - - mergeWith(other: GPXGlobalStatistics): void { - this.length += other.length; - - this.distance.total += other.distance.total; - this.distance.moving += other.distance.moving; - - this.time.start = - this.time.start !== undefined && other.time.start !== undefined - ? new Date(Math.min(this.time.start.getTime(), other.time.start.getTime())) - : (this.time.start ?? other.time.start); - this.time.end = - this.time.end !== undefined && other.time.end !== undefined - ? new Date(Math.max(this.time.end.getTime(), other.time.end.getTime())) - : (this.time.end ?? other.time.end); - - this.time.total += other.time.total; - this.time.moving += other.time.moving; - - this.speed.moving = - this.time.moving > 0 ? this.distance.moving / (this.time.moving / 3600) : 0; - this.speed.total = this.time.total > 0 ? this.distance.total / (this.time.total / 3600) : 0; - - this.elevation.gain += other.elevation.gain; - this.elevation.loss += other.elevation.loss; - - this.bounds.southWest.lat = Math.min(this.bounds.southWest.lat, other.bounds.southWest.lat); - this.bounds.southWest.lon = Math.min(this.bounds.southWest.lon, other.bounds.southWest.lon); - this.bounds.northEast.lat = Math.max(this.bounds.northEast.lat, other.bounds.northEast.lat); - this.bounds.northEast.lon = Math.max(this.bounds.northEast.lon, other.bounds.northEast.lon); - - this.atemp.avg = - (this.atemp.count * this.atemp.avg + other.atemp.count * other.atemp.avg) / - Math.max(1, this.atemp.count + other.atemp.count); - this.atemp.count += other.atemp.count; - this.hr.avg = - (this.hr.count * this.hr.avg + other.hr.count * other.hr.avg) / - Math.max(1, this.hr.count + other.hr.count); - this.hr.count += other.hr.count; - this.cad.avg = - (this.cad.count * this.cad.avg + other.cad.count * other.cad.avg) / - Math.max(1, this.cad.count + other.cad.count); - this.cad.count += other.cad.count; - this.power.avg = - (this.power.count * this.power.avg + other.power.count * other.power.avg) / - Math.max(1, this.power.count + other.power.count); - this.power.count += other.power.count; - - Object.keys(other.extensions).forEach((extension) => { - if (this.extensions[extension] === undefined) { - this.extensions[extension] = {}; - } - Object.keys(other.extensions[extension]).forEach((value) => { - if (this.extensions[extension][value] === undefined) { - this.extensions[extension][value] = 0; - } - this.extensions[extension][value] += other.extensions[extension][value]; - }); - }); - } -} - -export class TrackPointLocalStatistics { - distance: { - moving: number; - total: number; - }; - time: { - moving: number; - total: number; - }; - speed: number; - elevation: { - gain: number; - loss: number; - }; - slope: { - at: number; - segment: number; - length: number; - }; - - constructor() { - this.distance = { - moving: 0, - total: 0, - }; - this.time = { - moving: 0, - total: 0, - }; - this.speed = 0; - this.elevation = { - gain: 0, - loss: 0, - }; - this.slope = { - at: 0, - segment: 0, - length: 0, - }; - } -} - -export class GPXLocalStatistics { - points: TrackPoint[]; - data: TrackPointLocalStatistics[]; - - constructor() { - this.points = []; - this.data = []; - } -} - -export type TrackPointWithLocalStatistics = { - trkpt: TrackPoint; -} & TrackPointLocalStatistics; - -export class GPXStatistics { - global: GPXGlobalStatistics; - local: GPXLocalStatistics; - - constructor() { - this.global = new GPXGlobalStatistics(); - this.local = new GPXLocalStatistics(); - } - - sliced(start: number, end: number): GPXGlobalStatistics { - if (start < 0) { - start = 0; - } else if (start >= this.global.length) { - return new GPXGlobalStatistics(); - } - - if (end < start) { - return new GPXGlobalStatistics(); - } else if (end >= this.global.length) { - end = this.global.length - 1; - } - - if (start === 0 && end === this.global.length - 1) { - return this.global; - } - - let statistics = new GPXGlobalStatistics(); - - statistics.length = end - start + 1; - - statistics.distance.total = - this.local.data[end].distance.total - this.local.data[start].distance.total; - statistics.distance.moving = - this.local.data[end].distance.moving - this.local.data[start].distance.moving; - - statistics.time.start = this.local.points[start].time; - statistics.time.end = this.local.points[end].time; - - statistics.time.total = this.local.data[end].time.total - this.local.data[start].time.total; - statistics.time.moving = - this.local.data[end].time.moving - this.local.data[start].time.moving; - - statistics.speed.moving = - statistics.time.moving > 0 - ? statistics.distance.moving / (statistics.time.moving / 3600) - : 0; - statistics.speed.total = - statistics.time.total > 0 - ? statistics.distance.total / (statistics.time.total / 3600) - : 0; - - statistics.elevation.gain = - this.local.data[end].elevation.gain - this.local.data[start].elevation.gain; - statistics.elevation.loss = - this.local.data[end].elevation.loss - this.local.data[start].elevation.loss; - - statistics.bounds.southWest.lat = this.global.bounds.southWest.lat; - statistics.bounds.southWest.lon = this.global.bounds.southWest.lon; - statistics.bounds.northEast.lat = this.global.bounds.northEast.lat; - statistics.bounds.northEast.lon = this.global.bounds.northEast.lon; - - statistics.atemp = this.global.atemp; - statistics.hr = this.global.hr; - statistics.cad = this.global.cad; - statistics.power = this.global.power; - - return statistics; - } -} - -export class GPXStatisticsGroup { - private _statistics: GPXStatistics[]; - private _cumulative: GPXGlobalStatistics[]; - private _slice: [number, number] | null = null; - global: GPXGlobalStatistics; - - constructor() { - this._statistics = []; - this._cumulative = [new GPXGlobalStatistics()]; - this.global = new GPXGlobalStatistics(); - } - - add(statistics: GPXStatistics | GPXStatisticsGroup): void { - if (statistics instanceof GPXStatisticsGroup) { - statistics._statistics.forEach((stats) => this._add(stats)); - } else { - this._add(statistics); - } - } - - _add(statistics: GPXStatistics): void { - this._statistics.push(statistics); - const cumulative = new GPXGlobalStatistics(); - cumulative.mergeWith(this._cumulative[this._cumulative.length - 1]); - cumulative.mergeWith(statistics.global); - this._cumulative.push(cumulative); - this.global.mergeWith(statistics.global); - } - - sliced(start: number, end: number): GPXGlobalStatistics { - let sliced = new GPXGlobalStatistics(); - for (let i = 0; i < this._statistics.length; i++) { - const statistics = this._statistics[i]; - const cumulative = this._cumulative[i]; - if (start < cumulative.length + statistics.global.length && end >= cumulative.length) { - const localStart = Math.max(0, start - cumulative.length); - const localEnd = Math.min(statistics.global.length - 1, end - cumulative.length); - sliced.mergeWith(statistics.sliced(localStart, localEnd)); - } - } - return sliced; - } - - getTrackPoint(index: number): TrackPointWithLocalStatistics | undefined { - if (this._slice !== null) { - index += this._slice[0]; - } - for (let i = 0; i < this._statistics.length; i++) { - const statistics = this._statistics[i]; - const cumulative = this._cumulative[i]; - if (index < cumulative.length + statistics.global.length) { - return this._getTrackPoint(cumulative, statistics, index - cumulative.length); - } - } - return undefined; - } - - _getTrackPoint( - cumulative: GPXGlobalStatistics, - statistics: GPXStatistics, - index: number - ): TrackPointWithLocalStatistics { - const point = statistics.local.points[index]; - return { - trkpt: point, - distance: { - moving: statistics.local.data[index].distance.moving + cumulative.distance.moving, - total: statistics.local.data[index].distance.total + cumulative.distance.total, - }, - time: { - moving: statistics.local.data[index].time.moving + cumulative.time.moving, - total: statistics.local.data[index].time.total + cumulative.time.total, - }, - speed: statistics.local.data[index].speed, - elevation: { - gain: statistics.local.data[index].elevation.gain + cumulative.elevation.gain, - loss: statistics.local.data[index].elevation.loss + cumulative.elevation.loss, - }, - slope: { - at: statistics.local.data[index].slope.at, - segment: statistics.local.data[index].slope.segment, - length: statistics.local.data[index].slope.length, - }, - }; - } - - forEachTrackPoint( - callback: ( - point: TrackPoint, - distance: number, - speed: number, - slope: { at: number; segment: number; length: number }, - index: number - ) => void - ): void { - for (let i = 0; i < this._statistics.length; i++) { - const statistics = this._statistics[i]; - const cumulative = this._cumulative[i]; - statistics.local.points.forEach((point, index) => - callback( - point, - cumulative.distance.total + statistics.local.data[index].distance.total, - statistics.local.data[index].speed, - statistics.local.data[index].slope, - cumulative.length + index - ) - ); - } - } -} diff --git a/gpx/src/types.ts b/gpx/src/types.ts deleted file mode 100644 index 67273c447..000000000 --- a/gpx/src/types.ts +++ /dev/null @@ -1,125 +0,0 @@ -export type GPXFileType = { - attributes: GPXFileAttributes; - metadata: Metadata; - wpt: WaypointType[]; - trk: TrackType[]; - rte: RouteType[]; -}; - -export type GPXFileAttributes = { - creator?: string; - [key: string]: string; -}; - -export type Metadata = { - name?: string; - desc?: string; - author?: Author; - link?: Link; - time?: Date; -}; - -export type Link = { - attributes: LinkAttributes; - text?: string; - type?: string; -}; - -export type LinkAttributes = { - href: string; -}; - -export type WaypointType = { - attributes: Coordinates; - ele?: number; - time?: Date; - name?: string; - cmt?: string; - desc?: string; - link?: Link; - sym?: string; - type?: string; - extensions?: WaypointExtensions; -}; - -export type WaypointExtensions = { - 'gpxx:RoutePointExtension'?: RoutePointExtension; -}; - -export type Coordinates = { - lat: number; - lon: number; -}; - -export type TrackType = { - name?: string; - cmt?: string; - desc?: string; - src?: string; - link?: Link; - type?: string; - extensions?: TrackExtensions; - trkseg: TrackSegmentType[]; -}; - -export type TrackExtensions = { - 'gpx_style:line'?: LineStyleExtension; -}; - -export type LineStyleExtension = { - 'gpx_style:color'?: string; - 'gpx_style:opacity'?: number; - 'gpx_style:width'?: number; -}; - -export type TrackSegmentType = { - trkpt: TrackPointType[]; -}; - -export type TrackPointType = { - attributes: Coordinates; - ele?: number; - time?: Date; - extensions?: TrackPointExtensions; -}; - -export type TrackPointExtensions = { - 'gpxtpx:TrackPointExtension'?: TrackPointExtension; - 'gpxpx:PowerExtension'?: PowerExtension; -}; - -export type TrackPointExtension = { - 'gpxtpx:atemp'?: number; - 'gpxtpx:hr'?: number; - 'gpxtpx:cad'?: number; - 'gpxtpx:Extensions'?: Record; -}; - -export type PowerExtension = { - 'gpxpx:PowerInWatts'?: number; -}; - -export type Author = { - name?: string; - email?: string; - link?: Link; -}; - -export type RouteType = { - name?: string; - cmt?: string; - desc?: string; - src?: string; - link?: Link; - type?: string; - extensions?: TrackExtensions; - rtept: WaypointType[]; -}; - -export type RoutePointExtension = { - 'gpxx:rpt'?: GPXXRoutePoint[]; -}; - -export type GPXXRoutePoint = { - attributes: Coordinates; -}; diff --git a/gpx/test-data/simple.gpx b/gpx/test-data/simple.gpx deleted file mode 100644 index be565954b..000000000 --- a/gpx/test-data/simple.gpx +++ /dev/null @@ -1,260 +0,0 @@ - - - - simple - - gpx.studio - - - - - simple - Cycling - - - 109.0 - - - 110.8 - - - 110.3 - - - 110.0 - - - 110.3 - - - 109.3 - - - 107.0 - - - 106.0 - - - 108.5 - - - 109.8 - - - 110.8 - - - 112.0 - - - 112.8 - - - 113.5 - - - 114.3 - - - 115.3 - - - 114.8 - - - 114.3 - - - 114.3 - - - 114.5 - - - 115.0 - - - 115.8 - - - 115.8 - - - 116.0 - - - 115.8 - - - 115.5 - - - 114.5 - - - 112.5 - - - 110.8 - - - 109.0 - - - 106.3 - - - 104.3 - - - 104.0 - - - 103.8 - - - 103.3 - - - 103.5 - - - 103.8 - - - 104.8 - - - 105.8 - - - 106.8 - - - 107.8 - - - 108.5 - - - 109.3 - - - 110.0 - - - 110.5 - - - 110.8 - - - 111.8 - - - 112.8 - - - 113.3 - - - 113.5 - - - 113.5 - - - 113.8 - - - 114.8 - - - 115.8 - - - 118.5 - - - 119.5 - - - 121.3 - - - 122.0 - - - 122.8 - - - 123.5 - - - 126.3 - - - 128.0 - - - 129.0 - - - 130.0 - - - 130.8 - - - 131.8 - - - 132.3 - - - 132.8 - - - 135.8 - - - 135.5 - - - 132.5 - - - 134.0 - - - 136.8 - - - 137.5 - - - 137.3 - - - 137.5 - - - 137.3 - - - 134.8 - - - 132.3 - - - 129.5 - - - - \ No newline at end of file diff --git a/gpx/test-data/with_cad.gpx b/gpx/test-data/with_cad.gpx deleted file mode 100644 index 1ac66703b..000000000 --- a/gpx/test-data/with_cad.gpx +++ /dev/null @@ -1,660 +0,0 @@ - - - - with_cad - - gpx.studio - - - - - with_cad - Cycling - - - 109.0 - - - 80 - - - - - 110.8 - - - 80 - - - - - 110.3 - - - 80 - - - - - 110.0 - - - 80 - - - - - 110.3 - - - 80 - - - - - 109.3 - - - 80 - - - - - 107.0 - - - 80 - - - - - 106.0 - - - 80 - - - - - 108.5 - - - 80 - - - - - 109.8 - - - 80 - - - - - 110.8 - - - 80 - - - - - 112.0 - - - 80 - - - - - 112.8 - - - 80 - - - - - 113.5 - - - 80 - - - - - 114.3 - - - 80 - - - - - 115.3 - - - 80 - - - - - 114.8 - - - 80 - - - - - 114.3 - - - 80 - - - - - 114.3 - - - 80 - - - - - 114.5 - - - 80 - - - - - 115.0 - - - 80 - - - - - 115.8 - - - 80 - - - - - 115.8 - - - 80 - - - - - 116.0 - - - 80 - - - - - 115.8 - - - 80 - - - - - 115.5 - - - 80 - - - - - 114.5 - - - 80 - - - - - 112.5 - - - 80 - - - - - 110.8 - - - 80 - - - - - 109.0 - - - 80 - - - - - 106.3 - - - 80 - - - - - 104.3 - - - 80 - - - - - 104.0 - - - 80 - - - - - 103.8 - - - 80 - - - - - 103.3 - - - 80 - - - - - 103.5 - - - 80 - - - - - 103.8 - - - 80 - - - - - 104.8 - - - 80 - - - - - 105.8 - - - 80 - - - - - 106.8 - - - 80 - - - - - 107.8 - - - 80 - - - - - 108.5 - - - 80 - - - - - 109.3 - - - 80 - - - - - 110.0 - - - 80 - - - - - 110.5 - - - 80 - - - - - 110.8 - - - 80 - - - - - 111.8 - - - 80 - - - - - 112.8 - - - 80 - - - - - 113.3 - - - 80 - - - - - 113.5 - - - 80 - - - - - 113.5 - - - 80 - - - - - 113.8 - - - 80 - - - - - 114.8 - - - 80 - - - - - 115.8 - - - 80 - - - - - 118.5 - - - 80 - - - - - 119.5 - - - 80 - - - - - 121.3 - - - 80 - - - - - 122.0 - - - 80 - - - - - 122.8 - - - 80 - - - - - 123.5 - - - 80 - - - - - 126.3 - - - 80 - - - - - 128.0 - - - 80 - - - - - 129.0 - - - 80 - - - - - 130.0 - - - 80 - - - - - 130.8 - - - 80 - - - - - 131.8 - - - 80 - - - - - 132.3 - - - 80 - - - - - 132.8 - - - 80 - - - - - 135.8 - - - 80 - - - - - 135.5 - - - 80 - - - - - 132.5 - - - 80 - - - - - 134.0 - - - 80 - - - - - 136.8 - - - 80 - - - - - 137.5 - - - 80 - - - - - 137.3 - - - 80 - - - - - 137.5 - - - 80 - - - - - 137.3 - - - 80 - - - - - 134.8 - - - 80 - - - - - 132.3 - - - 80 - - - - - 129.5 - - - 90 - - - - - - \ No newline at end of file diff --git a/gpx/test-data/with_hr.gpx b/gpx/test-data/with_hr.gpx deleted file mode 100644 index f2c566a59..000000000 --- a/gpx/test-data/with_hr.gpx +++ /dev/null @@ -1,660 +0,0 @@ - - - - with_hr - - gpx.studio - - - - - with_hr - Cycling - - - 109.0 - - - 150 - - - - - 110.8 - - - 150 - - - - - 110.3 - - - 150 - - - - - 110.0 - - - 150 - - - - - 110.3 - - - 150 - - - - - 109.3 - - - 150 - - - - - 107.0 - - - 150 - - - - - 106.0 - - - 150 - - - - - 108.5 - - - 150 - - - - - 109.8 - - - 150 - - - - - 110.8 - - - 150 - - - - - 112.0 - - - 150 - - - - - 112.8 - - - 150 - - - - - 113.5 - - - 150 - - - - - 114.3 - - - 150 - - - - - 115.3 - - - 150 - - - - - 114.8 - - - 150 - - - - - 114.3 - - - 150 - - - - - 114.3 - - - 150 - - - - - 114.5 - - - 150 - - - - - 115.0 - - - 150 - - - - - 115.8 - - - 150 - - - - - 115.8 - - - 150 - - - - - 116.0 - - - 150 - - - - - 115.8 - - - 150 - - - - - 115.5 - - - 150 - - - - - 114.5 - - - 150 - - - - - 112.5 - - - 150 - - - - - 110.8 - - - 150 - - - - - 109.0 - - - 150 - - - - - 106.3 - - - 150 - - - - - 104.3 - - - 150 - - - - - 104.0 - - - 150 - - - - - 103.8 - - - 150 - - - - - 103.3 - - - 150 - - - - - 103.5 - - - 150 - - - - - 103.8 - - - 150 - - - - - 104.8 - - - 150 - - - - - 105.8 - - - 150 - - - - - 106.8 - - - 150 - - - - - 107.8 - - - 150 - - - - - 108.5 - - - 150 - - - - - 109.3 - - - 150 - - - - - 110.0 - - - 150 - - - - - 110.5 - - - 150 - - - - - 110.8 - - - 150 - - - - - 111.8 - - - 150 - - - - - 112.8 - - - 150 - - - - - 113.3 - - - 150 - - - - - 113.5 - - - 150 - - - - - 113.5 - - - 150 - - - - - 113.8 - - - 150 - - - - - 114.8 - - - 150 - - - - - 115.8 - - - 150 - - - - - 118.5 - - - 150 - - - - - 119.5 - - - 150 - - - - - 121.3 - - - 150 - - - - - 122.0 - - - 150 - - - - - 122.8 - - - 150 - - - - - 123.5 - - - 150 - - - - - 126.3 - - - 150 - - - - - 128.0 - - - 150 - - - - - 129.0 - - - 150 - - - - - 130.0 - - - 150 - - - - - 130.8 - - - 150 - - - - - 131.8 - - - 150 - - - - - 132.3 - - - 150 - - - - - 132.8 - - - 150 - - - - - 135.8 - - - 150 - - - - - 135.5 - - - 150 - - - - - 132.5 - - - 150 - - - - - 134.0 - - - 150 - - - - - 136.8 - - - 150 - - - - - 137.5 - - - 150 - - - - - 137.3 - - - 150 - - - - - 137.5 - - - 150 - - - - - 137.3 - - - 150 - - - - - 134.8 - - - 150 - - - - - 132.3 - - - 150 - - - - - 129.5 - - - 160 - - - - - - \ No newline at end of file diff --git a/gpx/test-data/with_power_1.gpx b/gpx/test-data/with_power_1.gpx deleted file mode 100644 index 5c6407762..000000000 --- a/gpx/test-data/with_power_1.gpx +++ /dev/null @@ -1,500 +0,0 @@ - - - - with_power - - gpx.studio - - - - - with_power - Cycling - - - 109.0 - - 200 - - - - 110.8 - - 200 - - - - 110.3 - - 200 - - - - 110.0 - - 200 - - - - 110.3 - - 200 - - - - 109.3 - - 200 - - - - 107.0 - - 200 - - - - 106.0 - - 200 - - - - 108.5 - - 200 - - - - 109.8 - - 200 - - - - 110.8 - - 200 - - - - 112.0 - - 200 - - - - 112.8 - - 200 - - - - 113.5 - - 200 - - - - 114.3 - - 200 - - - - 115.3 - - 200 - - - - 114.8 - - 200 - - - - 114.3 - - 200 - - - - 114.3 - - 200 - - - - 114.5 - - 200 - - - - 115.0 - - 200 - - - - 115.8 - - 200 - - - - 115.8 - - 200 - - - - 116.0 - - 200 - - - - 115.8 - - 200 - - - - 115.5 - - 200 - - - - 114.5 - - 200 - - - - 112.5 - - 200 - - - - 110.8 - - 200 - - - - 109.0 - - 200 - - - - 106.3 - - 200 - - - - 104.3 - - 200 - - - - 104.0 - - 200 - - - - 103.8 - - 200 - - - - 103.3 - - 200 - - - - 103.5 - - 200 - - - - 103.8 - - 200 - - - - 104.8 - - 200 - - - - 105.8 - - 200 - - - - 106.8 - - 200 - - - - 107.8 - - 200 - - - - 108.5 - - 200 - - - - 109.3 - - 200 - - - - 110.0 - - 200 - - - - 110.5 - - 200 - - - - 110.8 - - 200 - - - - 111.8 - - 200 - - - - 112.8 - - 200 - - - - 113.3 - - 200 - - - - 113.5 - - 200 - - - - 113.5 - - 200 - - - - 113.8 - - 200 - - - - 114.8 - - 200 - - - - 115.8 - - 200 - - - - 118.5 - - 200 - - - - 119.5 - - 200 - - - - 121.3 - - 200 - - - - 122.0 - - 200 - - - - 122.8 - - 200 - - - - 123.5 - - 200 - - - - 126.3 - - 200 - - - - 128.0 - - 200 - - - - 129.0 - - 200 - - - - 130.0 - - 200 - - - - 130.8 - - 200 - - - - 131.8 - - 200 - - - - 132.3 - - 200 - - - - 132.8 - - 200 - - - - 135.8 - - 200 - - - - 135.5 - - 200 - - - - 132.5 - - 200 - - - - 134.0 - - 200 - - - - 136.8 - - 200 - - - - 137.5 - - 200 - - - - 137.3 - - 200 - - - - 137.5 - - 200 - - - - 137.3 - - 200 - - - - 134.8 - - 200 - - - - 132.3 - - 200 - - - - 129.5 - - 210 - - - - - \ No newline at end of file diff --git a/gpx/test-data/with_power_2.gpx b/gpx/test-data/with_power_2.gpx deleted file mode 100644 index b0a530733..000000000 --- a/gpx/test-data/with_power_2.gpx +++ /dev/null @@ -1,660 +0,0 @@ - - - - with_power - - gpx.studio - - - - - with_power - Cycling - - - 109.0 - - - 200 - - - - - 110.8 - - - 200 - - - - - 110.3 - - - 200 - - - - - 110.0 - - - 200 - - - - - 110.3 - - - 200 - - - - - 109.3 - - - 200 - - - - - 107.0 - - - 200 - - - - - 106.0 - - - 200 - - - - - 108.5 - - - 200 - - - - - 109.8 - - - 200 - - - - - 110.8 - - - 200 - - - - - 112.0 - - - 200 - - - - - 112.8 - - - 200 - - - - - 113.5 - - - 200 - - - - - 114.3 - - - 200 - - - - - 115.3 - - - 200 - - - - - 114.8 - - - 200 - - - - - 114.3 - - - 200 - - - - - 114.3 - - - 200 - - - - - 114.5 - - - 200 - - - - - 115.0 - - - 200 - - - - - 115.8 - - - 200 - - - - - 115.8 - - - 200 - - - - - 116.0 - - - 200 - - - - - 115.8 - - - 200 - - - - - 115.5 - - - 200 - - - - - 114.5 - - - 200 - - - - - 112.5 - - - 200 - - - - - 110.8 - - - 200 - - - - - 109.0 - - - 200 - - - - - 106.3 - - - 200 - - - - - 104.3 - - - 200 - - - - - 104.0 - - - 200 - - - - - 103.8 - - - 200 - - - - - 103.3 - - - 200 - - - - - 103.5 - - - 200 - - - - - 103.8 - - - 200 - - - - - 104.8 - - - 200 - - - - - 105.8 - - - 200 - - - - - 106.8 - - - 200 - - - - - 107.8 - - - 200 - - - - - 108.5 - - - 200 - - - - - 109.3 - - - 200 - - - - - 110.0 - - - 200 - - - - - 110.5 - - - 200 - - - - - 110.8 - - - 200 - - - - - 111.8 - - - 200 - - - - - 112.8 - - - 200 - - - - - 113.3 - - - 200 - - - - - 113.5 - - - 200 - - - - - 113.5 - - - 200 - - - - - 113.8 - - - 200 - - - - - 114.8 - - - 200 - - - - - 115.8 - - - 200 - - - - - 118.5 - - - 200 - - - - - 119.5 - - - 200 - - - - - 121.3 - - - 200 - - - - - 122.0 - - - 200 - - - - - 122.8 - - - 200 - - - - - 123.5 - - - 200 - - - - - 126.3 - - - 200 - - - - - 128.0 - - - 200 - - - - - 129.0 - - - 200 - - - - - 130.0 - - - 200 - - - - - 130.8 - - - 200 - - - - - 131.8 - - - 200 - - - - - 132.3 - - - 200 - - - - - 132.8 - - - 200 - - - - - 135.8 - - - 200 - - - - - 135.5 - - - 200 - - - - - 132.5 - - - 200 - - - - - 134.0 - - - 200 - - - - - 136.8 - - - 200 - - - - - 137.5 - - - 200 - - - - - 137.3 - - - 200 - - - - - 137.5 - - - 200 - - - - - 137.3 - - - 200 - - - - - 134.8 - - - 200 - - - - - 132.3 - - - 200 - - - - - 129.5 - - - 210 - - - - - - \ No newline at end of file diff --git a/gpx/test-data/with_routes.gpx b/gpx/test-data/with_routes.gpx deleted file mode 100644 index fd55a291d..000000000 --- a/gpx/test-data/with_routes.gpx +++ /dev/null @@ -1,253 +0,0 @@ - - - - with_routes - - gpx.studio - - - - - route 1 - Cycling - - 109.0 - - - 110.8 - - - 110.3 - - - 110.0 - - - 110.3 - - - 109.3 - - - 107.0 - - - 106.0 - - - 108.5 - - - 109.8 - - - 110.8 - - - 112.0 - - - 112.8 - - - 113.5 - - - 114.3 - - - 115.3 - - - 114.8 - - - 114.3 - - - 114.3 - - - 114.5 - - - 115.0 - - - 115.8 - - - 115.8 - - - 116.0 - - - 115.8 - - - 115.5 - - - 114.5 - - - 112.5 - - - 110.8 - - - 109.0 - - - 106.3 - - - 104.3 - - - 104.0 - - - 103.8 - - - 103.3 - - - 103.5 - - - 103.8 - - - 104.8 - - - 105.8 - - - 106.8 - - - 107.8 - - - 108.5 - - - 109.3 - - - 110.0 - - - 110.5 - - - 110.8 - - - 111.8 - - - 112.8 - - - 113.3 - - - - route 2 - Cycling - - 115.5 - - - 115.8 - - - 118.5 - - - 119.5 - - - 121.3 - - - 122.0 - - - 122.8 - - - 123.5 - - - 126.3 - - - 128.0 - - - 129.0 - - - 130.0 - - - 130.8 - - - 131.8 - - - 132.3 - - - 132.8 - - - 135.8 - - - 135.5 - - - 132.5 - - - 134.0 - - - 136.8 - - - 137.5 - - - 137.3 - - - 137.5 - - - 137.3 - - - 134.8 - - - 132.3 - - - 129.5 - - - diff --git a/gpx/test-data/with_segments.gpx b/gpx/test-data/with_segments.gpx deleted file mode 100644 index 05690acb5..000000000 --- a/gpx/test-data/with_segments.gpx +++ /dev/null @@ -1,253 +0,0 @@ - - - - with_segments - - gpx.studio - - - - - with_segments - Cycling - - - 109.0 - - - 110.8 - - - 110.3 - - - 110.0 - - - 110.3 - - - 109.3 - - - 107.0 - - - 106.0 - - - 108.5 - - - 109.8 - - - 110.8 - - - 112.0 - - - 112.8 - - - 113.5 - - - 114.3 - - - 115.3 - - - 114.8 - - - 114.3 - - - 114.3 - - - 114.5 - - - 115.0 - - - 115.8 - - - 115.8 - - - 116.0 - - - 115.8 - - - 115.5 - - - 114.5 - - - 112.5 - - - 110.8 - - - 109.0 - - - 106.3 - - - 104.3 - - - 104.0 - - - 103.8 - - - 103.3 - - - 103.5 - - - 103.8 - - - 104.8 - - - 105.8 - - - 106.8 - - - 107.8 - - - 108.5 - - - 109.3 - - - 110.0 - - - 110.5 - - - 110.8 - - - 111.8 - - - 112.8 - - - 113.3 - - - - - 115.5 - - - 115.8 - - - 118.5 - - - 119.5 - - - 121.3 - - - 122.0 - - - 122.8 - - - 123.5 - - - 126.3 - - - 128.0 - - - 129.0 - - - 130.0 - - - 130.8 - - - 131.8 - - - 132.3 - - - 132.8 - - - 135.8 - - - 135.5 - - - 132.5 - - - 134.0 - - - 136.8 - - - 137.5 - - - 137.3 - - - 137.5 - - - 137.3 - - - 134.8 - - - 132.3 - - - 129.5 - - - - \ No newline at end of file diff --git a/gpx/test-data/with_style.gpx b/gpx/test-data/with_style.gpx deleted file mode 100644 index 98b5e7fc4..000000000 --- a/gpx/test-data/with_style.gpx +++ /dev/null @@ -1,267 +0,0 @@ - - - - with_style - - gpx.studio - - - - - with_style - Cycling - - - 2d3ee9 - 0.5 - 6 - - - - - 109.0 - - - 110.8 - - - 110.3 - - - 110.0 - - - 110.3 - - - 109.3 - - - 107.0 - - - 106.0 - - - 108.5 - - - 109.8 - - - 110.8 - - - 112.0 - - - 112.8 - - - 113.5 - - - 114.3 - - - 115.3 - - - 114.8 - - - 114.3 - - - 114.3 - - - 114.5 - - - 115.0 - - - 115.8 - - - 115.8 - - - 116.0 - - - 115.8 - - - 115.5 - - - 114.5 - - - 112.5 - - - 110.8 - - - 109.0 - - - 106.3 - - - 104.3 - - - 104.0 - - - 103.8 - - - 103.3 - - - 103.5 - - - 103.8 - - - 104.8 - - - 105.8 - - - 106.8 - - - 107.8 - - - 108.5 - - - 109.3 - - - 110.0 - - - 110.5 - - - 110.8 - - - 111.8 - - - 112.8 - - - 113.3 - - - 113.5 - - - 113.5 - - - 113.8 - - - 114.8 - - - 115.8 - - - 118.5 - - - 119.5 - - - 121.3 - - - 122.0 - - - 122.8 - - - 123.5 - - - 126.3 - - - 128.0 - - - 129.0 - - - 130.0 - - - 130.8 - - - 131.8 - - - 132.3 - - - 132.8 - - - 135.8 - - - 135.5 - - - 132.5 - - - 134.0 - - - 136.8 - - - 137.5 - - - 137.3 - - - 137.5 - - - 137.3 - - - 134.8 - - - 132.3 - - - 129.5 - - - - \ No newline at end of file diff --git a/gpx/test-data/with_surface.gpx b/gpx/test-data/with_surface.gpx deleted file mode 100644 index a4d539100..000000000 --- a/gpx/test-data/with_surface.gpx +++ /dev/null @@ -1,820 +0,0 @@ - - - - with_surface - - gpx.studio - - - - - with_surface - Cycling - - - 109.0 - - - - asphalt - - - - - - 110.8 - - - - asphalt - - - - - - 110.3 - - - - asphalt - - - - - - 110.0 - - - - asphalt - - - - - - 110.3 - - - - asphalt - - - - - - 109.3 - - - - asphalt - - - - - - 107.0 - - - - asphalt - - - - - - 106.0 - - - - asphalt - - - - - - 108.5 - - - - asphalt - - - - - - 109.8 - - - - asphalt - - - - - - 110.8 - - - - asphalt - - - - - - 112.0 - - - - asphalt - - - - - - 112.8 - - - - asphalt - - - - - - 113.5 - - - - asphalt - - - - - - 114.3 - - - - asphalt - - - - - - 115.3 - - - - asphalt - - - - - - 114.8 - - - - asphalt - - - - - - 114.3 - - - - asphalt - - - - - - 114.3 - - - - asphalt - - - - - - 114.5 - - - - asphalt - - - - - - 115.0 - - - - asphalt - - - - - - 115.8 - - - - asphalt - - - - - - 115.8 - - - - asphalt - - - - - - 116.0 - - - - asphalt - - - - - - 115.8 - - - - asphalt - - - - - - 115.5 - - - - asphalt - - - - - - 114.5 - - - - asphalt - - - - - - 112.5 - - - - asphalt - - - - - - 110.8 - - - - asphalt - - - - - - 109.0 - - - - asphalt - - - - - - 106.3 - - - - asphalt - - - - - - 104.3 - - - - asphalt - - - - - - 104.0 - - - - asphalt - - - - - - 103.8 - - - - asphalt - - - - - - 103.3 - - - - asphalt - - - - - - 103.5 - - - - asphalt - - - - - - 103.8 - - - - asphalt - - - - - - 104.8 - - - - asphalt - - - - - - 105.8 - - - - asphalt - - - - - - 106.8 - - - - asphalt - - - - - - 107.8 - - - - asphalt - - - - - - 108.5 - - - - asphalt - - - - - - 109.3 - - - - asphalt - - - - - - 110.0 - - - - asphalt - - - - - - 110.5 - - - - asphalt - - - - - - 110.8 - - - - asphalt - - - - - - 111.8 - - - - asphalt - - - - - - 112.8 - - - - asphalt - - - - - - 113.3 - - - - asphalt - - - - - - 113.5 - - - - asphalt - - - - - - 113.5 - - - - asphalt - - - - - - 113.8 - - - - asphalt - - - - - - 114.8 - - - - asphalt - - - - - - 115.8 - - - - asphalt - - - - - - 118.5 - - - - asphalt - - - - - - 119.5 - - - - asphalt - - - - - - 121.3 - - - - asphalt - - - - - - 122.0 - - - - asphalt - - - - - - 122.8 - - - - asphalt - - - - - - 123.5 - - - - asphalt - - - - - - 126.3 - - - - asphalt - - - - - - 128.0 - - - - asphalt - - - - - - 129.0 - - - - asphalt - - - - - - 130.0 - - - - asphalt - - - - - - 130.8 - - - - asphalt - - - - - - 131.8 - - - - asphalt - - - - - - 132.3 - - - - asphalt - - - - - - 132.8 - - - - asphalt - - - - - - 135.8 - - - - asphalt - - - - - - 135.5 - - - - asphalt - - - - - - 132.5 - - - - asphalt - - - - - - 134.0 - - - - asphalt - - - - - - 136.8 - - - - asphalt - - - - - - 137.5 - - - - asphalt - - - - - - 137.3 - - - - asphalt - - - - - - 137.5 - - - - asphalt - - - - - - 137.3 - - - - asphalt - - - - - - 134.8 - - - - asphalt - - - - - - 132.3 - - - - asphalt - - - - - - 129.5 - - - - cobblestone - - - - - - - \ No newline at end of file diff --git a/gpx/test-data/with_temp.gpx b/gpx/test-data/with_temp.gpx deleted file mode 100644 index b827235c6..000000000 --- a/gpx/test-data/with_temp.gpx +++ /dev/null @@ -1,660 +0,0 @@ - - - - with_temp - - gpx.studio - - - - - with_temp - Cycling - - - 109.0 - - - 21 - - - - - 110.8 - - - 21 - - - - - 110.3 - - - 21 - - - - - 110.0 - - - 21 - - - - - 110.3 - - - 21 - - - - - 109.3 - - - 21 - - - - - 107.0 - - - 21 - - - - - 106.0 - - - 21 - - - - - 108.5 - - - 21 - - - - - 109.8 - - - 21 - - - - - 110.8 - - - 21 - - - - - 112.0 - - - 21 - - - - - 112.8 - - - 21 - - - - - 113.5 - - - 21 - - - - - 114.3 - - - 21 - - - - - 115.3 - - - 21 - - - - - 114.8 - - - 21 - - - - - 114.3 - - - 21 - - - - - 114.3 - - - 21 - - - - - 114.5 - - - 21 - - - - - 115.0 - - - 21 - - - - - 115.8 - - - 21 - - - - - 115.8 - - - 21 - - - - - 116.0 - - - 21 - - - - - 115.8 - - - 21 - - - - - 115.5 - - - 21 - - - - - 114.5 - - - 21 - - - - - 112.5 - - - 21 - - - - - 110.8 - - - 21 - - - - - 109.0 - - - 21 - - - - - 106.3 - - - 21 - - - - - 104.3 - - - 21 - - - - - 104.0 - - - 21 - - - - - 103.8 - - - 21 - - - - - 103.3 - - - 21 - - - - - 103.5 - - - 21 - - - - - 103.8 - - - 21 - - - - - 104.8 - - - 21 - - - - - 105.8 - - - 21 - - - - - 106.8 - - - 21 - - - - - 107.8 - - - 21 - - - - - 108.5 - - - 21 - - - - - 109.3 - - - 21 - - - - - 110.0 - - - 21 - - - - - 110.5 - - - 21 - - - - - 110.8 - - - 21 - - - - - 111.8 - - - 21 - - - - - 112.8 - - - 21 - - - - - 113.3 - - - 21 - - - - - 113.5 - - - 21 - - - - - 113.5 - - - 21 - - - - - 113.8 - - - 21 - - - - - 114.8 - - - 21 - - - - - 115.8 - - - 21 - - - - - 118.5 - - - 21 - - - - - 119.5 - - - 21 - - - - - 121.3 - - - 21 - - - - - 122.0 - - - 21 - - - - - 122.8 - - - 21 - - - - - 123.5 - - - 21 - - - - - 126.3 - - - 21 - - - - - 128.0 - - - 21 - - - - - 129.0 - - - 21 - - - - - 130.0 - - - 21 - - - - - 130.8 - - - 21 - - - - - 131.8 - - - 21 - - - - - 132.3 - - - 21 - - - - - 132.8 - - - 21 - - - - - 135.8 - - - 21 - - - - - 135.5 - - - 21 - - - - - 132.5 - - - 21 - - - - - 134.0 - - - 21 - - - - - 136.8 - - - 21 - - - - - 137.5 - - - 21 - - - - - 137.3 - - - 21 - - - - - 137.5 - - - 21 - - - - - 137.3 - - - 21 - - - - - 134.8 - - - 21 - - - - - 132.3 - - - 21 - - - - - 129.5 - - - 22 - - - - - - \ No newline at end of file diff --git a/gpx/test-data/with_time.gpx b/gpx/test-data/with_time.gpx deleted file mode 100644 index f39d90ad3..000000000 --- a/gpx/test-data/with_time.gpx +++ /dev/null @@ -1,340 +0,0 @@ - - - - with_time - - gpx.studio - - - - - with_time - Cycling - - - 109.0 - - - - 110.8 - - - - 110.3 - - - - 110.0 - - - - 110.3 - - - - 109.3 - - - - 107.0 - - - - 106.0 - - - - 108.5 - - - - 109.8 - - - - 110.8 - - - - 112.0 - - - - 112.8 - - - - 113.5 - - - - 114.3 - - - - 115.3 - - - - 114.8 - - - - 114.3 - - - - 114.3 - - - - 114.5 - - - - 115.0 - - - - 115.8 - - - - 115.8 - - - - 116.0 - - - - 115.8 - - - - 115.5 - - - - 114.5 - - - - 112.5 - - - - 110.8 - - - - 109.0 - - - - 106.3 - - - - 104.3 - - - - 104.0 - - - - 103.8 - - - - 103.3 - - - - 103.5 - - - - 103.8 - - - - 104.8 - - - - 105.8 - - - - 106.8 - - - - 107.8 - - - - 108.5 - - - - 109.3 - - - - 110.0 - - - - 110.5 - - - - 110.8 - - - - 111.8 - - - - 112.8 - - - - 113.3 - - - - 113.5 - - - - 113.5 - - - - 113.8 - - - - 114.8 - - - - 115.8 - - - - 118.5 - - - - 119.5 - - - - 121.3 - - - - 122.0 - - - - 122.8 - - - - 123.5 - - - - 126.3 - - - - 128.0 - - - - 129.0 - - - - 130.0 - - - - 130.8 - - - - 131.8 - - - - 132.3 - - - - 132.8 - - - - 135.8 - - - - 135.5 - - - - 132.5 - - - - 134.0 - - - - 136.8 - - - - 137.5 - - - - 137.3 - - - - 137.5 - - - - 137.3 - - - - 134.8 - - - - 132.3 - - - - 129.5 - - - - - \ No newline at end of file diff --git a/gpx/test-data/with_tracks.gpx b/gpx/test-data/with_tracks.gpx deleted file mode 100644 index 937b53e55..000000000 --- a/gpx/test-data/with_tracks.gpx +++ /dev/null @@ -1,257 +0,0 @@ - - - - with_tracks - - gpx.studio - - - - - track 1 - Cycling - - - 109.0 - - - 110.8 - - - 110.3 - - - 110.0 - - - 110.3 - - - 109.3 - - - 107.0 - - - 106.0 - - - 108.5 - - - 109.8 - - - 110.8 - - - 112.0 - - - 112.8 - - - 113.5 - - - 114.3 - - - 115.3 - - - 114.8 - - - 114.3 - - - 114.3 - - - 114.5 - - - 115.0 - - - 115.8 - - - 115.8 - - - 116.0 - - - 115.8 - - - 115.5 - - - 114.5 - - - 112.5 - - - 110.8 - - - 109.0 - - - 106.3 - - - 104.3 - - - 104.0 - - - 103.8 - - - 103.3 - - - 103.5 - - - 103.8 - - - 104.8 - - - 105.8 - - - 106.8 - - - 107.8 - - - 108.5 - - - 109.3 - - - 110.0 - - - 110.5 - - - 110.8 - - - 111.8 - - - 112.8 - - - 113.3 - - - - - track 2 - Cycling - - - 115.5 - - - 115.8 - - - 118.5 - - - 119.5 - - - 121.3 - - - 122.0 - - - 122.8 - - - 123.5 - - - 126.3 - - - 128.0 - - - 129.0 - - - 130.0 - - - 130.8 - - - 131.8 - - - 132.3 - - - 132.8 - - - 135.8 - - - 135.5 - - - 132.5 - - - 134.0 - - - 136.8 - - - 137.5 - - - 137.3 - - - 137.5 - - - 137.3 - - - 134.8 - - - 132.3 - - - 129.5 - - - - \ No newline at end of file diff --git a/gpx/test-data/with_tracks_and_segments.gpx b/gpx/test-data/with_tracks_and_segments.gpx deleted file mode 100644 index f10e720a9..000000000 --- a/gpx/test-data/with_tracks_and_segments.gpx +++ /dev/null @@ -1,346 +0,0 @@ - - - - with_tracks_and_segments - - gpx.studio - - - - - track 1 - Running - - - 109.0 - - - - 110.8 - - - - 110.3 - - - - 110.0 - - - - 110.3 - - - - 109.3 - - - - 107.0 - - - - 106.0 - - - - 108.5 - - - - 109.8 - - - - 110.8 - - - - 112.0 - - - - 112.8 - - - - 113.5 - - - - 114.3 - - - - 115.3 - - - - - - 115.0 - - - - 114.8 - - - - 114.3 - - - - 114.3 - - - - 114.5 - - - - 115.0 - - - - 115.8 - - - - 115.8 - - - - 116.0 - - - - 115.8 - - - - 115.5 - - - - 114.5 - - - - 112.5 - - - - 110.8 - - - - 109.0 - - - - 106.3 - - - - 104.3 - - - - 104.0 - - - - 103.8 - - - - 103.3 - - - - 103.5 - - - - 103.8 - - - - 104.8 - - - - 105.8 - - - - 106.8 - - - - 107.8 - - - - 108.5 - - - - 109.3 - - - - 110.0 - - - - 110.5 - - - - 110.8 - - - - 111.8 - - - - 112.8 - - - - 113.3 - - - - - - track 2 - Running - - - 115.5 - - - - 115.8 - - - - 118.5 - - - - 119.5 - - - - 121.3 - - - - 122.0 - - - - 122.8 - - - - 123.5 - - - - 126.3 - - - - 128.0 - - - - 129.0 - - - - 130.0 - - - - 130.8 - - - - 131.8 - - - - 132.3 - - - - 132.8 - - - - 135.8 - - - - 135.5 - - - - 132.5 - - - - - - 133.3 - - - - 134.0 - - - - 136.8 - - - - 137.5 - - - - 137.3 - - - - 137.5 - - - - 137.3 - - - - 134.8 - - - - 132.3 - - - - 129.5 - - - - - \ No newline at end of file diff --git a/gpx/test-data/with_waypoint.gpx b/gpx/test-data/with_waypoint.gpx deleted file mode 100644 index c908b0515..000000000 --- a/gpx/test-data/with_waypoint.gpx +++ /dev/null @@ -1,267 +0,0 @@ - - - - with_waypoint - - gpx.studio - - - - - 122.0 - Waypoint - Comment - Description - Bike Trail - - - with_waypoint - Cycling - - - 109.0 - - - 110.8 - - - 110.3 - - - 110.0 - - - 110.3 - - - 109.3 - - - 107.0 - - - 106.0 - - - 108.5 - - - 109.8 - - - 110.8 - - - 112.0 - - - 112.8 - - - 113.5 - - - 114.3 - - - 115.3 - - - 114.8 - - - 114.3 - - - 114.3 - - - 114.5 - - - 115.0 - - - 115.8 - - - 115.8 - - - 116.0 - - - 115.8 - - - 115.5 - - - 114.5 - - - 112.5 - - - 110.8 - - - 109.0 - - - 106.3 - - - 104.3 - - - 104.0 - - - 103.8 - - - 103.3 - - - 103.5 - - - 103.8 - - - 104.8 - - - 105.8 - - - 106.8 - - - 107.8 - - - 108.5 - - - 109.3 - - - 110.0 - - - 110.5 - - - 110.8 - - - 111.8 - - - 112.8 - - - 113.3 - - - 113.5 - - - 113.5 - - - 113.8 - - - 114.8 - - - 115.8 - - - 118.5 - - - 119.5 - - - 121.3 - - - 122.0 - - - 122.8 - - - 123.5 - - - 126.3 - - - 128.0 - - - 129.0 - - - 130.0 - - - 130.8 - - - 131.8 - - - 132.3 - - - 132.8 - - - 135.8 - - - 135.5 - - - 132.5 - - - 134.0 - - - 136.8 - - - 137.5 - - - 137.3 - - - 137.5 - - - 137.3 - - - 134.8 - - - 132.3 - - - 129.5 - - - - \ No newline at end of file diff --git a/gpx/tsconfig.json b/gpx/tsconfig.json deleted file mode 100644 index 56f696ec0..000000000 --- a/gpx/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "module": "ES2020", - "target": "ES2015", - "declaration": true, - "outDir": "./dist", - "moduleResolution": "node" - }, - "include": ["src"] -}