finishes trail printing
This commit is contained in:
216
web/package-lock.json
generated
216
web/package-lock.json
generated
@@ -14,8 +14,10 @@
|
||||
"@threlte/extras": "^8.7.5",
|
||||
"@types/leaflet.awesome-markers": "^2.0.28",
|
||||
"@types/three": "^0.161.2",
|
||||
"canvg": "^4.0.1",
|
||||
"crypto-random-string": "^5.0.0",
|
||||
"jsdom": "^24.0.0",
|
||||
"jspdf": "^2.5.1",
|
||||
"leaflet": "^1.9.4",
|
||||
"leaflet-gpx": "^1.7.0",
|
||||
"leaflet.awesome-markers": "^2.0.5",
|
||||
@@ -73,6 +75,17 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz",
|
||||
"integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz",
|
||||
@@ -1105,12 +1118,22 @@
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/offscreencanvas": {
|
||||
"version": "2019.7.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz",
|
||||
"integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A=="
|
||||
},
|
||||
"node_modules/@types/pug": {
|
||||
"version": "2.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz",
|
||||
"integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/raf": {
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz",
|
||||
"integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw=="
|
||||
},
|
||||
"node_modules/@types/resolve": {
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
|
||||
@@ -1329,6 +1352,17 @@
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"node_modules/atob": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
|
||||
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
|
||||
"bin": {
|
||||
"atob": "bin/atob.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.17",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz",
|
||||
@@ -1393,6 +1427,15 @@
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"node_modules/base64-arraybuffer": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
||||
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
@@ -1491,6 +1534,17 @@
|
||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||
}
|
||||
},
|
||||
"node_modules/btoa": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz",
|
||||
"integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==",
|
||||
"bin": {
|
||||
"btoa": "bin/btoa.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-crc32": {
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
|
||||
@@ -1584,6 +1638,22 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/canvg": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/canvg/-/canvg-4.0.1.tgz",
|
||||
"integrity": "sha512-5gD/d6SiCCT7baLnVr0hokYe93DfcHW2rSqdKOuOQD84YMlyfttnZ8iQsThTdX6koYam+PROz/FuQTo500zqGw==",
|
||||
"dependencies": {
|
||||
"@types/offscreencanvas": "^2019.6.4",
|
||||
"@types/raf": "^3.4.0",
|
||||
"raf": "^3.4.1",
|
||||
"rgbcolor": "^1.0.1",
|
||||
"stackblur-canvas": "^2.0.0",
|
||||
"svg-pathdata": "^6.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/chai": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz",
|
||||
@@ -1805,6 +1875,17 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/core-js": {
|
||||
"version": "3.36.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.36.1.tgz",
|
||||
"integrity": "sha512-BTvUrwxVBezj5SZ3f10ImnX2oRByMxql3EimVqMysepbC9EeMUOpLwdy6Eoili2x6E4kf+ZUB5k/+Jv55alPfA==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-fetch": {
|
||||
"version": "3.1.8",
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz",
|
||||
@@ -1846,6 +1927,15 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/css-line-break": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
|
||||
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"utrie": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/css-tree": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
|
||||
@@ -2106,6 +2196,12 @@
|
||||
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.0.tgz",
|
||||
"integrity": "sha512-5RXhAXSCrKTqt9pSbobT9PVRX+oPpENplTZqCiK1l0ya+ZOzwo9kqsGLbYRsAhzIiLCwKEy99XKSSrqnRTLVcw==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
@@ -2689,6 +2785,19 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/html2canvas": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
|
||||
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"css-line-break": "^2.1.0",
|
||||
"text-segmentation": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/http-proxy-agent": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
|
||||
@@ -3225,6 +3334,53 @@
|
||||
"integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/jspdf": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.5.1.tgz",
|
||||
"integrity": "sha512-hXObxz7ZqoyhxET78+XR34Xu2qFGrJJ2I2bE5w4SM8eFaFEkW2xcGRVUss360fYelwRSid/jT078kbNvmoW0QA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.14.0",
|
||||
"atob": "^2.1.2",
|
||||
"btoa": "^1.2.1",
|
||||
"fflate": "^0.4.8"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"canvg": "^3.0.6",
|
||||
"core-js": "^3.6.0",
|
||||
"dompurify": "^2.2.0",
|
||||
"html2canvas": "^1.0.0-rc.5"
|
||||
}
|
||||
},
|
||||
"node_modules/jspdf/node_modules/canvg": {
|
||||
"version": "3.0.10",
|
||||
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz",
|
||||
"integrity": "sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@types/raf": "^3.4.0",
|
||||
"core-js": "^3.8.3",
|
||||
"raf": "^3.4.1",
|
||||
"regenerator-runtime": "^0.13.7",
|
||||
"rgbcolor": "^1.0.1",
|
||||
"stackblur-canvas": "^2.0.0",
|
||||
"svg-pathdata": "^6.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jspdf/node_modules/fflate": {
|
||||
"version": "0.4.8",
|
||||
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz",
|
||||
"integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA=="
|
||||
},
|
||||
"node_modules/jspdf/node_modules/regenerator-runtime": {
|
||||
"version": "0.13.11",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/kleur": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
|
||||
@@ -3882,6 +4038,11 @@
|
||||
"png-js": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/performance-now": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
|
||||
},
|
||||
"node_modules/periscopic": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz",
|
||||
@@ -4228,6 +4389,14 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/raf": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
|
||||
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
|
||||
"dependencies": {
|
||||
"performance-now": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
|
||||
@@ -4255,6 +4424,11 @@
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/regenerator-runtime": {
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
|
||||
},
|
||||
"node_modules/regexp.prototype.flags": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz",
|
||||
@@ -4338,6 +4512,14 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rgbcolor": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz",
|
||||
"integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==",
|
||||
"engines": {
|
||||
"node": ">= 0.8.15"
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
@@ -4586,6 +4768,14 @@
|
||||
"integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/stackblur-canvas": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz",
|
||||
"integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==",
|
||||
"engines": {
|
||||
"node": ">=0.1.14"
|
||||
}
|
||||
},
|
||||
"node_modules/std-env": {
|
||||
"version": "3.7.0",
|
||||
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz",
|
||||
@@ -4962,6 +5152,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/svg-pathdata": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz",
|
||||
"integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/symbol-tree": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
||||
@@ -5016,6 +5214,15 @@
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/text-segmentation": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
|
||||
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"utrie": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/thenify": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
||||
@@ -5305,6 +5512,15 @@
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/utrie": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
|
||||
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"base64-arraybuffer": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.0.12",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz",
|
||||
|
||||
@@ -39,8 +39,10 @@
|
||||
"@threlte/extras": "^8.7.5",
|
||||
"@types/leaflet.awesome-markers": "^2.0.28",
|
||||
"@types/three": "^0.161.2",
|
||||
"canvg": "^4.0.1",
|
||||
"crypto-random-string": "^5.0.0",
|
||||
"jsdom": "^24.0.0",
|
||||
"jspdf": "^2.5.1",
|
||||
"leaflet": "^1.9.4",
|
||||
"leaflet-gpx": "^1.7.0",
|
||||
"leaflet.awesome-markers": "^2.0.5",
|
||||
|
||||
7
web/src/lib/assets/fonts/IBMPlexSans-Regular-normal.js
Normal file
7
web/src/lib/assets/fonts/IBMPlexSans-Regular-normal.js
Normal file
File diff suppressed because one or more lines are too long
7
web/src/lib/assets/fonts/IBMPlexSans-SemiBold-bold.js
Normal file
7
web/src/lib/assets/fonts/IBMPlexSans-SemiBold-bold.js
Normal file
File diff suppressed because one or more lines are too long
7
web/src/lib/assets/fonts/fa-solid-900-normal.js
Normal file
7
web/src/lib/assets/fonts/fa-solid-900-normal.js
Normal file
File diff suppressed because one or more lines are too long
@@ -2,6 +2,7 @@
|
||||
import type { Trail } from "$lib/models/trail";
|
||||
import { currentUser } from "$lib/stores/user_store";
|
||||
import { createMarkerFromWaypoint } from "$lib/util/leaflet_util";
|
||||
import type AutoGraticule from "$lib/vendor/leaflet-graticule/leaflet-auto-graticule";
|
||||
import type { Map, Marker } from "leaflet";
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
|
||||
@@ -9,6 +10,7 @@
|
||||
export let markers: Marker[] = [];
|
||||
export let map: Map | null = null;
|
||||
export let options = {};
|
||||
export let graticule: AutoGraticule | null = null;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
@@ -110,7 +112,8 @@
|
||||
}
|
||||
|
||||
if(elevation_options.graticule) {
|
||||
new AutoGraticule().addTo(map!);
|
||||
graticule = new AutoGraticule();
|
||||
graticule.addTo(map!);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -120,6 +123,6 @@
|
||||
<div id="map" class="rounded-xl z-0 basis-full"></div>
|
||||
<div class="flex items-center justify-between">
|
||||
<slot />
|
||||
<div class="basis-full min-w-[300px]" id="elevation"></div>
|
||||
<div class="basis-[300px] flex-grow flex-shrink-0" id="elevation"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{ text: $_("print"), value: "print", icon: "print" },
|
||||
{ text: $_("add-to-list"), value: "list", icon: "bookmark" },
|
||||
{ text: $_("edit"), value: "edit", icon: "pen" },
|
||||
{ text: $_("delete"), value: "delete", icon: "trash" },
|
||||
@@ -61,6 +62,8 @@
|
||||
"_blank",
|
||||
)
|
||||
?.focus();
|
||||
} else if (item.value == "print") {
|
||||
goto(`/map/trail/${trail.id}/print`);
|
||||
} else if (item.value == "download") {
|
||||
downloadURI(getFileURL(trail, trail.gpx), trail.gpx!);
|
||||
} else if (item.value == "edit") {
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"entry": "Eintrag",
|
||||
"error-creating-user": "Fehler beim Erstellen des Nutzers",
|
||||
"error-during-login": "Fehler beim Login",
|
||||
"error-printing-map": "",
|
||||
"error-reading-file": "Fehler beim Lesen der Datei",
|
||||
"est-duration": "Gesch. Dauer",
|
||||
"explore": "Erkunden",
|
||||
@@ -59,6 +60,7 @@
|
||||
"features": "Features",
|
||||
"french": "Französisch",
|
||||
"german": "Deutsch",
|
||||
"grid": "",
|
||||
"hero_section_0_text": "Entdecke spannende Routen, speichere deine Favoriten und erlebe die Schönheit der Natur. Finde dein nächstes Abenteuer!",
|
||||
"hero_section_1_heading": "Hier gibt es noch keine Routen",
|
||||
"hero_section_1_text": "Hier sind einige Routen, die dir gefallen könnten. Oder du wirfst einen Blick auf die vollständige Liste.",
|
||||
@@ -67,6 +69,7 @@
|
||||
"hungarian": "Ungarisch",
|
||||
"icon": "Icon",
|
||||
"imperial": "Amerikanisch",
|
||||
"include-description": "",
|
||||
"invalid-date": "Ungültiges Datum",
|
||||
"invalid-username": "Ungültiger Nutzername",
|
||||
"language": "Sprache",
|
||||
@@ -99,6 +102,8 @@
|
||||
"not-a-valid-email-address": "Keine gültige Email-Adresse",
|
||||
"not-completed": "Nicht abgeschlossen",
|
||||
"or": "oder",
|
||||
"orientation": "",
|
||||
"paper-size": "",
|
||||
"password": "Passwort",
|
||||
"photos": "Fotos",
|
||||
"pick-a-trail": "Route auswählen",
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"entry": "Entry",
|
||||
"error-creating-user": "Error creating user",
|
||||
"error-during-login": "Error during login",
|
||||
"error-printing-map": "Error printing map",
|
||||
"error-reading-file": "Error reading file",
|
||||
"est-duration": "Est. duration",
|
||||
"explore": "Explore",
|
||||
@@ -59,6 +60,7 @@
|
||||
"features": "Features",
|
||||
"french": "French",
|
||||
"german": "German",
|
||||
"grid": "Grid",
|
||||
"hero_section_0_text": "Explore exciting trails, save your favorites, and experience the beauty of nature. Find your next adventure!",
|
||||
"hero_section_1_heading": "It seems there are no trails here yet.",
|
||||
"hero_section_1_text": "Here are some trails you might like. Or you can just go to the full list right now.",
|
||||
@@ -67,6 +69,7 @@
|
||||
"hungarian": "Hungarian",
|
||||
"icon": "Icon",
|
||||
"imperial": "Imperial",
|
||||
"include-description": "Include description",
|
||||
"invalid-date": "Invalid Date",
|
||||
"invalid-username": "Invalid username",
|
||||
"language": "Language",
|
||||
@@ -99,6 +102,8 @@
|
||||
"not-a-valid-email-address": "Not a valid email address",
|
||||
"not-completed": "Not completed",
|
||||
"or": "or",
|
||||
"orientation": "Orientation",
|
||||
"paper-size": "Paper size",
|
||||
"password": "Password",
|
||||
"photos": "Photos",
|
||||
"pick-a-trail": "Pick a trail",
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"entry": "Entrée",
|
||||
"error-creating-user": "Erreur durant la création de l'utilisateur",
|
||||
"error-during-login": "Erreur durant la connexion",
|
||||
"error-printing-map": "",
|
||||
"error-reading-file": "Erreur de lecture du fichier",
|
||||
"est-duration": "Temps estimé",
|
||||
"explore": "Explorer",
|
||||
@@ -59,6 +60,7 @@
|
||||
"features": "Fonctionnalités",
|
||||
"french": "Français",
|
||||
"german": "Allemand",
|
||||
"grid": "",
|
||||
"hero_section_0_text": "Explorez des itinéraires passionnants, sauvegardez vos favoris et découvrez la beauté de la nature. Trouvez votre prochaine aventure !",
|
||||
"hero_section_1_heading": "Il semble qu'il n'y ait pas encore d'itinéraire ici.",
|
||||
"hero_section_1_text": "Voici quelques itinéraires qui pourraient vous plaire. Vous pouvez également consulter la liste complète dès maintenant.",
|
||||
@@ -67,6 +69,7 @@
|
||||
"hungarian": "Hongrois",
|
||||
"icon": "Icône",
|
||||
"imperial": "Impérial",
|
||||
"include-description": "",
|
||||
"invalid-date": "Date invalide",
|
||||
"invalid-username": "Nom d'utilisateur invalide",
|
||||
"language": "Langue",
|
||||
@@ -99,6 +102,8 @@
|
||||
"not-a-valid-email-address": "Adresse email invalide",
|
||||
"not-completed": "Pas terminé",
|
||||
"or": "ou",
|
||||
"orientation": "",
|
||||
"paper-size": "",
|
||||
"password": "Mot de passe",
|
||||
"photos": "Photos",
|
||||
"pick-a-trail": "Choisir un itinéraire",
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"entry": "Bejegyzés",
|
||||
"error-creating-user": "Hiba felhasználó hozzáadása közben",
|
||||
"error-during-login": "Hiba bejelentkezés közben",
|
||||
"error-printing-map": "",
|
||||
"error-reading-file": "Hiba a fájl olvasása közben",
|
||||
"est-duration": "Becsült időtartam",
|
||||
"explore": "Felfedezés",
|
||||
@@ -59,6 +60,7 @@
|
||||
"features": "Jellemzők",
|
||||
"french": "Francia",
|
||||
"german": "Német",
|
||||
"grid": "",
|
||||
"hero_section_0_text": "Fedezze fel az izgalmas útvonalakat, mentse el kedvenceit, és tapasztalja meg a természet szépségét. Találja meg a következő kalandját!",
|
||||
"hero_section_1_heading": "Úgy tűnik, itt még nincsenek ösvények.",
|
||||
"hero_section_1_text": "Íme néhány nyomvonal, amely tetszeni fog. Vagy csak menj a teljes listára most rögtön.",
|
||||
@@ -67,6 +69,7 @@
|
||||
"hungarian": "Magyar",
|
||||
"icon": "Ikon",
|
||||
"imperial": "Angolszász",
|
||||
"include-description": "",
|
||||
"invalid-date": "Érvénytelen dátum",
|
||||
"invalid-username": "Érvénytelen felhasználó",
|
||||
"language": "Nyelf",
|
||||
@@ -99,6 +102,8 @@
|
||||
"not-a-valid-email-address": "Érvénytelen e-mail cím",
|
||||
"not-completed": "Nem teljesített",
|
||||
"or": "vagy",
|
||||
"orientation": "",
|
||||
"paper-size": "",
|
||||
"password": "Jelszó",
|
||||
"photos": "Képek",
|
||||
"pick-a-trail": "Válasszon útvonalat",
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"entry": "Item",
|
||||
"error-creating-user": "Het account kan niet worden aangemaakt",
|
||||
"error-during-login": "Het inloggen is mislukt",
|
||||
"error-printing-map": "",
|
||||
"error-reading-file": "Het bestand kan niet worden ingelezen",
|
||||
"est-duration": "Geschatte duur",
|
||||
"explore": "Verkennen",
|
||||
@@ -59,6 +60,7 @@
|
||||
"features": "Kenmerken",
|
||||
"french": "Frans",
|
||||
"german": "Duits",
|
||||
"grid": "",
|
||||
"hero_section_0_text": "Verken uitdagende wandelroutes, bewaar je favoriete en ervaar de pracht van de natuur. Op naar het volgende avontuur!",
|
||||
"hero_section_1_heading": "Je hebt nog geen wandelroutes.",
|
||||
"hero_section_1_text": "Wellicht vind je deze routes interessant. Ook kun je de volledige lijst bekijken.",
|
||||
@@ -67,6 +69,7 @@
|
||||
"hungarian": "Hongaars",
|
||||
"icon": "Pictogram",
|
||||
"imperial": "Imperiaal",
|
||||
"include-description": "",
|
||||
"invalid-date": "Ongeldige datum",
|
||||
"invalid-username": "Ongeldige gebruikersnaam",
|
||||
"language": "Taal",
|
||||
@@ -99,6 +102,8 @@
|
||||
"not-a-valid-email-address": "Het e-mailadres is ongeldig",
|
||||
"not-completed": "Niet voltooid",
|
||||
"or": "of",
|
||||
"orientation": "",
|
||||
"paper-size": "",
|
||||
"password": "Wachtwoord",
|
||||
"photos": "Foto's",
|
||||
"pick-a-trail": "Kies een wandelroute",
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"entry": "Pozycja",
|
||||
"error-creating-user": "Błąd tworzenia użytkownika",
|
||||
"error-during-login": "Błąd podczas logowania",
|
||||
"error-printing-map": "",
|
||||
"error-reading-file": "Błąd wczytywania pliku",
|
||||
"est-duration": "Szacowany czas",
|
||||
"explore": "Eksploruj",
|
||||
@@ -59,6 +60,7 @@
|
||||
"features": "Funkcje",
|
||||
"french": "Francuski",
|
||||
"german": "Niemiecki",
|
||||
"grid": "",
|
||||
"hero_section_0_text": "Eksploruj ekscytujące szlaki, zapisz swoje ulubione, doświadcz piękna naturalnego świata. Znajdź swoją następną przygodę!",
|
||||
"hero_section_1_heading": "Wygląda na to że nie ma tutaj ścieżek.",
|
||||
"hero_section_1_text": "Oto kila ścieżek które możesz zobaczyć lub możesz przejść do pełnej listy.",
|
||||
@@ -67,6 +69,7 @@
|
||||
"hungarian": "Język węgierski",
|
||||
"icon": "Ikona",
|
||||
"imperial": "Anglosaskie",
|
||||
"include-description": "",
|
||||
"invalid-date": "Nieprawidłowa data",
|
||||
"invalid-username": "Błędna nazwa użytkownika",
|
||||
"language": "Język",
|
||||
@@ -99,6 +102,8 @@
|
||||
"not-a-valid-email-address": "Nieprawidłowy adres email",
|
||||
"not-completed": "Nie dokończono",
|
||||
"or": "lub",
|
||||
"orientation": "",
|
||||
"paper-size": "",
|
||||
"password": "Hasło",
|
||||
"photos": "Zdjęcia",
|
||||
"pick-a-trail": "Wybierz ścieżkę",
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"entry": "Entrada",
|
||||
"error-creating-user": "Erro ao criar usuário",
|
||||
"error-during-login": "Erro durante o login",
|
||||
"error-printing-map": "",
|
||||
"error-reading-file": "Erro ao ler o arquivo",
|
||||
"est-duration": "Duração prevista",
|
||||
"explore": "Explorar",
|
||||
@@ -59,6 +60,7 @@
|
||||
"features": "Características",
|
||||
"french": "Francês",
|
||||
"german": "Alemão",
|
||||
"grid": "",
|
||||
"hero_section_0_text": "Explore trilhas emocionantes, salve seus favoritos e experimente a beleza da natureza. Encontre sua próxima aventura!",
|
||||
"hero_section_1_heading": "Parece que ainda não há trilhas aqui.",
|
||||
"hero_section_1_text": "Aqui estão algumas trilhas que você pode gostar. Ou podes ir à lista completa agora.",
|
||||
@@ -67,6 +69,7 @@
|
||||
"hungarian": "Húngaro",
|
||||
"icon": "Ícone",
|
||||
"imperial": "Imperial",
|
||||
"include-description": "",
|
||||
"invalid-date": "Data inválida",
|
||||
"invalid-username": "Nome de usuário inválido",
|
||||
"language": "Língua",
|
||||
@@ -99,6 +102,8 @@
|
||||
"not-a-valid-email-address": "Não um endereço de e-mail válido",
|
||||
"not-completed": "Não preenchido",
|
||||
"or": "ou",
|
||||
"orientation": "",
|
||||
"paper-size": "",
|
||||
"password": "Senha",
|
||||
"photos": "Fotos",
|
||||
"pick-a-trail": "Escolha uma trilha",
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"entry": "日程",
|
||||
"error-creating-user": "创建用户错误",
|
||||
"error-during-login": "登录错误",
|
||||
"error-printing-map": "",
|
||||
"error-reading-file": "读取文件错误",
|
||||
"est-duration": "预计时长",
|
||||
"explore": "探索",
|
||||
@@ -59,6 +60,7 @@
|
||||
"features": "特性",
|
||||
"french": "法语",
|
||||
"german": "德语",
|
||||
"grid": "",
|
||||
"hero_section_0_text": "探索激动人心的探险,收藏最爱行程,,体验美丽地大自然。找到你的下一次冒险!",
|
||||
"hero_section_1_heading": "这儿似乎还没有行程。",
|
||||
"hero_section_1_text": "以下有你可能喜欢的线路,或者立刻浏览全部列表。",
|
||||
@@ -67,6 +69,7 @@
|
||||
"hungarian": "匈牙利语",
|
||||
"icon": "图标",
|
||||
"imperial": "英制",
|
||||
"include-description": "",
|
||||
"invalid-date": "无效日期",
|
||||
"invalid-username": "无效用户名",
|
||||
"language": "语言",
|
||||
@@ -99,6 +102,8 @@
|
||||
"not-a-valid-email-address": "无效电子邮箱地址",
|
||||
"not-completed": "未完成",
|
||||
"or": "或",
|
||||
"orientation": "",
|
||||
"paper-size": "",
|
||||
"password": "密码",
|
||||
"photos": "图片",
|
||||
"pick-a-trail": "选择一个路线",
|
||||
|
||||
@@ -4,6 +4,7 @@ export function createRect(
|
||||
width: number,
|
||||
height: number,
|
||||
fill: string,
|
||||
border?: string,
|
||||
) {
|
||||
const svgns = "http://www.w3.org/2000/svg";
|
||||
|
||||
@@ -12,7 +13,7 @@ export function createRect(
|
||||
rect.setAttribute("y", y.toString());
|
||||
rect.setAttribute("width", width.toString());
|
||||
rect.setAttribute("height", height.toString());
|
||||
rect.setAttribute("fill", fill);
|
||||
rect.setAttribute("style", `fill:${fill};stroke-width:1;stroke:${border ?? ''}`);
|
||||
return rect;
|
||||
}
|
||||
|
||||
|
||||
@@ -327,7 +327,7 @@
|
||||
--ele-stroke: #7b7b7b;
|
||||
--ele-circle: #fff;
|
||||
--ele-line: #000;
|
||||
--ele-grid: rgba(var(--input-border));
|
||||
--ele-grid: #d1d5db;
|
||||
}
|
||||
|
||||
.elevation-detached.lightblue-theme .area {
|
||||
|
||||
@@ -215,7 +215,7 @@ export default function leafletImage(map, callback) {
|
||||
im.onload = function () {
|
||||
if (marker._icon.classList.contains("leaflet-grid-label")) {
|
||||
const label = marker._icon.children[0].textContent;
|
||||
ctx.font = '600 8px "Font Awesome 6 Free"';
|
||||
ctx.font = '600 10px "IBMPlexSans"';
|
||||
ctx.fillStyle = "#000";
|
||||
ctx.fillText(label, x + 4, y + 12);
|
||||
} else {
|
||||
@@ -224,14 +224,16 @@ export default function leafletImage(map, callback) {
|
||||
ctx.drawImage(this, x, y, size[0], size[1]);
|
||||
ctx.font = '600 14px "Font Awesome 6 Free"';
|
||||
ctx.fillStyle = "#ffffff";
|
||||
ctx.fillText(icon, x + 10.2, y + 24);
|
||||
const iconWidth = ctx.measureText(icon).width
|
||||
ctx.fillText(icon, x + size[0] / 2 - iconWidth / 2, y + 24);
|
||||
if (waypointName) {
|
||||
ctx.fillStyle = "#313131"
|
||||
ctx.roundRect(x - 8, y - 18, waypointName.length * 7, 20, 5)
|
||||
ctx.fill();
|
||||
ctx.fillStyle = "rgba(49, 49, 49, 0.5)"
|
||||
ctx.font = '600 12px "IBMPlexSans"';
|
||||
const textWidth = ctx.measureText(waypointName).width
|
||||
ctx.roundRect(size[0] / 2 + x - (textWidth + 16) / 2, y - 18, textWidth + 16, 20, 5)
|
||||
ctx.fill();
|
||||
ctx.fillStyle = "#ffffff";
|
||||
ctx.fillText(waypointName, x, y - 4);
|
||||
ctx.fillText(waypointName, size[0] / 2 + x - (textWidth + 16) / 2 + 8, y - 4);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { page } from "$app/stores";
|
||||
import "$lib/assets/fonts/IBMPlexSans-Regular-normal";
|
||||
import "$lib/assets/fonts/IBMPlexSans-SemiBold-bold";
|
||||
import "$lib/assets/fonts/fa-solid-900-normal";
|
||||
import Button from "$lib/components/base/button.svelte";
|
||||
import Select from "$lib/components/base/select.svelte";
|
||||
import Select, {
|
||||
type SelectItem,
|
||||
} from "$lib/components/base/select.svelte";
|
||||
import LogoText from "$lib/components/logo/logo_text.svelte";
|
||||
import MapWithElevation from "$lib/components/trail/map_with_elevation.svelte";
|
||||
import { trail } from "$lib/stores/trail_store";
|
||||
@@ -18,14 +23,19 @@
|
||||
import { createRect, createText } from "$lib/util/svg_util";
|
||||
import "$lib/vendor/leaflet-elevation/src/index.css";
|
||||
import leafletImage from "$lib/vendor/leaflet-image/leaflet-image.js";
|
||||
import { jsPDF } from "jspdf";
|
||||
import type { Map } from "leaflet";
|
||||
import "leaflet.awesome-markers/dist/leaflet.awesome-markers.css";
|
||||
import "leaflet/dist/leaflet.css";
|
||||
import QrCodeWithLogo from "qrcode-with-logos";
|
||||
import { onMount, tick } from "svelte";
|
||||
import { _ } from "svelte-i18n";
|
||||
import { Canvg } from "canvg";
|
||||
import type AutoGraticule from "$lib/vendor/leaflet-graticule/leaflet-auto-graticule";
|
||||
import { show_toast } from "$lib/stores/toast_store";
|
||||
|
||||
let map: Map;
|
||||
let graticule: AutoGraticule;
|
||||
|
||||
const paperSizes: { text: string; value: keyof typeof paperDimensions }[] =
|
||||
[
|
||||
@@ -34,13 +44,25 @@
|
||||
];
|
||||
const paperDimensions = {
|
||||
a4: {
|
||||
css: {
|
||||
width: "21cm",
|
||||
height: "29.7cm",
|
||||
},
|
||||
pdf: {
|
||||
width: 210,
|
||||
height: 297,
|
||||
},
|
||||
},
|
||||
letter: {
|
||||
css: {
|
||||
width: "21.59cm",
|
||||
height: "27.94cm",
|
||||
},
|
||||
pdf: {
|
||||
width: 215.9,
|
||||
height: 279.4,
|
||||
},
|
||||
},
|
||||
};
|
||||
let selectedPaperSize = paperSizes[0].value;
|
||||
|
||||
@@ -50,7 +72,17 @@
|
||||
];
|
||||
let selectedOrientation: "portrait" | "landscape" = orientations[0].value;
|
||||
|
||||
let scale = 1;
|
||||
const gridOptions = [
|
||||
{ text: "Degrees", value: "degree" },
|
||||
{ text: "No Grid", value: "off" },
|
||||
];
|
||||
let selectedGrid = gridOptions[0].value;
|
||||
|
||||
let scale: number = 1;
|
||||
|
||||
let printLoading: boolean = false;
|
||||
|
||||
let includeDescription: boolean = false;
|
||||
|
||||
onMount(() => {
|
||||
let qrcode = new QrCodeWithLogo({
|
||||
@@ -63,16 +95,262 @@
|
||||
});
|
||||
|
||||
function print() {
|
||||
leafletImage(map, function (err: string, canvas: HTMLCanvasElement) {
|
||||
printLoading = true;
|
||||
const doc = new jsPDF(selectedOrientation, "mm", [
|
||||
paperDimensions[selectedPaperSize].pdf[
|
||||
selectedOrientation == "portrait" ? "width" : "height"
|
||||
],
|
||||
paperDimensions[selectedPaperSize].pdf[
|
||||
selectedOrientation == "portrait" ? "height" : "width"
|
||||
],
|
||||
]);
|
||||
doc.setFont("IBMPlexSans-Regular", "normal");
|
||||
var width = doc.internal.pageSize.getWidth();
|
||||
var height = doc.internal.pageSize.getHeight();
|
||||
let currentHeight = 0;
|
||||
const rulerElement = document.getElementById("ruler") as HTMLElement;
|
||||
const rulerSegments = rulerElement.getElementsByTagName("rect");
|
||||
const rulerLabels = rulerElement.getElementsByTagName("text");
|
||||
|
||||
const pageWidth = document
|
||||
.getElementById("paper")
|
||||
?.getBoundingClientRect().width;
|
||||
|
||||
try {
|
||||
leafletImage(
|
||||
map,
|
||||
async function (err: string, canvas: HTMLCanvasElement) {
|
||||
// Map
|
||||
var img = document.createElement("img");
|
||||
var dimensions = map.getSize();
|
||||
img.width = dimensions.x;
|
||||
img.height = dimensions.y;
|
||||
const ratio = img.height / img.width;
|
||||
img.src = canvas.toDataURL();
|
||||
doc.addImage(img.src, "png", 8, 8, 0, (width - 16) * ratio);
|
||||
currentHeight += (width - 16) * ratio + 11;
|
||||
const elevationProfileHeight = currentHeight - 4;
|
||||
|
||||
document.getElementById("images")!.innerHTML = "";
|
||||
document.getElementById("images")!.appendChild(img);
|
||||
// QR Code
|
||||
doc.addImage(
|
||||
document.getElementById("qrcode") as HTMLImageElement,
|
||||
"png",
|
||||
8,
|
||||
currentHeight,
|
||||
24,
|
||||
24,
|
||||
);
|
||||
|
||||
// Distance
|
||||
currentHeight += 5;
|
||||
let currentWidth = 8 + 24 + 4;
|
||||
doc.setFontSize(9);
|
||||
doc.setTextColor(107, 114, 128);
|
||||
doc.setFont("fa-solid-900", "normal");
|
||||
doc.text("\uf337", currentWidth, currentHeight);
|
||||
currentWidth += doc.getTextWidth("\uf337") + 3;
|
||||
doc.setFont("IBMPlexSans-Regular", "normal");
|
||||
doc.text(
|
||||
formatDistance($trail.distance),
|
||||
currentWidth,
|
||||
currentHeight,
|
||||
);
|
||||
currentWidth +=
|
||||
doc.getTextWidth(formatDistance($trail.distance)) + 4;
|
||||
|
||||
// Elevation gain
|
||||
doc.setFont("fa-solid-900", "normal");
|
||||
doc.text("\uf338", currentWidth, currentHeight);
|
||||
currentWidth += doc.getTextWidth("\uf338") + 3;
|
||||
doc.setFont("IBMPlexSans-Regular", "normal");
|
||||
doc.text(
|
||||
formatElevation($trail.elevation_gain),
|
||||
currentWidth,
|
||||
currentHeight,
|
||||
);
|
||||
currentWidth +=
|
||||
doc.getTextWidth(
|
||||
formatDistance($trail.elevation_gain),
|
||||
) + 4;
|
||||
|
||||
// Duration
|
||||
doc.setFont("fa-solid-900", "normal");
|
||||
doc.text("\uf017", currentWidth, currentHeight);
|
||||
currentWidth += doc.getTextWidth("\uf337") + 3;
|
||||
doc.setFont("IBMPlexSans-Regular", "normal");
|
||||
doc.text(
|
||||
formatTimeHHMM($trail.duration),
|
||||
currentWidth,
|
||||
currentHeight,
|
||||
);
|
||||
|
||||
// Ruler
|
||||
currentHeight += 6;
|
||||
currentWidth = 8 + 24 + 4;
|
||||
doc.setTextColor(0, 0, 0);
|
||||
for (let i = 0; i < rulerSegments.length; i++) {
|
||||
const segment = rulerSegments[i];
|
||||
const label = rulerLabels[i].textContent ?? "";
|
||||
const segmentWidth =
|
||||
segment.getBoundingClientRect().width;
|
||||
const segmentWidthPercent =
|
||||
segmentWidth / (pageWidth ?? 0);
|
||||
const segmentPDFWidth = width * segmentWidthPercent;
|
||||
let textWidth = doc.getTextWidth(label);
|
||||
|
||||
if (i % 2 == 0) {
|
||||
doc.setFillColor(255, 255, 255);
|
||||
} else {
|
||||
doc.setFillColor(93, 93, 93);
|
||||
}
|
||||
|
||||
doc.rect(
|
||||
currentWidth,
|
||||
currentHeight,
|
||||
segmentPDFWidth,
|
||||
2,
|
||||
"FD",
|
||||
);
|
||||
doc.text(
|
||||
label,
|
||||
currentWidth - textWidth / 2,
|
||||
currentHeight + 6,
|
||||
);
|
||||
currentWidth += segmentPDFWidth;
|
||||
if (i == rulerSegments.length - 1) {
|
||||
const endLabel =
|
||||
rulerLabels[i + 1].textContent ?? "";
|
||||
textWidth = doc.getTextWidth(endLabel);
|
||||
doc.text(
|
||||
endLabel,
|
||||
currentWidth - textWidth / 2,
|
||||
currentHeight + 6,
|
||||
);
|
||||
textWidth = doc.getTextWidth("KM");
|
||||
doc.text(
|
||||
"KM",
|
||||
currentWidth - textWidth,
|
||||
currentHeight - 2,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Scale
|
||||
currentHeight += 12;
|
||||
doc.setFillColor(255, 255, 255);
|
||||
doc.text(
|
||||
`Scale: 1 : ${Math.round(scale)}`,
|
||||
8 + 24 + 4,
|
||||
currentHeight,
|
||||
);
|
||||
|
||||
// Elevation profile
|
||||
const gridLines = document.querySelectorAll(
|
||||
"#elevation .grid line",
|
||||
);
|
||||
gridLines.forEach((l) =>
|
||||
l.setAttribute("stroke", "#d1d5db"),
|
||||
);
|
||||
const gridBorders = document.querySelectorAll(
|
||||
"#elevation .grid path",
|
||||
);
|
||||
gridBorders.forEach((l) =>
|
||||
l.setAttribute("stroke", "#fff"),
|
||||
);
|
||||
const tooltip = document.querySelector(
|
||||
"#elevation .tooltip",
|
||||
) as HTMLElement;
|
||||
tooltip.style.display = "none";
|
||||
const units = document.querySelectorAll(
|
||||
"#elevation .axis text",
|
||||
);
|
||||
units.forEach((l) => l.setAttribute("fill", "#000"));
|
||||
const elevationCanvas = document.createElement("canvas");
|
||||
const ctx = elevationCanvas.getContext("2d")!;
|
||||
const v = await Canvg.from(
|
||||
ctx,
|
||||
document.querySelector("#elevation svg")!.outerHTML,
|
||||
);
|
||||
v.start();
|
||||
const profileRatio =
|
||||
elevationCanvas.height / elevationCanvas.width;
|
||||
doc.addImage(
|
||||
elevationCanvas.toDataURL("image/png"),
|
||||
width - 79.3 - 8,
|
||||
elevationProfileHeight,
|
||||
79.3,
|
||||
79.3 * profileRatio,
|
||||
);
|
||||
const elevationPlot = (
|
||||
document.querySelector(
|
||||
".canvas-plot",
|
||||
) as HTMLCanvasElement
|
||||
).toDataURL("image/png");
|
||||
doc.addImage(
|
||||
elevationPlot,
|
||||
width - 77.3,
|
||||
elevationProfileHeight + 8,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
tooltip.style.display = "block";
|
||||
|
||||
// Separator
|
||||
currentHeight += 5;
|
||||
doc.setDrawColor(232, 234, 237);
|
||||
doc.line(8, currentHeight, width - 8, currentHeight);
|
||||
currentHeight += 6;
|
||||
|
||||
// Trail name & location
|
||||
doc.setTextColor(0, 0, 0);
|
||||
doc.setFont("IBMPlexSans-SemiBold", "bold");
|
||||
doc.text($trail.name, 8, currentHeight);
|
||||
doc.setFont("fa-solid-900", "normal");
|
||||
currentHeight += 5;
|
||||
doc.text("\uf3c5", 8, currentHeight);
|
||||
doc.setFont("IBMPlexSans-Regular", "normal");
|
||||
doc.text($trail.location || "-", 12, currentHeight);
|
||||
|
||||
// Logo
|
||||
const logo = new Image();
|
||||
logo.src = "/imgs/logo_text_dark.png";
|
||||
const logoRatio = 64 / 212;
|
||||
const logoWidth = 32;
|
||||
const logoHeight = logoWidth * logoRatio;
|
||||
doc.addImage(
|
||||
logo,
|
||||
width - 8 - logoWidth,
|
||||
height - 4 - logoHeight,
|
||||
logoWidth,
|
||||
logoHeight,
|
||||
);
|
||||
|
||||
if (includeDescription && $trail.description) {
|
||||
doc.addPage();
|
||||
doc.text($trail.description, 16, 16, {
|
||||
maxWidth: width - 32,
|
||||
});
|
||||
doc.addImage(
|
||||
logo,
|
||||
width - 8 - logoWidth,
|
||||
height - 4 - logoHeight,
|
||||
logoWidth,
|
||||
logoHeight,
|
||||
);
|
||||
}
|
||||
|
||||
doc.save($trail.name + ".pdf");
|
||||
printLoading = false;
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
show_toast({
|
||||
icon: "close",
|
||||
type: "error",
|
||||
text: $_("error-printing-map"),
|
||||
});
|
||||
printLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function updateMapSize() {
|
||||
@@ -107,8 +385,7 @@
|
||||
label: string,
|
||||
fill: string,
|
||||
) {
|
||||
const bar = createRect(x, height / 2, width - 4, 6, fill);
|
||||
const tick = createRect(x + width - 4, height / 2, 4, 6, "#000");
|
||||
const bar = createRect(x, height / 2, width, 6, fill, "#000");
|
||||
const text = createText(
|
||||
label,
|
||||
x + width - 5 * label.length,
|
||||
@@ -116,7 +393,6 @@
|
||||
"0.75rem",
|
||||
);
|
||||
svg.appendChild(bar);
|
||||
svg.appendChild(tick);
|
||||
svg.append(text);
|
||||
}
|
||||
let multiplier = 1;
|
||||
@@ -148,7 +424,7 @@
|
||||
oneUnitInPixels * i * multiplier,
|
||||
oneUnitInPixels * multiplier,
|
||||
`${multiplier < 1 ? ((i + 1) * multiplier).toFixed(1) : (i + 1) * multiplier}`,
|
||||
i % 2 == 0 ? "#7c7c7c" : "#5d5d5d",
|
||||
i % 2 == 0 ? "#fff" : "#5d5d5d",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -162,40 +438,72 @@
|
||||
|
||||
scale = calculateScaleFactor(map);
|
||||
}
|
||||
|
||||
function toggleGrid() {
|
||||
if (selectedGrid == "off") {
|
||||
graticule?.remove();
|
||||
} else {
|
||||
graticule.addTo(map);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{$_("print")} | wanderer</title>
|
||||
</svelte:head>
|
||||
|
||||
<main class="grid grid-cols-1 md:grid-cols-[400px_1fr] gap-x-1 gap-y-4 items-start">
|
||||
<div
|
||||
class="print-details px-6 space-y-4 pb-4 border border-input-border rounded-3xl"
|
||||
<main
|
||||
class="grid grid-cols-1 md:grid-cols-[400px_1fr] gap-x-1 gap-y-4 items-start"
|
||||
>
|
||||
<div
|
||||
class="print-details md:sticky w-80 md:top-8 mx-auto md:ml-8 p-6 flex flex-col gap-y-4 border border-input-border rounded-3xl"
|
||||
>
|
||||
<div id="images"></div>
|
||||
<h1 class="text-4xl font-bold">Print Trail</h1>
|
||||
<Select
|
||||
bind:value={selectedPaperSize}
|
||||
items={paperSizes}
|
||||
label="Paper size"
|
||||
label={$_("paper-size")}
|
||||
on:change={updateMapSize}
|
||||
></Select>
|
||||
<Select
|
||||
bind:value={selectedOrientation}
|
||||
items={orientations}
|
||||
label="Orientation"
|
||||
label={$_("orientation")}
|
||||
on:change={updateMapSize}
|
||||
></Select>
|
||||
<Select
|
||||
bind:value={selectedGrid}
|
||||
items={gridOptions}
|
||||
label={$_("grid")}
|
||||
on:change={toggleGrid}
|
||||
></Select>
|
||||
<div>
|
||||
<input
|
||||
id="description-checkbox"
|
||||
type="checkbox"
|
||||
bind:value={includeDescription}
|
||||
class="w-4 h-4 bg-input-background accent-primary border-input-border focus:ring-input-ring focus:ring-2"
|
||||
/>
|
||||
<label for="description-checkbox" class="ms-2 text-sm"
|
||||
>{$_("include-description")}</label
|
||||
>
|
||||
</div>
|
||||
|
||||
<Button primary={true} on:click={print}>Print!</Button>
|
||||
<Button
|
||||
loading={printLoading}
|
||||
extraClasses="mt-2"
|
||||
primary={true}
|
||||
on:click={print}>Print!</Button
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="paper-container overflow-scroll">
|
||||
<div
|
||||
class="paper flex flex-col mx-auto shadow-xl mb-8 p-8 bg-white"
|
||||
style="width: {paperDimensions[selectedPaperSize][
|
||||
id="paper"
|
||||
class="flex flex-col mx-auto shadow-xl mb-8 p-8 bg-white"
|
||||
style="width: {paperDimensions[selectedPaperSize].css[
|
||||
selectedOrientation == 'portrait' ? 'width' : 'height'
|
||||
]}; height: {paperDimensions[selectedPaperSize][
|
||||
]}; height: {paperDimensions[selectedPaperSize].css[
|
||||
selectedOrientation == 'portrait' ? 'height' : 'width'
|
||||
]}"
|
||||
>
|
||||
@@ -214,6 +522,7 @@
|
||||
}}
|
||||
on:zoomend={(e) => updateScale(e.detail)}
|
||||
bind:map
|
||||
bind:graticule
|
||||
>
|
||||
<img
|
||||
class="w-[100px] h-[100px]"
|
||||
@@ -239,7 +548,14 @@
|
||||
></i>{formatTimeHHMM($trail.duration)}</span
|
||||
>
|
||||
</div>
|
||||
<svg width="100%" height="50" id="ruler"> </svg>
|
||||
<svg
|
||||
width="100%"
|
||||
height="50"
|
||||
id="ruler"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
>
|
||||
</svg>
|
||||
<span class="text-sm"
|
||||
>Scale:   1 : {Math.round(scale)}</span
|
||||
>
|
||||
|
||||
BIN
web/static/imgs/logo_text_dark.png
Normal file
BIN
web/static/imgs/logo_text_dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
Reference in New Issue
Block a user