more work on print view
This commit is contained in:
309
web/package-lock.json
generated
309
web/package-lock.json
generated
@@ -23,6 +23,7 @@
|
|||||||
"nouislider": "^15.7.1",
|
"nouislider": "^15.7.1",
|
||||||
"photoswipe": "^5.4.3",
|
"photoswipe": "^5.4.3",
|
||||||
"pocketbase": "^0.21.0",
|
"pocketbase": "^0.21.0",
|
||||||
|
"qrcode-with-logos": "^1.0.5",
|
||||||
"svelte-i18n": "^4.0.0",
|
"svelte-i18n": "^4.0.0",
|
||||||
"three": "^0.161.0",
|
"three": "^0.161.0",
|
||||||
"yup": "^1.3.3"
|
"yup": "^1.3.3"
|
||||||
@@ -1463,6 +1464,14 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/camelcase": {
|
||||||
|
"version": "5.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||||
|
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/camelcase-css": {
|
"node_modules/camelcase-css": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
||||||
@@ -1564,6 +1573,80 @@
|
|||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cliui": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^4.2.0",
|
||||||
|
"strip-ansi": "^6.0.0",
|
||||||
|
"wrap-ansi": "^6.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cliui/node_modules/ansi-regex": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cliui/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==",
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cliui/node_modules/emoji-regex": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||||
|
},
|
||||||
|
"node_modules/cliui/node_modules/string-width": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
|
"dependencies": {
|
||||||
|
"emoji-regex": "^8.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
"strip-ansi": "^6.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cliui/node_modules/strip-ansi": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cliui/node_modules/wrap-ansi": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"string-width": "^4.1.0",
|
||||||
|
"strip-ansi": "^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/code-red": {
|
"node_modules/code-red": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz",
|
||||||
@@ -1580,7 +1663,6 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-name": "~1.1.4"
|
"color-name": "~1.1.4"
|
||||||
},
|
},
|
||||||
@@ -1591,8 +1673,7 @@
|
|||||||
"node_modules/color-name": {
|
"node_modules/color-name": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/combined-stream": {
|
"node_modules/combined-stream": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
@@ -1772,6 +1853,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/decamelize": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/decimal.js": {
|
"node_modules/decimal.js": {
|
||||||
"version": "10.4.3",
|
"version": "10.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
|
||||||
@@ -1842,6 +1931,11 @@
|
|||||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dijkstrajs": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="
|
||||||
|
},
|
||||||
"node_modules/dlv": {
|
"node_modules/dlv": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
|
||||||
@@ -1866,6 +1960,11 @@
|
|||||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/encode-utf8": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw=="
|
||||||
|
},
|
||||||
"node_modules/entities": {
|
"node_modules/entities": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||||
@@ -2093,6 +2192,18 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/find-up": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||||
|
"dependencies": {
|
||||||
|
"locate-path": "^5.0.0",
|
||||||
|
"path-exists": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/foreground-child": {
|
"node_modules/foreground-child": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
|
||||||
@@ -2161,6 +2272,14 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/get-caller-file": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||||
|
"engines": {
|
||||||
|
"node": "6.* || 8.* || >= 10.*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/get-func-name": {
|
"node_modules/get-func-name": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
|
||||||
@@ -2396,7 +2515,6 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
@@ -2625,6 +2743,17 @@
|
|||||||
"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
|
||||||
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="
|
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/locate-path": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||||
|
"dependencies": {
|
||||||
|
"p-locate": "^4.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/loupe": {
|
"node_modules/loupe": {
|
||||||
"version": "2.3.7",
|
"version": "2.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz",
|
||||||
@@ -3012,6 +3141,39 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/p-locate": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||||
|
"dependencies": {
|
||||||
|
"p-limit": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p-locate/node_modules/p-limit": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||||
|
"dependencies": {
|
||||||
|
"p-try": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p-try": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/parent-module": {
|
"node_modules/parent-module": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||||
@@ -3035,6 +3197,14 @@
|
|||||||
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"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==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/path-is-absolute": {
|
"node_modules/path-is-absolute": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
@@ -3182,6 +3352,14 @@
|
|||||||
"node": ">=16"
|
"node": ">=16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pngjs": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.13.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pocketbase": {
|
"node_modules/pocketbase": {
|
||||||
"version": "0.21.0",
|
"version": "0.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.21.0.tgz",
|
||||||
@@ -3364,6 +3542,31 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/qrcode": {
|
||||||
|
"version": "1.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.3.tgz",
|
||||||
|
"integrity": "sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==",
|
||||||
|
"dependencies": {
|
||||||
|
"dijkstrajs": "^1.0.1",
|
||||||
|
"encode-utf8": "^1.0.3",
|
||||||
|
"pngjs": "^5.0.0",
|
||||||
|
"yargs": "^15.3.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"qrcode": "bin/qrcode"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.13.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode-with-logos": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/qrcode-with-logos/-/qrcode-with-logos-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-ZHynBWdidxv57T5Lw8KBGpM6c/bwrL9buITPOrtX0PaoV0AgKkqHifdO+rKV7jMSiR/EcQWpvHI9ZarZonDq6g==",
|
||||||
|
"dependencies": {
|
||||||
|
"qrcode": "^1.4.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/querystringify": {
|
"node_modules/querystringify": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||||
@@ -3416,6 +3619,14 @@
|
|||||||
"node": ">=8.10.0"
|
"node": ">=8.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/require-directory": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/require-from-string": {
|
"node_modules/require-from-string": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||||
@@ -3424,6 +3635,11 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/require-main-filename": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
|
||||||
|
},
|
||||||
"node_modules/requires-port": {
|
"node_modules/requires-port": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
@@ -3574,6 +3790,11 @@
|
|||||||
"node": ">=v12.22.7"
|
"node": ">=v12.22.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/set-blocking": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
|
||||||
|
},
|
||||||
"node_modules/set-cookie-parser": {
|
"node_modules/set-cookie-parser": {
|
||||||
"version": "2.6.0",
|
"version": "2.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz",
|
||||||
@@ -4577,6 +4798,11 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/which-module": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="
|
||||||
|
},
|
||||||
"node_modules/why-is-node-running": {
|
"node_modules/why-is-node-running": {
|
||||||
"version": "2.2.2",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz",
|
||||||
@@ -4734,6 +4960,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
|
||||||
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
|
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/y18n": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
|
||||||
|
},
|
||||||
"node_modules/yaml": {
|
"node_modules/yaml": {
|
||||||
"version": "2.3.4",
|
"version": "2.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz",
|
||||||
@@ -4743,6 +4974,76 @@
|
|||||||
"node": ">= 14"
|
"node": ">= 14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/yargs": {
|
||||||
|
"version": "15.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
|
||||||
|
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
|
||||||
|
"dependencies": {
|
||||||
|
"cliui": "^6.0.0",
|
||||||
|
"decamelize": "^1.2.0",
|
||||||
|
"find-up": "^4.1.0",
|
||||||
|
"get-caller-file": "^2.0.1",
|
||||||
|
"require-directory": "^2.1.1",
|
||||||
|
"require-main-filename": "^2.0.0",
|
||||||
|
"set-blocking": "^2.0.0",
|
||||||
|
"string-width": "^4.2.0",
|
||||||
|
"which-module": "^2.0.0",
|
||||||
|
"y18n": "^4.0.0",
|
||||||
|
"yargs-parser": "^18.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yargs-parser": {
|
||||||
|
"version": "18.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
|
||||||
|
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"camelcase": "^5.0.0",
|
||||||
|
"decamelize": "^1.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yargs/node_modules/ansi-regex": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yargs/node_modules/emoji-regex": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||||
|
},
|
||||||
|
"node_modules/yargs/node_modules/string-width": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
|
"dependencies": {
|
||||||
|
"emoji-regex": "^8.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
"strip-ansi": "^6.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yargs/node_modules/strip-ansi": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yocto-queue": {
|
"node_modules/yocto-queue": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz",
|
||||||
|
|||||||
@@ -48,6 +48,7 @@
|
|||||||
"nouislider": "^15.7.1",
|
"nouislider": "^15.7.1",
|
||||||
"photoswipe": "^5.4.3",
|
"photoswipe": "^5.4.3",
|
||||||
"pocketbase": "^0.21.0",
|
"pocketbase": "^0.21.0",
|
||||||
|
"qrcode-with-logos": "^1.0.5",
|
||||||
"svelte-i18n": "^4.0.0",
|
"svelte-i18n": "^4.0.0",
|
||||||
"three": "^0.161.0",
|
"three": "^0.161.0",
|
||||||
"yup": "^1.3.3"
|
"yup": "^1.3.3"
|
||||||
|
|||||||
@@ -3,13 +3,16 @@
|
|||||||
import { currentUser } from "$lib/stores/user_store";
|
import { currentUser } from "$lib/stores/user_store";
|
||||||
import { createMarkerFromWaypoint } from "$lib/util/leaflet_util";
|
import { createMarkerFromWaypoint } from "$lib/util/leaflet_util";
|
||||||
import type { Map, Marker } from "leaflet";
|
import type { Map, Marker } from "leaflet";
|
||||||
import { onMount } from "svelte";
|
import { createEventDispatcher, onMount } from "svelte";
|
||||||
|
|
||||||
export let trail: Trail;
|
export let trail: Trail;
|
||||||
export let markers: Marker[] = [];
|
export let markers: Marker[] = [];
|
||||||
|
export let map: Map | null = null;
|
||||||
|
export let options = {};
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
let L: any;
|
let L: any;
|
||||||
let map: Map;
|
|
||||||
let controlElevation: any;
|
let controlElevation: any;
|
||||||
|
|
||||||
$: if (trail.expand.gpx_data && controlElevation) {
|
$: if (trail.expand.gpx_data && controlElevation) {
|
||||||
@@ -23,14 +26,21 @@
|
|||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
await import("$lib/vendor/leaflet-elevation/src/index.js");
|
await import("$lib/vendor/leaflet-elevation/src/index.js");
|
||||||
|
|
||||||
map = L.map("map").setView([trail.lat ?? 0, trail.lon ?? 0], 14);
|
map = L.map("map", { preferCanvas: true }).setView(
|
||||||
map.attributionControl.setPrefix(false)
|
[trail.lat ?? 0, trail.lon ?? 0],
|
||||||
|
14,
|
||||||
|
);
|
||||||
|
map!.attributionControl.setPrefix(false);
|
||||||
|
|
||||||
|
map!.on("zoomend", function () {
|
||||||
|
dispatch("zoomend", map);
|
||||||
|
});
|
||||||
|
|
||||||
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
||||||
attribution: "© OpenStreetMap contributors",
|
attribution: "© OpenStreetMap contributors",
|
||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
|
|
||||||
const elevation_options = {
|
const default_elevation_options = {
|
||||||
height: 200,
|
height: 200,
|
||||||
|
|
||||||
theme: "lightblue-theme",
|
theme: "lightblue-theme",
|
||||||
@@ -84,11 +94,16 @@
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const elevation_options = Object.assign(
|
||||||
|
default_elevation_options,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
controlElevation = L.control.elevation(elevation_options).addTo(map);
|
controlElevation = L.control.elevation(elevation_options).addTo(map);
|
||||||
|
|
||||||
for (const waypoint of trail.expand.waypoints) {
|
for (const waypoint of trail.expand.waypoints) {
|
||||||
const marker = createMarkerFromWaypoint(L, waypoint);
|
const marker = createMarkerFromWaypoint(L, waypoint);
|
||||||
marker.addTo(map);
|
marker.addTo(map!);
|
||||||
markers.push(marker);
|
markers.push(marker);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -96,16 +111,8 @@
|
|||||||
|
|
||||||
<div id="map-container" class="flex flex-col">
|
<div id="map-container" class="flex flex-col">
|
||||||
<div id="map" class="rounded-xl z-0 basis-full"></div>
|
<div id="map" class="rounded-xl z-0 basis-full"></div>
|
||||||
<div id="elevation"></div>
|
<div class="flex items-center justify-between">
|
||||||
|
<slot />
|
||||||
|
<div class="basis-full min-w-[300px]" id="elevation"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
|
||||||
#map-container {
|
|
||||||
height: calc(100vh - 180px);
|
|
||||||
}
|
|
||||||
@media only screen and (min-width: 768px) {
|
|
||||||
#map-container {
|
|
||||||
height: calc(100vh - 124px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { Waypoint } from "$lib/models/waypoint";
|
import type { Waypoint } from "$lib/models/waypoint";
|
||||||
import type { Icon, LeafletEvent, Marker } from "leaflet";
|
import type { Icon, LeafletEvent, Map, Marker } from "leaflet";
|
||||||
|
|
||||||
export function createMarkerFromWaypoint(L: any, waypoint: Waypoint, onDragEnd?: (event: LeafletEvent) => void): Marker {
|
export function createMarkerFromWaypoint(L: any, waypoint: Waypoint, onDragEnd?: (event: LeafletEvent) => void): Marker {
|
||||||
const fontAwesomeIcon = L.AwesomeMarkers.icon({
|
const fontAwesomeIcon = L.AwesomeMarkers.icon({
|
||||||
@@ -27,4 +27,43 @@ export function createMarkerFromWaypoint(L: any, waypoint: Waypoint, onDragEnd?:
|
|||||||
}
|
}
|
||||||
|
|
||||||
return marker;
|
return marker;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculatePixelPerMeter(map: Map, meters: number) {
|
||||||
|
const y = map.getSize().y;
|
||||||
|
const x = map.getSize().x;
|
||||||
|
const maxMeters = map.containerPointToLatLng([0, y]).distanceTo(map.containerPointToLatLng([x, y]));
|
||||||
|
const pixelPerMeter = x / maxMeters;
|
||||||
|
|
||||||
|
return pixelPerMeter * meters
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateScaleFactor(map: Map) {
|
||||||
|
function _pxTOmm() {
|
||||||
|
let heightRef = document.createElement('div');
|
||||||
|
heightRef.style.height = '1mm';
|
||||||
|
heightRef.style.position = "absolute";
|
||||||
|
heightRef.id = 'heightRef';
|
||||||
|
document.body.appendChild(heightRef);
|
||||||
|
|
||||||
|
const pxPermm = heightRef.getBoundingClientRect().height;
|
||||||
|
|
||||||
|
document.body.removeChild(heightRef);
|
||||||
|
|
||||||
|
return function pxTOmm(px: number) {
|
||||||
|
return px / pxPermm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var centerOfMap = map.getSize().y / 2;
|
||||||
|
|
||||||
|
var realWorlMetersPer100Pixels = map.distance(
|
||||||
|
map.containerPointToLatLng([0, centerOfMap]),
|
||||||
|
map.containerPointToLatLng([100, centerOfMap])
|
||||||
|
);
|
||||||
|
|
||||||
|
const screenMetersPer100Pixels = _pxTOmm()(100) / 1000;
|
||||||
|
|
||||||
|
const scaleFactor = realWorlMetersPer100Pixels / screenMetersPer100Pixels
|
||||||
|
|
||||||
|
return scaleFactor
|
||||||
}
|
}
|
||||||
29
web/src/lib/util/svg_util.ts
Normal file
29
web/src/lib/util/svg_util.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
export function createRect(
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
fill: string,
|
||||||
|
) {
|
||||||
|
const svgns = "http://www.w3.org/2000/svg";
|
||||||
|
|
||||||
|
const rect = document.createElementNS(svgns, "rect");
|
||||||
|
rect.setAttribute("x", x.toString());
|
||||||
|
rect.setAttribute("y", y.toString());
|
||||||
|
rect.setAttribute("width", width.toString());
|
||||||
|
rect.setAttribute("height", height.toString());
|
||||||
|
rect.setAttribute("fill", fill);
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createText(content: string, x: number,
|
||||||
|
y: number, fontSize?: string) {
|
||||||
|
const svgns = "http://www.w3.org/2000/svg";
|
||||||
|
|
||||||
|
const text = document.createElementNS(svgns, "text");
|
||||||
|
text.setAttribute("x", x.toString());
|
||||||
|
text.setAttribute("y", y.toString());
|
||||||
|
text.style.fontSize = fontSize ?? ""
|
||||||
|
text.textContent = content;
|
||||||
|
return text;
|
||||||
|
}
|
||||||
@@ -773,6 +773,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|||||||
this.track_info.name = geojson.name;
|
this.track_info.name = geojson.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
style.color = "#ff0000"
|
||||||
let layer = L.geoJson(geojson, {
|
let layer = L.geoJson(geojson, {
|
||||||
distanceMarkers: distanceMarkers,
|
distanceMarkers: distanceMarkers,
|
||||||
style: style,
|
style: style,
|
||||||
|
|||||||
@@ -319,6 +319,17 @@
|
|||||||
--ele-grid: rgba(var(--input-border));
|
--ele-grid: rgba(var(--input-border));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* GRAY THEME //////////////////////////////////////////////// */
|
||||||
|
.gray-theme {
|
||||||
|
--ele-bg: #ffffff;
|
||||||
|
--ele-area: #242734;
|
||||||
|
--ele-alpha: 0.45;
|
||||||
|
--ele-stroke: #7b7b7b;
|
||||||
|
--ele-circle: #fff;
|
||||||
|
--ele-line: #000;
|
||||||
|
--ele-grid: rgba(var(--input-border));
|
||||||
|
}
|
||||||
|
|
||||||
.elevation-detached.lightblue-theme .area {
|
.elevation-detached.lightblue-theme .area {
|
||||||
stroke: #3366CC;
|
stroke: #3366CC;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ export const Colors = {
|
|||||||
'purple' : { area: '#732C7B' },
|
'purple' : { area: '#732C7B' },
|
||||||
'steelblue': { area: '#4682B4' },
|
'steelblue': { area: '#4682B4' },
|
||||||
'red' : { area: '#F00' },
|
'red' : { area: '#F00' },
|
||||||
'lime' : { area: '#9CC222', line: '#566B13' }
|
'lime' : { area: '#9CC222', line: '#566B13' },
|
||||||
|
'gray': { area: '#000000', line: '#242734', alpha: 0.00001, stroke: '#000000' }
|
||||||
};
|
};
|
||||||
|
|
||||||
const SEC = 1000;
|
const SEC = 1000;
|
||||||
|
|||||||
261
web/src/lib/vendor/leaflet-image/leaflet-image.js
vendored
Normal file
261
web/src/lib/vendor/leaflet-image/leaflet-image.js
vendored
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
import queue from "./queue";
|
||||||
|
|
||||||
|
var cacheBusterDate = +new Date();
|
||||||
|
|
||||||
|
// leaflet-image
|
||||||
|
export default function leafletImage(map, callback) {
|
||||||
|
|
||||||
|
var dimensions = map.getSize(),
|
||||||
|
layerQueue = new queue(1);
|
||||||
|
|
||||||
|
var canvas = document.createElement('canvas');
|
||||||
|
canvas.width = dimensions.x;
|
||||||
|
canvas.height = dimensions.y;
|
||||||
|
var ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// dummy canvas image when loadTile get 404 error
|
||||||
|
// and layer don't have errorTileUrl
|
||||||
|
var dummycanvas = document.createElement('canvas');
|
||||||
|
dummycanvas.width = 1;
|
||||||
|
dummycanvas.height = 1;
|
||||||
|
var dummyctx = dummycanvas.getContext('2d');
|
||||||
|
dummyctx.fillStyle = 'rgba(0,0,0,0)';
|
||||||
|
dummyctx.fillRect(0, 0, 1, 1);
|
||||||
|
|
||||||
|
// layers are drawn in the same order as they are composed in the DOM:
|
||||||
|
// tiles, paths, and then markers
|
||||||
|
map.eachLayer(drawTileLayer);
|
||||||
|
map.eachLayer(drawEsriDynamicLayer);
|
||||||
|
|
||||||
|
if (map._pathRoot) {
|
||||||
|
layerQueue.defer(handlePathRoot, map._pathRoot);
|
||||||
|
} else if (map._panes) {
|
||||||
|
var firstCanvas = map._panes.overlayPane.getElementsByTagName('canvas').item(0);
|
||||||
|
if (firstCanvas) {
|
||||||
|
layerQueue.defer(handlePathRoot, firstCanvas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map.eachLayer(drawMarkerLayer);
|
||||||
|
layerQueue.awaitAll(layersDone);
|
||||||
|
|
||||||
|
function drawTileLayer(l) {
|
||||||
|
if (l instanceof L.TileLayer) layerQueue.defer(handleTileLayer, l);
|
||||||
|
else if (l._heat) layerQueue.defer(handlePathRoot, l._canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawMarkerLayer(l) {
|
||||||
|
if (l instanceof L.Marker && l.options.icon instanceof L.Icon) {
|
||||||
|
layerQueue.defer(handleMarkerLayer, l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawEsriDynamicLayer(l) {
|
||||||
|
if (!L.esri) return;
|
||||||
|
|
||||||
|
if (l instanceof L.esri.DynamicMapLayer) {
|
||||||
|
layerQueue.defer(handleEsriDymamicLayer, l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function done() {
|
||||||
|
callback(null, canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
function layersDone(err, layers) {
|
||||||
|
if (err) throw err;
|
||||||
|
layers.forEach(function (layer) {
|
||||||
|
if (layer && layer.canvas) {
|
||||||
|
ctx.drawImage(layer.canvas, 0, 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTileLayer(layer, callback) {
|
||||||
|
// `L.TileLayer.Canvas` was removed in leaflet 1.0
|
||||||
|
var isCanvasLayer = (L.TileLayer.Canvas && layer instanceof L.TileLayer.Canvas),
|
||||||
|
canvas = document.createElement('canvas');
|
||||||
|
|
||||||
|
canvas.width = dimensions.x;
|
||||||
|
canvas.height = dimensions.y;
|
||||||
|
|
||||||
|
var ctx = canvas.getContext('2d'),
|
||||||
|
bounds = map.getPixelBounds(),
|
||||||
|
zoom = map.getZoom(),
|
||||||
|
tileSize = layer.options.tileSize;
|
||||||
|
|
||||||
|
if (zoom > layer.options.maxZoom ||
|
||||||
|
zoom < layer.options.minZoom
|
||||||
|
) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
var tileBounds = L.bounds(
|
||||||
|
bounds.min.divideBy(tileSize)._floor(),
|
||||||
|
bounds.max.divideBy(tileSize)._floor()),
|
||||||
|
tiles = [],
|
||||||
|
j, i,
|
||||||
|
tileQueue = new queue(1);
|
||||||
|
|
||||||
|
for (j = tileBounds.min.y; j <= tileBounds.max.y; j++) {
|
||||||
|
for (i = tileBounds.min.x; i <= tileBounds.max.x; i++) {
|
||||||
|
tiles.push(new L.Point(i, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tiles.forEach(function (tilePoint) {
|
||||||
|
var originalTilePoint = tilePoint.clone();
|
||||||
|
|
||||||
|
if (layer._adjustTilePoint) {
|
||||||
|
layer._adjustTilePoint(tilePoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
var tilePos = originalTilePoint
|
||||||
|
.scaleBy(new L.Point(tileSize, tileSize))
|
||||||
|
.subtract(bounds.min);
|
||||||
|
|
||||||
|
if (tilePoint.y >= 0) {
|
||||||
|
if (isCanvasLayer) {
|
||||||
|
var tile = layer._tiles[tilePoint.x + ':' + tilePoint.y];
|
||||||
|
tileQueue.defer(canvasTile, tile, tilePos, tileSize);
|
||||||
|
} else {
|
||||||
|
var url = addCacheString(layer.getTileUrl(tilePoint));
|
||||||
|
tileQueue.defer(loadTile, url, tilePos, tileSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tileQueue.awaitAll(tileQueueFinish);
|
||||||
|
|
||||||
|
function canvasTile(tile, tilePos, tileSize, callback) {
|
||||||
|
callback(null, {
|
||||||
|
img: tile,
|
||||||
|
pos: tilePos,
|
||||||
|
size: tileSize
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadTile(url, tilePos, tileSize, callback) {
|
||||||
|
var im = new Image();
|
||||||
|
im.crossOrigin = '';
|
||||||
|
im.onload = function () {
|
||||||
|
callback(null, {
|
||||||
|
img: this,
|
||||||
|
pos: tilePos,
|
||||||
|
size: tileSize
|
||||||
|
});
|
||||||
|
};
|
||||||
|
im.onerror = function (e) {
|
||||||
|
// use canvas instead of errorTileUrl if errorTileUrl get 404
|
||||||
|
if (layer.options.errorTileUrl != '' && e.target.errorCheck === undefined) {
|
||||||
|
e.target.errorCheck = true;
|
||||||
|
e.target.src = layer.options.errorTileUrl;
|
||||||
|
} else {
|
||||||
|
callback(null, {
|
||||||
|
img: dummycanvas,
|
||||||
|
pos: tilePos,
|
||||||
|
size: tileSize
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
im.src = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tileQueueFinish(err, data) {
|
||||||
|
data.forEach(drawTile);
|
||||||
|
callback(null, { canvas: canvas });
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawTile(d) {
|
||||||
|
ctx.drawImage(d.img, Math.floor(d.pos.x), Math.floor(d.pos.y),
|
||||||
|
d.size, d.size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePathRoot(root, callback) {
|
||||||
|
var bounds = map.getPixelBounds(),
|
||||||
|
origin = map.getPixelOrigin(),
|
||||||
|
canvas = document.createElement('canvas');
|
||||||
|
canvas.width = dimensions.x;
|
||||||
|
canvas.height = dimensions.y;
|
||||||
|
var ctx = canvas.getContext('2d');
|
||||||
|
var pos = L.DomUtil.getPosition(root).subtract(bounds.min).add(origin);
|
||||||
|
try {
|
||||||
|
ctx.drawImage(root, pos.x, pos.y, canvas.width - (pos.x * 2), canvas.height - (pos.y * 2));
|
||||||
|
callback(null, {
|
||||||
|
canvas: canvas
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Element could not be drawn on canvas', root); // eslint-disable-line no-console
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMarkerLayer(marker, callback) {
|
||||||
|
var canvas = document.createElement('canvas'),
|
||||||
|
ctx = canvas.getContext('2d'),
|
||||||
|
pixelBounds = map.getPixelBounds(),
|
||||||
|
minPoint = new L.Point(pixelBounds.min.x, pixelBounds.min.y),
|
||||||
|
pixelPoint = map.project(marker.getLatLng()),
|
||||||
|
url = "/vendor/leaflet-image/marker.png",
|
||||||
|
icon = getComputedStyle(marker._icon.children[0], ':before').getPropertyValue('content').substring(1, 2) ?? "\uf111",
|
||||||
|
im = new Image(),
|
||||||
|
options = marker.options.icon.options,
|
||||||
|
size = options.iconSize,
|
||||||
|
pos = pixelPoint.subtract(minPoint),
|
||||||
|
anchor = L.point(options.iconAnchor || size && size.divideBy(2, true));
|
||||||
|
|
||||||
|
if (size instanceof L.Point) size = [size.x, size.y];
|
||||||
|
|
||||||
|
var x = Math.round(pos.x - size[0] + anchor.x),
|
||||||
|
y = Math.round(pos.y - anchor.y);
|
||||||
|
|
||||||
|
canvas.width = dimensions.x;
|
||||||
|
canvas.height = dimensions.y;
|
||||||
|
im.crossOrigin = '';
|
||||||
|
|
||||||
|
im.onload = function () {
|
||||||
|
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);
|
||||||
|
callback(null, {
|
||||||
|
canvas: canvas
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
im.src = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEsriDymamicLayer(dynamicLayer, callback) {
|
||||||
|
var canvas = document.createElement('canvas');
|
||||||
|
canvas.width = dimensions.x;
|
||||||
|
canvas.height = dimensions.y;
|
||||||
|
|
||||||
|
var ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
var im = new Image();
|
||||||
|
im.crossOrigin = '';
|
||||||
|
im.src = addCacheString(dynamicLayer._currentImage._image.src);
|
||||||
|
|
||||||
|
im.onload = function () {
|
||||||
|
ctx.drawImage(im, 0, 0);
|
||||||
|
callback(null, {
|
||||||
|
canvas: canvas
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function addCacheString(url) {
|
||||||
|
// If it's a data URL we don't want to touch this.
|
||||||
|
if (isDataURL(url)) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
return url + ((url.match(/\?/)) ? '&' : '?') + 'cache=' + cacheBusterDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDataURL(url) {
|
||||||
|
var dataURLRegex = /^\s*data:([a-z]+\/[a-z]+(;[a-z\-]+\=[a-z\-]+)?)?(;base64)?,[a-z0-9\!\$\&\'\,\(\)\*\+\,\;\=\-\.\_\~\:\@\/\?\%\s]*\s*$/i;
|
||||||
|
return !!url.match(dataURLRegex);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
120
web/src/lib/vendor/leaflet-image/queue.js
vendored
Normal file
120
web/src/lib/vendor/leaflet-image/queue.js
vendored
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
var noabort = {};
|
||||||
|
const slice = [].slice;
|
||||||
|
|
||||||
|
function Queue(size) {
|
||||||
|
this._size = size;
|
||||||
|
this._call =
|
||||||
|
this._error = null;
|
||||||
|
this._tasks = [];
|
||||||
|
this._data = [];
|
||||||
|
this._waiting =
|
||||||
|
this._active =
|
||||||
|
this._ended =
|
||||||
|
this._start = 0; // inside a synchronous task callback?
|
||||||
|
}
|
||||||
|
|
||||||
|
Queue.prototype = queue.prototype = {
|
||||||
|
constructor: Queue,
|
||||||
|
defer: function(callback) {
|
||||||
|
if (typeof callback !== "function") throw new Error("invalid callback");
|
||||||
|
if (this._call) throw new Error("defer after await");
|
||||||
|
if (this._error != null) return this;
|
||||||
|
var t = slice.call(arguments, 1);
|
||||||
|
t.push(callback);
|
||||||
|
++this._waiting, this._tasks.push(t);
|
||||||
|
poke(this);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
abort: function() {
|
||||||
|
if (this._error == null) abort(this, new Error("abort"));
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
await: function(callback) {
|
||||||
|
if (typeof callback !== "function") throw new Error("invalid callback");
|
||||||
|
if (this._call) throw new Error("multiple await");
|
||||||
|
this._call = function(error, results) { callback.apply(null, [error].concat(results)); };
|
||||||
|
maybeNotify(this);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
awaitAll: function(callback) {
|
||||||
|
if (typeof callback !== "function") throw new Error("invalid callback");
|
||||||
|
if (this._call) throw new Error("multiple await");
|
||||||
|
this._call = callback;
|
||||||
|
maybeNotify(this);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function poke(q) {
|
||||||
|
if (!q._start) {
|
||||||
|
try { start(q); } // let the current task complete
|
||||||
|
catch (e) {
|
||||||
|
if (q._tasks[q._ended + q._active - 1]) abort(q, e); // task errored synchronously
|
||||||
|
else if (!q._data) throw e; // await callback errored synchronously
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function start(q) {
|
||||||
|
while (q._start = q._waiting && q._active < q._size) {
|
||||||
|
var i = q._ended + q._active,
|
||||||
|
t = q._tasks[i],
|
||||||
|
j = t.length - 1,
|
||||||
|
c = t[j];
|
||||||
|
t[j] = end(q, i);
|
||||||
|
--q._waiting, ++q._active;
|
||||||
|
t = c.apply(null, t);
|
||||||
|
if (!q._tasks[i]) continue; // task finished synchronously
|
||||||
|
q._tasks[i] = t || noabort;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function end(q, i) {
|
||||||
|
return function(e, r) {
|
||||||
|
if (!q._tasks[i]) return; // ignore multiple callbacks
|
||||||
|
--q._active, ++q._ended;
|
||||||
|
q._tasks[i] = null;
|
||||||
|
if (q._error != null) return; // ignore secondary errors
|
||||||
|
if (e != null) {
|
||||||
|
abort(q, e);
|
||||||
|
} else {
|
||||||
|
q._data[i] = r;
|
||||||
|
if (q._waiting) poke(q);
|
||||||
|
else maybeNotify(q);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function abort(q, e) {
|
||||||
|
var i = q._tasks.length, t;
|
||||||
|
q._error = e; // ignore active callbacks
|
||||||
|
q._data = undefined; // allow gc
|
||||||
|
q._waiting = NaN; // prevent starting
|
||||||
|
|
||||||
|
while (--i >= 0) {
|
||||||
|
if (t = q._tasks[i]) {
|
||||||
|
q._tasks[i] = null;
|
||||||
|
if (t.abort) {
|
||||||
|
try { t.abort(); }
|
||||||
|
catch (e) { /* ignore */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
q._active = NaN; // allow notification
|
||||||
|
maybeNotify(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
function maybeNotify(q) {
|
||||||
|
if (!q._active && q._call) {
|
||||||
|
var d = q._data;
|
||||||
|
q._data = undefined; // allow gc
|
||||||
|
q._call(q._error, d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function queue(concurrency) {
|
||||||
|
if (concurrency == null) concurrency = Infinity;
|
||||||
|
else if (!((concurrency = +concurrency) >= 1)) throw new Error("invalid concurrency");
|
||||||
|
return new Queue(concurrency);
|
||||||
|
}
|
||||||
@@ -16,9 +16,7 @@
|
|||||||
<title>{$trail.name} | {$_("map")} | wanderer</title>
|
<title>{$trail.name} | {$_("map")} | wanderer</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
<main class="grid grid-cols-1 md:grid-cols-[458px_1fr] gap-x-1 gap-y-4">
|
<main class="grid grid-cols-1 md:grid-cols-[458px_1fr] gap-x-1 gap-y-4">
|
||||||
<div
|
<div id="trail-details">
|
||||||
id="trail-details"
|
|
||||||
>
|
|
||||||
<TrailInfoPanel trail={$trail} {markers}></TrailInfoPanel>
|
<TrailInfoPanel trail={$trail} {markers}></TrailInfoPanel>
|
||||||
</div>
|
</div>
|
||||||
<MapWithElevation trail={$trail} bind:markers></MapWithElevation>
|
<MapWithElevation trail={$trail} bind:markers></MapWithElevation>
|
||||||
|
|||||||
262
web/src/routes/map/trail/[id]/print/+page.svelte
Normal file
262
web/src/routes/map/trail/[id]/print/+page.svelte
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { page } from "$app/stores";
|
||||||
|
import Button from "$lib/components/base/button.svelte";
|
||||||
|
import Select from "$lib/components/base/select.svelte";
|
||||||
|
import MapWithElevation from "$lib/components/trail/map_with_elevation.svelte";
|
||||||
|
import { trail } from "$lib/stores/trail_store";
|
||||||
|
import {
|
||||||
|
calculatePixelPerMeter,
|
||||||
|
calculateScaleFactor,
|
||||||
|
} from "$lib/util/leaflet_util";
|
||||||
|
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 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 { currentUser } from "$lib/stores/user_store";
|
||||||
|
import {
|
||||||
|
formatDistance,
|
||||||
|
formatElevation,
|
||||||
|
formatTimeHHMM,
|
||||||
|
} from "$lib/util/format_util";
|
||||||
|
import LogoTextLight from "$lib/components/logo/logo_text_light.svelte";
|
||||||
|
import LogoText from "$lib/components/logo/logo_text.svelte";
|
||||||
|
|
||||||
|
let map: Map;
|
||||||
|
|
||||||
|
const paperSizes: { text: string; value: keyof typeof paperDimensions }[] =
|
||||||
|
[
|
||||||
|
{ text: "A4", value: "a4" },
|
||||||
|
{ text: "Letter", value: "letter" },
|
||||||
|
];
|
||||||
|
const paperDimensions = {
|
||||||
|
a4: {
|
||||||
|
width: "21cm",
|
||||||
|
height: "29.7cm",
|
||||||
|
},
|
||||||
|
letter: {
|
||||||
|
width: "21.59cm",
|
||||||
|
height: "27.94cm",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let selectedPaperSize = paperSizes[0].value;
|
||||||
|
|
||||||
|
const orientations: { text: string; value: "portrait" | "landscape" }[] = [
|
||||||
|
{ text: "Portrait", value: "portrait" },
|
||||||
|
{ text: "Landscape", value: "landscape" },
|
||||||
|
];
|
||||||
|
let selectedOrientation: "portrait" | "landscape" = orientations[0].value;
|
||||||
|
|
||||||
|
let scale = 1;
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
let qrcode = new QrCodeWithLogo({
|
||||||
|
content: $page.url.href.replace("/print", ""),
|
||||||
|
image: document.getElementById("qrcode") as HTMLImageElement,
|
||||||
|
logo: {
|
||||||
|
src: "/favicon.png",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function print() {
|
||||||
|
leafletImage(map, function (err: string, canvas: HTMLCanvasElement) {
|
||||||
|
var img = document.createElement("img");
|
||||||
|
var dimensions = map.getSize();
|
||||||
|
img.width = dimensions.x;
|
||||||
|
img.height = dimensions.y;
|
||||||
|
img.src = canvas.toDataURL();
|
||||||
|
|
||||||
|
document.getElementById("images")!.innerHTML = "";
|
||||||
|
document.getElementById("images")!.appendChild(img);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateMapSize() {
|
||||||
|
if (!map) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await tick();
|
||||||
|
map.invalidateSize();
|
||||||
|
updateScale(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateScale(map: Map) {
|
||||||
|
const svg = document.getElementById("ruler") as HTMLElement;
|
||||||
|
if (!svg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const svgRect = svg.getBoundingClientRect();
|
||||||
|
const height = svgRect.height;
|
||||||
|
const width = svgRect.width;
|
||||||
|
|
||||||
|
svg.replaceChildren();
|
||||||
|
|
||||||
|
let oneUnitInPixels = calculatePixelPerMeter(
|
||||||
|
map,
|
||||||
|
$currentUser && $currentUser.unit == "imperial" ? 1609.34 : 1000,
|
||||||
|
);
|
||||||
|
let unitsInRuler = width / oneUnitInPixels;
|
||||||
|
|
||||||
|
function createRulerSegment(
|
||||||
|
x: number,
|
||||||
|
width: number,
|
||||||
|
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 text = createText(
|
||||||
|
label,
|
||||||
|
x + width - 5 * label.length,
|
||||||
|
height / 2 + 20,
|
||||||
|
"0.75rem",
|
||||||
|
);
|
||||||
|
svg.appendChild(bar);
|
||||||
|
svg.appendChild(tick);
|
||||||
|
svg.append(text);
|
||||||
|
}
|
||||||
|
let multiplier = 1;
|
||||||
|
|
||||||
|
if (unitsInRuler > 500) {
|
||||||
|
multiplier = 250;
|
||||||
|
} else if (unitsInRuler > 150) {
|
||||||
|
multiplier = 50;
|
||||||
|
} else if (unitsInRuler > 25) {
|
||||||
|
multiplier = 10;
|
||||||
|
} else if (unitsInRuler > 15) {
|
||||||
|
multiplier = 5;
|
||||||
|
} else if (unitsInRuler > 5) {
|
||||||
|
multiplier = 2;
|
||||||
|
} else if (unitsInRuler > 1.5) {
|
||||||
|
multiplier = 1;
|
||||||
|
} else if (unitsInRuler > 1) {
|
||||||
|
multiplier = 0.5;
|
||||||
|
} else if (unitsInRuler > 0.2) {
|
||||||
|
multiplier = 0.1;
|
||||||
|
} else if (unitsInRuler > 0) {
|
||||||
|
multiplier = 0.05;
|
||||||
|
}
|
||||||
|
const segmentCount = Math.floor(unitsInRuler / multiplier);
|
||||||
|
|
||||||
|
svg.appendChild(createText("0", 0, height / 2 + 20, "0.75rem"));
|
||||||
|
for (let i = 0; i < segmentCount; i++) {
|
||||||
|
createRulerSegment(
|
||||||
|
oneUnitInPixels * i * multiplier,
|
||||||
|
oneUnitInPixels * multiplier,
|
||||||
|
`${multiplier < 1 ? ((i + 1) * multiplier).toFixed(1) : (i + 1) * multiplier}`,
|
||||||
|
i % 2 == 0 ? "#7c7c7c" : "#5d5d5d",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
svg.appendChild(
|
||||||
|
createText(
|
||||||
|
$currentUser && $currentUser.unit == "imperial" ? "MI" : "KM",
|
||||||
|
segmentCount * multiplier * oneUnitInPixels - 20,
|
||||||
|
height / 2 - 7,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
scale = calculateScaleFactor(map);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<main class="grid grid-cols-1 md:grid-cols-[400px_1fr] gap-x-1 gap-y-4">
|
||||||
|
<div
|
||||||
|
class="print-details px-6 space-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"
|
||||||
|
on:change={updateMapSize}
|
||||||
|
></Select>
|
||||||
|
<Select
|
||||||
|
bind:value={selectedOrientation}
|
||||||
|
items={orientations}
|
||||||
|
label="Orientation"
|
||||||
|
on:change={updateMapSize}
|
||||||
|
></Select>
|
||||||
|
|
||||||
|
<Button 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][
|
||||||
|
selectedOrientation == 'portrait' ? 'width' : 'height'
|
||||||
|
]}; height: {paperDimensions[selectedPaperSize][
|
||||||
|
selectedOrientation == 'portrait' ? 'height' : 'width'
|
||||||
|
]}"
|
||||||
|
>
|
||||||
|
<div class="basis-full">
|
||||||
|
<MapWithElevation
|
||||||
|
trail={$trail}
|
||||||
|
options={{
|
||||||
|
theme: "gray-theme",
|
||||||
|
slope: false,
|
||||||
|
speed: false,
|
||||||
|
legend: false,
|
||||||
|
time: false,
|
||||||
|
hotline: false,
|
||||||
|
height: 150,
|
||||||
|
}}
|
||||||
|
on:zoomend={(e) => updateScale(e.detail)}
|
||||||
|
bind:map
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="w-[100px] h-[100px]"
|
||||||
|
id="qrcode"
|
||||||
|
alt="QR Code"
|
||||||
|
/>
|
||||||
|
<div class="basis-full mx-4">
|
||||||
|
<svg width="100%" height="50" id="ruler"> </svg>
|
||||||
|
<span class="text-sm"
|
||||||
|
>Scale:   1 : {Math.round(scale)}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</MapWithElevation>
|
||||||
|
</div>
|
||||||
|
<div class="pt-4 -mt-4 border-t border-t-input-border z-10">
|
||||||
|
<div class="float-right"><LogoText></LogoText></div>
|
||||||
|
|
||||||
|
<h4 class="font-semibold">{$trail.name}</h4>
|
||||||
|
<h5>
|
||||||
|
<i class="fa fa-location-dot mr-2"></i>{$trail.location ||
|
||||||
|
"-"}
|
||||||
|
</h5>
|
||||||
|
<div
|
||||||
|
class="flex mt-1 gap-4 text-sm text-gray-500 whitespace-nowrap"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
><i class="fa fa-left-right mr-2"></i>{formatDistance(
|
||||||
|
$trail.distance,
|
||||||
|
)}</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
><i class="fa fa-up-down mr-2"></i>{formatElevation(
|
||||||
|
$trail.elevation_gain,
|
||||||
|
)}</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
><i class="fa fa-clock mr-2"></i>{formatTimeHHMM(
|
||||||
|
$trail.duration,
|
||||||
|
)}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:global(#map-container) {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
19
web/src/routes/map/trail/[id]/print/+page.ts
Normal file
19
web/src/routes/map/trail/[id]/print/+page.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { lists_index } from "$lib/stores/list_store";
|
||||||
|
import { trails_show } from "$lib/stores/trail_store";
|
||||||
|
import { error, type Load } from "@sveltejs/kit";
|
||||||
|
import { ClientResponseError } from "pocketbase";
|
||||||
|
|
||||||
|
export const load: Load = async ({ params, fetch }) => {
|
||||||
|
try {
|
||||||
|
await trails_show(params.id!, true, fetch)
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof ClientResponseError && e.status == 404) {
|
||||||
|
error(404, {
|
||||||
|
message: 'Not found'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log(e);
|
||||||
|
|
||||||
|
}
|
||||||
|
await lists_index(undefined, fetch);
|
||||||
|
};
|
||||||
BIN
web/static/vendor/leaflet-image/marker.png
vendored
Normal file
BIN
web/static/vendor/leaflet-image/marker.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
Reference in New Issue
Block a user