Merge branch 'develop'
This commit is contained in:
commit
943f90e1a7
14 changed files with 297 additions and 111 deletions
|
@ -17,7 +17,7 @@ countrycoder.mapcomplete.org {
|
||||||
|
|
||||||
report.mapcomplete.org {
|
report.mapcomplete.org {
|
||||||
reverse_proxy {
|
reverse_proxy {
|
||||||
to http://127.0.0.1:2600
|
to http://127.0.0.1:2348
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,49 @@
|
||||||
|
|
||||||
This server hosts the studio files and is used for expermintal builds.
|
This server hosts the studio files and is used for expermintal builds.
|
||||||
|
|
||||||
For used hosts, see the Caddyfile
|
For used ports, see the Caddyfile
|
||||||
|
|
||||||
|
To update caddy
|
||||||
|
|
||||||
|
```
|
||||||
|
cp Caddyfile /etc/caddy/
|
||||||
|
# If caddy was running via a console instead of as a service, do `caddy stop` now
|
||||||
|
systemctl reload caddy
|
||||||
|
```
|
||||||
|
|
||||||
|
Debug logs with: `journalctl -u caddy --no-pager | less +G`
|
||||||
|
|
||||||
|
Services:
|
||||||
|
|
||||||
|
|
||||||
## Cache forwarding
|
### studio + theme sync
|
||||||
|
|
||||||
As the ISP of Nerdlab is a bit picky, we use SSH-port-forwarding on the cache server:
|
The studio server, handling those requests.
|
||||||
|
|
||||||
`ssh -R 5445:127.0.0.1:5445 hetzner`
|
`npm run server:studio`
|
||||||
|
|
||||||
|
Additionally, this runs syncthing to make a backup of all theme files.
|
||||||
|
|
||||||
|
### LOD-server
|
||||||
|
|
||||||
|
A server scraping other websites.
|
||||||
|
|
||||||
|
`npm run server:ldjson`
|
||||||
|
|
||||||
|
### Error report server
|
||||||
|
|
||||||
|
A simple server logging everything it receives
|
||||||
|
|
||||||
|
`npm run server:errorreport`
|
||||||
|
|
||||||
|
### geo-ip
|
||||||
|
|
||||||
|
Provides geolocation based on
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/pietervdvn/geoip-server
|
||||||
|
cd geoip-server
|
||||||
|
mkdir data
|
||||||
|
# Drop the databases from https://lite.ip2location.com/ in the data dir
|
||||||
|
npm run start
|
||||||
|
```
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
{
|
{
|
||||||
"id": "mapcomplete-changes",
|
"id": "mapcomplete-changes",
|
||||||
"title": {
|
"title": {
|
||||||
"en": "Changes made with MapComplete"
|
"en": "Changes made with MapComplete",
|
||||||
|
"de": "Änderungen mit MapComplete"
|
||||||
},
|
},
|
||||||
"shortDescription": {
|
"shortDescription": {
|
||||||
"en": "Shows changes made by MapComplete"
|
"en": "Shows changes made by MapComplete",
|
||||||
|
"de": "Änderungen von MapComplete anzeigen"
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"en": "This maps shows all the changes made with MapComplete"
|
"en": "This maps shows all the changes made with MapComplete",
|
||||||
|
"de": "Diese Karte zeigt alle mit MapComplete vorgenommenen Änderungen",
|
||||||
|
"pl": "Ta mapa pokazuje wszystkie zmiany wprowadzone za pomocą MapComplete"
|
||||||
},
|
},
|
||||||
"icon": "./assets/svg/logo.svg",
|
"icon": "./assets/svg/logo.svg",
|
||||||
"hideFromOverview": true,
|
"hideFromOverview": true,
|
||||||
|
@ -18,7 +22,9 @@
|
||||||
{
|
{
|
||||||
"id": "mapcomplete-changes",
|
"id": "mapcomplete-changes",
|
||||||
"name": {
|
"name": {
|
||||||
"en": "Changeset centers"
|
"en": "Changeset centers",
|
||||||
|
"de": "Zentrum der Änderungssätze",
|
||||||
|
"zh_Hant": "變更集中心"
|
||||||
},
|
},
|
||||||
"minzoom": 0,
|
"minzoom": 0,
|
||||||
"source": {
|
"source": {
|
||||||
|
@ -28,41 +34,48 @@
|
||||||
},
|
},
|
||||||
"title": {
|
"title": {
|
||||||
"render": {
|
"render": {
|
||||||
"en": "Changeset for {theme}"
|
"en": "Changeset for {theme}",
|
||||||
|
"de": "Änderungssatz für {theme}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"en": "Shows all MapComplete changes"
|
"en": "Shows all MapComplete changes",
|
||||||
|
"de": "Alle MapComplete-Änderungen anzeigen"
|
||||||
},
|
},
|
||||||
"tagRenderings": [
|
"tagRenderings": [
|
||||||
{
|
{
|
||||||
"id": "show_changeset_id",
|
"id": "show_changeset_id",
|
||||||
"render": {
|
"render": {
|
||||||
"en": "Changeset <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>"
|
"en": "Changeset <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>",
|
||||||
|
"de": "Änderungssatz <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "contributor",
|
"id": "contributor",
|
||||||
"question": {
|
"question": {
|
||||||
"en": "What contributor did make this change?"
|
"en": "What contributor did make this change?",
|
||||||
|
"de": "Welcher Mitwirkende hat diese Änderung vorgenommen?"
|
||||||
},
|
},
|
||||||
"freeform": {
|
"freeform": {
|
||||||
"key": "user"
|
"key": "user"
|
||||||
},
|
},
|
||||||
"render": {
|
"render": {
|
||||||
"en": "Change made by <a href='https://openstreetmap.org/user/{user}' target='_blank'>{user}</a>"
|
"en": "Change made by <a href='https://openstreetmap.org/user/{user}' target='_blank'>{user}</a>",
|
||||||
|
"de": "Änderung vorgenommen von <a href='https://openstreetmap.org/user/{user}' target='_blank'>{user}</a>"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "theme-id",
|
"id": "theme-id",
|
||||||
"question": {
|
"question": {
|
||||||
"en": "What theme was used to make this change?"
|
"en": "What theme was used to make this change?",
|
||||||
|
"de": "Welches Thema wurde für die Änderung verwendet?"
|
||||||
},
|
},
|
||||||
"freeform": {
|
"freeform": {
|
||||||
"key": "theme"
|
"key": "theme"
|
||||||
},
|
},
|
||||||
"render": {
|
"render": {
|
||||||
"en": "Change with theme <a href='https://mapcomplete.org/{theme}'>{theme}</a>"
|
"en": "Change with theme <a href='https://mapcomplete.org/{theme}'>{theme}</a>",
|
||||||
|
"de": "Geändert mit Thema <a href='https://mapcomplete.osm.be/{theme}'>{theme}</a>"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -71,19 +84,23 @@
|
||||||
"key": "locale"
|
"key": "locale"
|
||||||
},
|
},
|
||||||
"question": {
|
"question": {
|
||||||
"en": "What locale (language) was this change made in?"
|
"en": "What locale (language) was this change made in?",
|
||||||
|
"de": "In welcher Benutzersprache wurde die Änderung vorgenommen?"
|
||||||
},
|
},
|
||||||
"render": {
|
"render": {
|
||||||
"en": "User locale is {locale}"
|
"en": "User locale is {locale}",
|
||||||
|
"de": "Benutzersprache {locale}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "host",
|
"id": "host",
|
||||||
"render": {
|
"render": {
|
||||||
"en": "Change with with <a href='{host}'>{host}</a>"
|
"en": "Change with with <a href='{host}'>{host}</a>",
|
||||||
|
"de": "Änderung über <a href='{host}'>{host}</a>"
|
||||||
},
|
},
|
||||||
"question": {
|
"question": {
|
||||||
"en": "What host (website) was this change made with?"
|
"en": "What host (website) was this change made with?",
|
||||||
|
"de": "Über welchen Host (Webseite) wurde diese Änderung vorgenommen?"
|
||||||
},
|
},
|
||||||
"freeform": {
|
"freeform": {
|
||||||
"key": "host"
|
"key": "host"
|
||||||
|
@ -104,10 +121,12 @@
|
||||||
{
|
{
|
||||||
"id": "version",
|
"id": "version",
|
||||||
"question": {
|
"question": {
|
||||||
"en": "What version of MapComplete was used to make this change?"
|
"en": "What version of MapComplete was used to make this change?",
|
||||||
|
"de": "Mit welcher MapComplete Version wurde die Änderung vorgenommen?"
|
||||||
},
|
},
|
||||||
"render": {
|
"render": {
|
||||||
"en": "Made with {editor}"
|
"en": "Made with {editor}",
|
||||||
|
"de": "Erstellt mit {editor}"
|
||||||
},
|
},
|
||||||
"freeform": {
|
"freeform": {
|
||||||
"key": "editor"
|
"key": "editor"
|
||||||
|
@ -493,7 +512,9 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Themename contains {search}"
|
"en": "Themename contains {search}",
|
||||||
|
"de": "Themenname enthält {search}",
|
||||||
|
"pl": "Nazwa tematu zawiera {search}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -509,7 +530,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Themename does <b>not</b> contain {search}"
|
"en": "Themename does <b>not</b> contain {search}",
|
||||||
|
"de": "Themename enthält <b>not</b> {search}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -525,7 +547,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Made by contributor {search}"
|
"en": "Made by contributor {search}",
|
||||||
|
"de": "Erstellt vom Mitwirkenden {search}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -541,7 +564,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"question": {
|
"question": {
|
||||||
"en": "<b>Not</b> made by contributor {search}"
|
"en": "<b>Not</b> made by contributor {search}",
|
||||||
|
"de": "<b>Nicht</b> erstellt von Mitwirkendem {search}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -558,7 +582,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Made before {search}"
|
"en": "Made before {search}",
|
||||||
|
"de": "Erstellt vor {search}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -575,7 +600,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Made after {search}"
|
"en": "Made after {search}",
|
||||||
|
"de": "Erstellt nach {search}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -591,7 +617,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"question": {
|
"question": {
|
||||||
"en": "User language (iso-code) {search}"
|
"en": "User language (iso-code) {search}",
|
||||||
|
"de": "Benutzersprache (ISO-Code) {search}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -607,7 +634,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Made with host {search}"
|
"en": "Made with host {search}",
|
||||||
|
"de": "Erstellt mit Host {search}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -618,7 +646,8 @@
|
||||||
{
|
{
|
||||||
"osmTags": "add-image>0",
|
"osmTags": "add-image>0",
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Changeset added at least one image"
|
"en": "Changeset added at least one image",
|
||||||
|
"de": "Änderungssatz hat mindestens ein Bild hinzugefügt"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -629,7 +658,8 @@
|
||||||
{
|
{
|
||||||
"osmTags": "theme!=grb",
|
"osmTags": "theme!=grb",
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Exclude GRB theme"
|
"en": "Exclude GRB theme",
|
||||||
|
"de": "GRB-Thema ausschließen"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -640,7 +670,8 @@
|
||||||
{
|
{
|
||||||
"osmTags": "theme!=etymology",
|
"osmTags": "theme!=etymology",
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Exclude etymology theme"
|
"en": "Exclude etymology theme",
|
||||||
|
"de": "Etymologie-Thema ausschließen"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -655,7 +686,8 @@
|
||||||
{
|
{
|
||||||
"id": "link_to_more",
|
"id": "link_to_more",
|
||||||
"render": {
|
"render": {
|
||||||
"en": "More statistics can be found <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>here</a>"
|
"en": "More statistics can be found <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>here</a>",
|
||||||
|
"de": "Weitere Statistiken gibt es <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>hier</a>"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -288,6 +288,7 @@
|
||||||
"loadingTheme": "Loading {theme}…",
|
"loadingTheme": "Loading {theme}…",
|
||||||
"loginFailed": "Logging in into OpenStreetMap failed",
|
"loginFailed": "Logging in into OpenStreetMap failed",
|
||||||
"loginFailedOfflineMode": "OpenStreetMap.org is currently not available due to maintenance. Making edits will be possible soon",
|
"loginFailedOfflineMode": "OpenStreetMap.org is currently not available due to maintenance. Making edits will be possible soon",
|
||||||
|
"loginFailedOfflineOther": "OpenStreetMap.org could not be reached",
|
||||||
"loginFailedReadonlyMode": "OpenStreetMap.org is currently in readonly mode due to maintenance. Making edits will be possible soon",
|
"loginFailedReadonlyMode": "OpenStreetMap.org is currently in readonly mode due to maintenance. Making edits will be possible soon",
|
||||||
"loginFailedUnreachableMode": "OpenStreetMap.org is currently not reachable. Are you connected to the internet or do you block third parties? Try again later",
|
"loginFailedUnreachableMode": "OpenStreetMap.org is currently not reachable. Are you connected to the internet or do you block third parties? Try again later",
|
||||||
"loginOnlyNeededToEdit": "if you want to make changes",
|
"loginOnlyNeededToEdit": "if you want to make changes",
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
"#summary_server": "Should be the endpoint; appending status.json should work",
|
"#summary_server": "Should be the endpoint; appending status.json should work",
|
||||||
"summary_server": "https://cache.mapcomplete.org/",
|
"summary_server": "https://cache.mapcomplete.org/",
|
||||||
"geoip_server": "https://ipinfo.mapcomplete.org/",
|
"geoip_server": "https://ipinfo.mapcomplete.org/",
|
||||||
|
"error_server": "https://report.mapcomplete.org/report",
|
||||||
"disabled:oauth_credentials": {
|
"disabled:oauth_credentials": {
|
||||||
"##": "DEV",
|
"##": "DEV",
|
||||||
"#": "This client-id is registered by 'MapComplete' on https://master.apis.dev.openstreetmap.org/",
|
"#": "This client-id is registered by 'MapComplete' on https://master.apis.dev.openstreetmap.org/",
|
||||||
|
@ -114,8 +115,10 @@
|
||||||
"dloadVelopark": "vite-node scripts/velopark/veloParkToGeojson.ts ",
|
"dloadVelopark": "vite-node scripts/velopark/veloParkToGeojson.ts ",
|
||||||
"compareVelopark": "vite-node scripts/velopark/compare.ts -- velopark_nonsynced_.geojson ~/Projecten/OSM/Fietsberaad/2024-02-02\\ Fietsenstallingen_OSM_met_velopark_ref.geojson\n",
|
"compareVelopark": "vite-node scripts/velopark/compare.ts -- velopark_nonsynced_.geojson ~/Projecten/OSM/Fietsberaad/2024-02-02\\ Fietsenstallingen_OSM_met_velopark_ref.geojson\n",
|
||||||
"scrapeWebsites": "vite-node scripts/importscripts/compareWebsiteData.ts -- ~/Downloads/ShopsWithWebsiteNodes.csv ~/data/scraped_websites/",
|
"scrapeWebsites": "vite-node scripts/importscripts/compareWebsiteData.ts -- ~/Downloads/ShopsWithWebsiteNodes.csv ~/data/scraped_websites/",
|
||||||
"summary-server": "vite-node scripts/osm2pgsql/tilecountServer.ts",
|
"server:summary": "vite-node scripts/osm2pgsql/tilecountServer.ts",
|
||||||
"ldjson-server": "vite-node scripts/serverLdScrape.ts",
|
"server:ldjson": "vite-node scripts/serverLdScrape.ts",
|
||||||
|
"sever:studio": "vite-node scripts/studioServer -- /root/git/MapComplete/assets",
|
||||||
|
"server:errorreport": "vite-node scripts/serverErrorReport.ts -- /root/error_reports/",
|
||||||
"generate:buildDbScript": "vite-node scripts/osm2pgsql/generateBuildDbScript.ts"
|
"generate:buildDbScript": "vite-node scripts/osm2pgsql/generateBuildDbScript.ts"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|
|
@ -1229,14 +1229,14 @@ video {
|
||||||
height: 6rem;
|
height: 6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.h-screen {
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.h-full {
|
.h-full {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.h-screen {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
.h-fit {
|
.h-fit {
|
||||||
height: -webkit-fit-content;
|
height: -webkit-fit-content;
|
||||||
height: -moz-fit-content;
|
height: -moz-fit-content;
|
||||||
|
|
|
@ -1,17 +1,36 @@
|
||||||
import http from "node:http"
|
import http from "node:http"
|
||||||
|
|
||||||
|
export interface Handler {
|
||||||
|
mustMatch: string | RegExp
|
||||||
|
mimetype: string
|
||||||
|
addHeaders?: Record<string, string>
|
||||||
|
handle: (path: string, queryParams: URLSearchParams, req: http.IncomingMessage) => Promise<string>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class ServerUtils {
|
||||||
|
|
||||||
|
public static getBody(req: http.IncomingMessage): Promise<string> {
|
||||||
|
return new Promise<string>((resolve) => {
|
||||||
|
let body = '';
|
||||||
|
req.on('data', (chunk) => {
|
||||||
|
body += chunk;
|
||||||
|
});
|
||||||
|
req.on('end', () => {
|
||||||
|
resolve(body)
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
export class Server {
|
export class Server {
|
||||||
constructor(
|
constructor(
|
||||||
port: number,
|
port: number,
|
||||||
options: {
|
options: {
|
||||||
ignorePathPrefix?: string[]
|
ignorePathPrefix?: string[]
|
||||||
},
|
},
|
||||||
handle: {
|
handle: Handler[]
|
||||||
mustMatch: string | RegExp
|
|
||||||
mimetype: string
|
|
||||||
addHeaders?: Record<string, string>
|
|
||||||
handle: (path: string, queryParams: URLSearchParams) => Promise<string>
|
|
||||||
}[]
|
|
||||||
) {
|
) {
|
||||||
handle.push({
|
handle.push({
|
||||||
mustMatch: "",
|
mustMatch: "",
|
||||||
|
@ -81,8 +100,9 @@ export class Server {
|
||||||
res.end()
|
res.end()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
let body = undefined
|
||||||
if (req.method === "POST" || req.method === "UPDATE") {
|
if (req.method === "POST" || req.method === "UPDATE") {
|
||||||
return
|
body = await ServerUtils.getBody(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.method === "DELETE") {
|
if (req.method === "DELETE") {
|
||||||
|
@ -90,7 +110,7 @@ export class Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await handler.handle(path, url.searchParams)
|
const result = await handler.handle(path, url.searchParams, req, body)
|
||||||
if (result === undefined) {
|
if (result === undefined) {
|
||||||
res.writeHead(500)
|
res.writeHead(500)
|
||||||
res.write("Could not fetch this website, probably blocked by them")
|
res.write("Could not fetch this website, probably blocked by them")
|
||||||
|
|
52
scripts/serverErrorReport.ts
Normal file
52
scripts/serverErrorReport.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import { Handler, Server } from "./server"
|
||||||
|
import Script from "./Script"
|
||||||
|
import { appendFileSync, existsSync, mkdirSync, writeFileSync } from "fs"
|
||||||
|
import { mkdir } from "node:fs"
|
||||||
|
import ScriptUtils from "./ScriptUtils"
|
||||||
|
|
||||||
|
class ServerErrorReport extends Script {
|
||||||
|
constructor() {
|
||||||
|
super("A server which receives and logs error reports")
|
||||||
|
}
|
||||||
|
|
||||||
|
async main(args: string[]): Promise<void> {
|
||||||
|
const logDirectory = args[0] ?? "./error_logs"
|
||||||
|
console.log("Logging to directory", logDirectory)
|
||||||
|
if (!existsSync(logDirectory)) {
|
||||||
|
mkdirSync(logDirectory)
|
||||||
|
console.log("Created this directory")
|
||||||
|
}
|
||||||
|
let errorReport = 0
|
||||||
|
new Server(2348, {},
|
||||||
|
[<Handler>{
|
||||||
|
mustMatch: "report",
|
||||||
|
mimetype: "application/json",
|
||||||
|
handle: async (_, queryParams, req, body) => {
|
||||||
|
if (!body) {
|
||||||
|
throw "{\"error\": \"No body; use a post request\"}"
|
||||||
|
}
|
||||||
|
console.log(body)
|
||||||
|
const ip = <string>req.headers["x-forwarded-for"]
|
||||||
|
const d = new Date()
|
||||||
|
const date = d.toISOString()
|
||||||
|
const file = logDirectory + "/" + d.getUTCFullYear() + "_" + d.getUTCMonth() + "_" + d.getUTCDay() + ".lines.json"
|
||||||
|
try{
|
||||||
|
body = JSON.parse(body)
|
||||||
|
}catch (e) {
|
||||||
|
// could not parse, we'll save it as is
|
||||||
|
}
|
||||||
|
const contents = "\n"+JSON.stringify({ ip, index: errorReport, date, message: body })
|
||||||
|
if (!existsSync(file)) {
|
||||||
|
writeFileSync(file, contents)
|
||||||
|
} else {
|
||||||
|
appendFileSync(file, contents)
|
||||||
|
}
|
||||||
|
errorReport++
|
||||||
|
return `{"status":"ok", "nr": ${errorReport}}`
|
||||||
|
},
|
||||||
|
|
||||||
|
}])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new ServerErrorReport().run()
|
|
@ -43,6 +43,7 @@ export class Changes {
|
||||||
private readonly previouslyCreated: OsmObject[] = []
|
private readonly previouslyCreated: OsmObject[] = []
|
||||||
private readonly _leftRightSensitive: boolean
|
private readonly _leftRightSensitive: boolean
|
||||||
private readonly _changesetHandler: ChangesetHandler
|
private readonly _changesetHandler: ChangesetHandler
|
||||||
|
private readonly _reportError?: (string: string | Error) => void
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
state: {
|
state: {
|
||||||
|
@ -53,7 +54,8 @@ export class Changes {
|
||||||
historicalUserLocations?: FeatureSource
|
historicalUserLocations?: FeatureSource
|
||||||
featureSwitches?: FeatureSwitchState
|
featureSwitches?: FeatureSwitchState
|
||||||
},
|
},
|
||||||
leftRightSensitive: boolean = false
|
leftRightSensitive: boolean = false,
|
||||||
|
reportError?: (string: string | Error) => void,
|
||||||
) {
|
) {
|
||||||
this._leftRightSensitive = leftRightSensitive
|
this._leftRightSensitive = leftRightSensitive
|
||||||
// We keep track of all changes just as well
|
// We keep track of all changes just as well
|
||||||
|
@ -62,11 +64,13 @@ export class Changes {
|
||||||
this._nextId = Math.min(-1, ...(this.pendingChanges.data?.map((pch) => pch.id) ?? []))
|
this._nextId = Math.min(-1, ...(this.pendingChanges.data?.map((pch) => pch.id) ?? []))
|
||||||
this.state = state
|
this.state = state
|
||||||
this.backend = state.osmConnection.Backend()
|
this.backend = state.osmConnection.Backend()
|
||||||
|
this._reportError = reportError
|
||||||
this._changesetHandler = new ChangesetHandler(
|
this._changesetHandler = new ChangesetHandler(
|
||||||
state.dryRun,
|
state.dryRun,
|
||||||
state.osmConnection,
|
state.osmConnection,
|
||||||
state.featurePropertiesStore,
|
state.featurePropertiesStore,
|
||||||
this
|
this,
|
||||||
|
e => this._reportError(e)
|
||||||
)
|
)
|
||||||
this.historicalUserLocations = state.historicalUserLocations
|
this.historicalUserLocations = state.historicalUserLocations
|
||||||
|
|
||||||
|
@ -234,6 +238,7 @@ export class Changes {
|
||||||
console.log("Changes flushed. Your changeset is " + csNumber)
|
console.log("Changes flushed. Your changeset is " + csNumber)
|
||||||
this.errors.setData([])
|
this.errors.setData([])
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
this._reportError(e)
|
||||||
this.isUploading.setData(false)
|
this.isUploading.setData(false)
|
||||||
this.errors.data.push(e)
|
this.errors.data.push(e)
|
||||||
this.errors.ping()
|
this.errors.ping()
|
||||||
|
@ -518,6 +523,9 @@ export class Changes {
|
||||||
const osmObj = await downloader.DownloadObjectAsync(id, 0)
|
const osmObj = await downloader.DownloadObjectAsync(id, 0)
|
||||||
return { id, osmObj }
|
return { id, osmObj }
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
this._reportError( "Could not download OSM-object"+
|
||||||
|
id+
|
||||||
|
" dropping it from the changes (" + e + ")")
|
||||||
console.error(
|
console.error(
|
||||||
"Could not download OSM-object",
|
"Could not download OSM-object",
|
||||||
id,
|
id,
|
||||||
|
@ -685,6 +693,7 @@ export class Changes {
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
this._reportError(e)
|
||||||
console.error("Could not upload some changes:", e)
|
console.error("Could not upload some changes:", e)
|
||||||
this.errors.data.push(e)
|
this.errors.data.push(e)
|
||||||
this.errors.ping()
|
this.errors.ping()
|
||||||
|
|
|
@ -26,6 +26,7 @@ export class ChangesetHandler {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
private readonly _remappings = new Map<string, string>()
|
private readonly _remappings = new Map<string, string>()
|
||||||
|
private readonly _reportError: (e: (string | Error)) => void
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
dryRun: Store<boolean>,
|
dryRun: Store<boolean>,
|
||||||
|
@ -34,9 +35,11 @@ export class ChangesetHandler {
|
||||||
| FeaturePropertiesStore
|
| FeaturePropertiesStore
|
||||||
| { addAlias: (id0: string, id1: string) => void }
|
| { addAlias: (id0: string, id1: string) => void }
|
||||||
| undefined,
|
| undefined,
|
||||||
changes: Changes
|
changes: Changes,
|
||||||
|
reportError: (e: string | Error) => void
|
||||||
) {
|
) {
|
||||||
this.osmConnection = osmConnection
|
this.osmConnection = osmConnection
|
||||||
|
this._reportError = reportError
|
||||||
this.allElements = <FeaturePropertiesStore>allElements
|
this.allElements = <FeaturePropertiesStore>allElements
|
||||||
this.changes = changes
|
this.changes = changes
|
||||||
this._dryRun = dryRun
|
this._dryRun = dryRun
|
||||||
|
@ -148,8 +151,13 @@ export class ChangesetHandler {
|
||||||
await this.UpdateTags(csId, extraMetaTags)
|
await this.UpdateTags(csId, extraMetaTags)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Could not open/upload changeset due to ", e)
|
if(this._reportError){
|
||||||
|
this._reportError(e)
|
||||||
|
}
|
||||||
|
console.warn("Could not open/upload changeset due to ", e,"trying t")
|
||||||
openChangeset.setData(undefined)
|
openChangeset.setData(undefined)
|
||||||
|
|
||||||
|
throw e
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// There still exists an open changeset (or at least we hope so)
|
// There still exists an open changeset (or at least we hope so)
|
||||||
|
@ -178,8 +186,12 @@ export class ChangesetHandler {
|
||||||
)
|
)
|
||||||
await this.UpdateTags(csId, rewrittenTags)
|
await this.UpdateTags(csId, rewrittenTags)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if(this._reportError){
|
||||||
|
this._reportError(e)
|
||||||
|
}
|
||||||
console.warn("Could not upload, changeset is probably closed: ", e)
|
console.warn("Could not upload, changeset is probably closed: ", e)
|
||||||
openChangeset.setData(undefined)
|
openChangeset.setData(undefined)
|
||||||
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,6 +164,7 @@ export default class Constants {
|
||||||
*/
|
*/
|
||||||
public static VectorTileServer: string | undefined = Constants.config.mvt_layer_server
|
public static VectorTileServer: string | undefined = Constants.config.mvt_layer_server
|
||||||
public static GeoIpServer: string | undefined = Constants.config.geoip_server
|
public static GeoIpServer: string | undefined = Constants.config.geoip_server
|
||||||
|
public static ErrorReportServer: string | undefined = Constants.config.error_server
|
||||||
|
|
||||||
public static readonly maptilerApiKey = "GvoVAJgu46I5rZapJuAy"
|
public static readonly maptilerApiKey = "GvoVAJgu46I5rZapJuAy"
|
||||||
public static readonly SummaryServer: string = Constants.config.summary_server
|
public static readonly SummaryServer: string = Constants.config.summary_server
|
||||||
|
|
|
@ -2,11 +2,7 @@ import LayoutConfig from "./ThemeConfig/LayoutConfig"
|
||||||
import { SpecialVisualizationState } from "../UI/SpecialVisualization"
|
import { SpecialVisualizationState } from "../UI/SpecialVisualization"
|
||||||
import { Changes } from "../Logic/Osm/Changes"
|
import { Changes } from "../Logic/Osm/Changes"
|
||||||
import { Store, UIEventSource } from "../Logic/UIEventSource"
|
import { Store, UIEventSource } from "../Logic/UIEventSource"
|
||||||
import {
|
import { FeatureSource, IndexedFeatureSource, WritableFeatureSource } from "../Logic/FeatureSource/FeatureSource"
|
||||||
FeatureSource,
|
|
||||||
IndexedFeatureSource,
|
|
||||||
WritableFeatureSource,
|
|
||||||
} from "../Logic/FeatureSource/FeatureSource"
|
|
||||||
import { OsmConnection } from "../Logic/Osm/OsmConnection"
|
import { OsmConnection } from "../Logic/Osm/OsmConnection"
|
||||||
import { ExportableMap, MapProperties } from "./MapProperties"
|
import { ExportableMap, MapProperties } from "./MapProperties"
|
||||||
import LayerState from "../Logic/State/LayerState"
|
import LayerState from "../Logic/State/LayerState"
|
||||||
|
@ -50,9 +46,7 @@ import BackgroundLayerResetter from "../Logic/Actors/BackgroundLayerResetter"
|
||||||
import SaveFeatureSourceToLocalStorage from "../Logic/FeatureSource/Actors/SaveFeatureSourceToLocalStorage"
|
import SaveFeatureSourceToLocalStorage from "../Logic/FeatureSource/Actors/SaveFeatureSourceToLocalStorage"
|
||||||
import BBoxFeatureSource from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource"
|
import BBoxFeatureSource from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource"
|
||||||
import ThemeViewStateHashActor from "../Logic/Web/ThemeViewStateHashActor"
|
import ThemeViewStateHashActor from "../Logic/Web/ThemeViewStateHashActor"
|
||||||
import NoElementsInViewDetector, {
|
import NoElementsInViewDetector, { FeatureViewState } from "../Logic/Actors/NoElementsInViewDetector"
|
||||||
FeatureViewState,
|
|
||||||
} from "../Logic/Actors/NoElementsInViewDetector"
|
|
||||||
import FilteredLayer from "./FilteredLayer"
|
import FilteredLayer from "./FilteredLayer"
|
||||||
import { PreferredRasterLayerSelector } from "../Logic/Actors/PreferredRasterLayerSelector"
|
import { PreferredRasterLayerSelector } from "../Logic/Actors/PreferredRasterLayerSelector"
|
||||||
import { ImageUploadManager } from "../Logic/ImageProviders/ImageUploadManager"
|
import { ImageUploadManager } from "../Logic/ImageProviders/ImageUploadManager"
|
||||||
|
@ -158,7 +152,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
this.featureSwitches = new FeatureSwitchState(layout)
|
this.featureSwitches = new FeatureSwitchState(layout)
|
||||||
this.guistate = new MenuState(
|
this.guistate = new MenuState(
|
||||||
this.featureSwitches.featureSwitchWelcomeMessage.data,
|
this.featureSwitches.featureSwitchWelcomeMessage.data,
|
||||||
layout.id
|
layout.id,
|
||||||
)
|
)
|
||||||
this.map = new UIEventSource<MlMap>(undefined)
|
this.map = new UIEventSource<MlMap>(undefined)
|
||||||
const geolocationState = new GeoLocationState()
|
const geolocationState = new GeoLocationState()
|
||||||
|
@ -174,14 +168,14 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
oauth_token: QueryParameters.GetQueryParameter(
|
oauth_token: QueryParameters.GetQueryParameter(
|
||||||
"oauth_token",
|
"oauth_token",
|
||||||
undefined,
|
undefined,
|
||||||
"Used to complete the login"
|
"Used to complete the login",
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
this.userRelatedState = new UserRelatedState(
|
this.userRelatedState = new UserRelatedState(
|
||||||
this.osmConnection,
|
this.osmConnection,
|
||||||
layout,
|
layout,
|
||||||
this.featureSwitches,
|
this.featureSwitches,
|
||||||
this.mapProperties
|
this.mapProperties,
|
||||||
)
|
)
|
||||||
this.userRelatedState.fixateNorth.addCallbackAndRunD((fixated) => {
|
this.userRelatedState.fixateNorth.addCallbackAndRunD((fixated) => {
|
||||||
this.mapProperties.allowRotating.setData(fixated !== "yes")
|
this.mapProperties.allowRotating.setData(fixated !== "yes")
|
||||||
|
@ -192,13 +186,13 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
geolocationState,
|
geolocationState,
|
||||||
this.selectedElement,
|
this.selectedElement,
|
||||||
this.mapProperties,
|
this.mapProperties,
|
||||||
this.userRelatedState.gpsLocationHistoryRetentionTime
|
this.userRelatedState.gpsLocationHistoryRetentionTime,
|
||||||
)
|
)
|
||||||
this.geolocationControl = new GeolocationControlState(this.geolocation, this.mapProperties)
|
this.geolocationControl = new GeolocationControlState(this.geolocation, this.mapProperties)
|
||||||
|
|
||||||
this.availableLayers = AvailableRasterLayers.layersAvailableAt(
|
this.availableLayers = AvailableRasterLayers.layersAvailableAt(
|
||||||
this.mapProperties.location,
|
this.mapProperties.location,
|
||||||
this.osmConnection.isLoggedIn
|
this.osmConnection.isLoggedIn,
|
||||||
)
|
)
|
||||||
|
|
||||||
const self = this
|
const self = this
|
||||||
|
@ -210,7 +204,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
const isDisplayed = QueryParameters.GetBooleanQueryParameter(
|
const isDisplayed = QueryParameters.GetBooleanQueryParameter(
|
||||||
"overlay-" + rasterInfo.id,
|
"overlay-" + rasterInfo.id,
|
||||||
rasterInfo.defaultState ?? true,
|
rasterInfo.defaultState ?? true,
|
||||||
"Wether or not overlayer layer " + rasterInfo.id + " is shown"
|
"Wether or not overlayer layer " + rasterInfo.id + " is shown",
|
||||||
)
|
)
|
||||||
const state = { isDisplayed }
|
const state = { isDisplayed }
|
||||||
overlayLayerStates.set(rasterInfo.id, state)
|
overlayLayerStates.set(rasterInfo.id, state)
|
||||||
|
@ -235,7 +229,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
this.osmConnection.Backend(),
|
this.osmConnection.Backend(),
|
||||||
(id) => self.layerState.filteredLayers.get(id).isDisplayed,
|
(id) => self.layerState.filteredLayers.get(id).isDisplayed,
|
||||||
mvtAvailableLayers,
|
mvtAvailableLayers,
|
||||||
this.fullNodeDatabase
|
this.fullNodeDatabase,
|
||||||
)
|
)
|
||||||
|
|
||||||
let currentViewIndex = 0
|
let currentViewIndex = 0
|
||||||
|
@ -253,7 +247,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
id: "current_view_" + currentViewIndex,
|
id: "current_view_" + currentViewIndex,
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
this.featuresInView = new BBoxFeatureSource(layoutSource, this.mapProperties.bounds)
|
this.featuresInView = new BBoxFeatureSource(layoutSource, this.mapProperties.bounds)
|
||||||
|
|
||||||
|
@ -270,19 +264,20 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
historicalUserLocations: this.geolocation.historicalUserLocations,
|
historicalUserLocations: this.geolocation.historicalUserLocations,
|
||||||
featureSwitches: this.featureSwitches,
|
featureSwitches: this.featureSwitches,
|
||||||
},
|
},
|
||||||
layout?.isLeftRightSensitive() ?? false
|
layout?.isLeftRightSensitive() ?? false,
|
||||||
|
e => this.reportError(e),
|
||||||
)
|
)
|
||||||
this.historicalUserLocations = this.geolocation.historicalUserLocations
|
this.historicalUserLocations = this.geolocation.historicalUserLocations
|
||||||
this.newFeatures = new NewGeometryFromChangesFeatureSource(
|
this.newFeatures = new NewGeometryFromChangesFeatureSource(
|
||||||
this.changes,
|
this.changes,
|
||||||
layoutSource,
|
layoutSource,
|
||||||
this.featureProperties
|
this.featureProperties,
|
||||||
)
|
)
|
||||||
layoutSource.addSource(this.newFeatures)
|
layoutSource.addSource(this.newFeatures)
|
||||||
|
|
||||||
const perLayer = new PerLayerFeatureSourceSplitter(
|
const perLayer = new PerLayerFeatureSourceSplitter(
|
||||||
Array.from(this.layerState.filteredLayers.values()).filter(
|
Array.from(this.layerState.filteredLayers.values()).filter(
|
||||||
(l) => l.layerDef?.source !== null
|
(l) => l.layerDef?.source !== null,
|
||||||
),
|
),
|
||||||
new ChangeGeometryApplicator(this.indexedFeatures, this.changes),
|
new ChangeGeometryApplicator(this.indexedFeatures, this.changes),
|
||||||
{
|
{
|
||||||
|
@ -293,10 +288,10 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
"Got ",
|
"Got ",
|
||||||
features.length,
|
features.length,
|
||||||
"leftover features, such as",
|
"leftover features, such as",
|
||||||
features[0].properties
|
features[0].properties,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
this.perLayer = perLayer.perLayer
|
this.perLayer = perLayer.perLayer
|
||||||
}
|
}
|
||||||
|
@ -335,12 +330,12 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
|
|
||||||
this.lastClickObject = new LastClickFeatureSource(
|
this.lastClickObject = new LastClickFeatureSource(
|
||||||
this.layout,
|
this.layout,
|
||||||
this.mapProperties.lastClickLocation
|
this.mapProperties.lastClickLocation,
|
||||||
)
|
)
|
||||||
|
|
||||||
this.osmObjectDownloader = new OsmObjectDownloader(
|
this.osmObjectDownloader = new OsmObjectDownloader(
|
||||||
this.osmConnection.Backend(),
|
this.osmConnection.Backend(),
|
||||||
this.changes
|
this.changes,
|
||||||
)
|
)
|
||||||
|
|
||||||
this.perLayerFiltered = this.showNormalDataOn(this.map)
|
this.perLayerFiltered = this.showNormalDataOn(this.map)
|
||||||
|
@ -351,7 +346,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
currentZoom: this.mapProperties.zoom,
|
currentZoom: this.mapProperties.zoom,
|
||||||
layerState: this.layerState,
|
layerState: this.layerState,
|
||||||
bounds: this.visualFeedbackViewportBounds,
|
bounds: this.visualFeedbackViewportBounds,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
this.hasDataInView = new NoElementsInViewDetector(this).hasFeatureInView
|
this.hasDataInView = new NoElementsInViewDetector(this).hasFeatureInView
|
||||||
this.imageUploadManager = new ImageUploadManager(
|
this.imageUploadManager = new ImageUploadManager(
|
||||||
|
@ -359,7 +354,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
Imgur.singleton,
|
Imgur.singleton,
|
||||||
this.featureProperties,
|
this.featureProperties,
|
||||||
this.osmConnection,
|
this.osmConnection,
|
||||||
this.changes
|
this.changes,
|
||||||
)
|
)
|
||||||
this.favourites = new FavouritesFeatureSource(this)
|
this.favourites = new FavouritesFeatureSource(this)
|
||||||
|
|
||||||
|
@ -402,7 +397,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
LayoutSource.fromCacheZoomLevel,
|
LayoutSource.fromCacheZoomLevel,
|
||||||
fs,
|
fs,
|
||||||
this.featureProperties,
|
this.featureProperties,
|
||||||
fs.layer.layerDef.maxAgeOfCache
|
fs.layer.layerDef.maxAgeOfCache,
|
||||||
)
|
)
|
||||||
toLocalStorage.set(layerId, storage)
|
toLocalStorage.set(layerId, storage)
|
||||||
})
|
})
|
||||||
|
@ -415,7 +410,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
const doShowLayer = this.mapProperties.zoom.map(
|
const doShowLayer = this.mapProperties.zoom.map(
|
||||||
(z) =>
|
(z) =>
|
||||||
(fs.layer.isDisplayed?.data ?? true) && z >= (fs.layer.layerDef?.minzoom ?? 0),
|
(fs.layer.isDisplayed?.data ?? true) && z >= (fs.layer.layerDef?.minzoom ?? 0),
|
||||||
[fs.layer.isDisplayed]
|
[fs.layer.isDisplayed],
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!doShowLayer.data && this.featureSwitches.featureSwitchFilter.data === false) {
|
if (!doShowLayer.data && this.featureSwitches.featureSwitchFilter.data === false) {
|
||||||
|
@ -432,7 +427,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
fs.layer,
|
fs.layer,
|
||||||
fs,
|
fs,
|
||||||
(id) => this.featureProperties.getStore(id),
|
(id) => this.featureProperties.getStore(id),
|
||||||
this.layerState.globalFilters
|
this.layerState.globalFilters,
|
||||||
)
|
)
|
||||||
filteringFeatureSource.set(layerName, filtered)
|
filteringFeatureSource.set(layerName, filtered)
|
||||||
|
|
||||||
|
@ -573,7 +568,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.selectClosestAtCenter(0)
|
this.selectClosestAtCenter(0)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
for (let i = 1; i < 9; i++) {
|
for (let i = 1; i < 9; i++) {
|
||||||
|
@ -591,7 +586,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
onUp: true,
|
onUp: true,
|
||||||
},
|
},
|
||||||
doc,
|
doc,
|
||||||
() => this.selectClosestAtCenter(i - 1)
|
() => this.selectClosestAtCenter(i - 1),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -608,7 +603,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
if (this.featureSwitches.featureSwitchBackgroundSelection.data) {
|
if (this.featureSwitches.featureSwitchBackgroundSelection.data) {
|
||||||
this.guistate.backgroundLayerSelectionIsOpened.setData(true)
|
this.guistate.backgroundLayerSelectionIsOpened.setData(true)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
Hotkeys.RegisterHotkey(
|
Hotkeys.RegisterHotkey(
|
||||||
{
|
{
|
||||||
|
@ -620,14 +615,14 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
if (this.featureSwitches.featureSwitchFilter.data) {
|
if (this.featureSwitches.featureSwitchFilter.data) {
|
||||||
this.guistate.openFilterView()
|
this.guistate.openFilterView()
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
Hotkeys.RegisterHotkey(
|
Hotkeys.RegisterHotkey(
|
||||||
{ shift: "O" },
|
{ shift: "O" },
|
||||||
Translations.t.hotkeyDocumentation.selectMapnik,
|
Translations.t.hotkeyDocumentation.selectMapnik,
|
||||||
() => {
|
() => {
|
||||||
this.mapProperties.rasterLayer.setData(AvailableRasterLayers.osmCarto)
|
this.mapProperties.rasterLayer.setData(AvailableRasterLayers.osmCarto)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
const setLayerCategory = (category: EliCategory) => {
|
const setLayerCategory = (category: EliCategory) => {
|
||||||
const available = this.availableLayers.data
|
const available = this.availableLayers.data
|
||||||
|
@ -635,7 +630,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
const best = RasterLayerUtils.SelectBestLayerAccordingTo(
|
const best = RasterLayerUtils.SelectBestLayerAccordingTo(
|
||||||
available,
|
available,
|
||||||
category,
|
category,
|
||||||
current.data
|
current.data,
|
||||||
)
|
)
|
||||||
console.log("Best layer for category", category, "is", best.properties.id)
|
console.log("Best layer for category", category, "is", best.properties.id)
|
||||||
current.setData(best)
|
current.setData(best)
|
||||||
|
@ -644,26 +639,26 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
Hotkeys.RegisterHotkey(
|
Hotkeys.RegisterHotkey(
|
||||||
{ nomod: "O" },
|
{ nomod: "O" },
|
||||||
Translations.t.hotkeyDocumentation.selectOsmbasedmap,
|
Translations.t.hotkeyDocumentation.selectOsmbasedmap,
|
||||||
() => setLayerCategory("osmbasedmap")
|
() => setLayerCategory("osmbasedmap"),
|
||||||
)
|
)
|
||||||
|
|
||||||
Hotkeys.RegisterHotkey(
|
Hotkeys.RegisterHotkey(
|
||||||
{ nomod: "M" },
|
{ nomod: "M" },
|
||||||
Translations.t.hotkeyDocumentation.selectMap,
|
Translations.t.hotkeyDocumentation.selectMap,
|
||||||
() => setLayerCategory("map")
|
() => setLayerCategory("map"),
|
||||||
)
|
)
|
||||||
|
|
||||||
Hotkeys.RegisterHotkey(
|
Hotkeys.RegisterHotkey(
|
||||||
{ nomod: "P" },
|
{ nomod: "P" },
|
||||||
Translations.t.hotkeyDocumentation.selectAerial,
|
Translations.t.hotkeyDocumentation.selectAerial,
|
||||||
() => setLayerCategory("photo")
|
() => setLayerCategory("photo"),
|
||||||
)
|
)
|
||||||
Hotkeys.RegisterHotkey(
|
Hotkeys.RegisterHotkey(
|
||||||
{ nomod: "L" },
|
{ nomod: "L" },
|
||||||
Translations.t.hotkeyDocumentation.geolocate,
|
Translations.t.hotkeyDocumentation.geolocate,
|
||||||
() => {
|
() => {
|
||||||
this.geolocationControl.handleClick()
|
this.geolocationControl.handleClick()
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
@ -675,7 +670,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
Translations.t.hotkeyDocumentation.translationMode,
|
Translations.t.hotkeyDocumentation.translationMode,
|
||||||
() => {
|
() => {
|
||||||
Locale.showLinkToWeblate.setData(!Locale.showLinkToWeblate.data)
|
Locale.showLinkToWeblate.setData(!Locale.showLinkToWeblate.data)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -686,7 +681,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
const normalLayers = this.layout.layers.filter(
|
const normalLayers = this.layout.layers.filter(
|
||||||
(l) =>
|
(l) =>
|
||||||
Constants.priviliged_layers.indexOf(<any>l.id) < 0 &&
|
Constants.priviliged_layers.indexOf(<any>l.id) < 0 &&
|
||||||
!l.id.startsWith("note_import")
|
!l.id.startsWith("note_import"),
|
||||||
)
|
)
|
||||||
const maxzoom = Math.min(...normalLayers.map((l) => l.minzoom))
|
const maxzoom = Math.min(...normalLayers.map((l) => l.minzoom))
|
||||||
|
|
||||||
|
@ -694,7 +689,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
(l) =>
|
(l) =>
|
||||||
Constants.priviliged_layers.indexOf(<any>l.id) < 0 &&
|
Constants.priviliged_layers.indexOf(<any>l.id) < 0 &&
|
||||||
l.source.geojsonSource === undefined &&
|
l.source.geojsonSource === undefined &&
|
||||||
l.doCount
|
l.doCount,
|
||||||
)
|
)
|
||||||
const summaryTileSource = new SummaryTileSource(
|
const summaryTileSource = new SummaryTileSource(
|
||||||
Constants.SummaryServer,
|
Constants.SummaryServer,
|
||||||
|
@ -703,7 +698,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
this.mapProperties,
|
this.mapProperties,
|
||||||
{
|
{
|
||||||
isActive: this.mapProperties.zoom.map((z) => z <= maxzoom),
|
isActive: this.mapProperties.zoom.map((z) => z <= maxzoom),
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
return new SummaryTileSourceRewriter(summaryTileSource, this.layerState.filteredLayers)
|
return new SummaryTileSourceRewriter(summaryTileSource, this.layerState.filteredLayers)
|
||||||
}
|
}
|
||||||
|
@ -723,12 +718,12 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
gps_location_history: this.geolocation.historicalUserLocations,
|
gps_location_history: this.geolocation.historicalUserLocations,
|
||||||
gps_track: this.geolocation.historicalUserLocationsTrack,
|
gps_track: this.geolocation.historicalUserLocationsTrack,
|
||||||
selected_element: new StaticFeatureSource(
|
selected_element: new StaticFeatureSource(
|
||||||
this.selectedElement.map((f) => (f === undefined ? empty : [f]))
|
this.selectedElement.map((f) => (f === undefined ? empty : [f])),
|
||||||
),
|
),
|
||||||
range: new StaticFeatureSource(
|
range: new StaticFeatureSource(
|
||||||
this.mapProperties.maxbounds.map((bbox) =>
|
this.mapProperties.maxbounds.map((bbox) =>
|
||||||
bbox === undefined ? empty : <Feature[]>[bbox.asGeoJson({ id: "range" })]
|
bbox === undefined ? empty : <Feature[]>[bbox.asGeoJson({ id: "range" })],
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
current_view: this.currentView,
|
current_view: this.currentView,
|
||||||
favourite: this.favourites,
|
favourite: this.favourites,
|
||||||
|
@ -743,7 +738,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
ShowDataLayer.showRange(
|
ShowDataLayer.showRange(
|
||||||
this.map,
|
this.map,
|
||||||
new StaticFeatureSource([bbox.asGeoJson({ id: "range" })]),
|
new StaticFeatureSource([bbox.asGeoJson({ id: "range" })]),
|
||||||
this.featureSwitches.featureSwitchIsTesting
|
this.featureSwitches.featureSwitchIsTesting,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const currentViewLayer = this.layout.layers.find((l) => l.id === "current_view")
|
const currentViewLayer = this.layout.layers.find((l) => l.id === "current_view")
|
||||||
|
@ -757,7 +752,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
currentViewLayer,
|
currentViewLayer,
|
||||||
this.layout,
|
this.layout,
|
||||||
this.osmObjectDownloader,
|
this.osmObjectDownloader,
|
||||||
this.featureProperties
|
this.featureProperties,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -801,20 +796,20 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
|
|
||||||
const lastClickLayerConfig = new LayerConfig(
|
const lastClickLayerConfig = new LayerConfig(
|
||||||
<LayerConfigJson>last_click_layerconfig,
|
<LayerConfigJson>last_click_layerconfig,
|
||||||
"last_click"
|
"last_click",
|
||||||
)
|
)
|
||||||
const lastClickFiltered =
|
const lastClickFiltered =
|
||||||
lastClickLayerConfig.isShown === undefined
|
lastClickLayerConfig.isShown === undefined
|
||||||
? specialLayers.last_click
|
? specialLayers.last_click
|
||||||
: specialLayers.last_click.features.mapD((fs) =>
|
: specialLayers.last_click.features.mapD((fs) =>
|
||||||
fs.filter((f) => {
|
fs.filter((f) => {
|
||||||
const matches = lastClickLayerConfig.isShown.matchesProperties(
|
const matches = lastClickLayerConfig.isShown.matchesProperties(
|
||||||
f.properties
|
f.properties,
|
||||||
)
|
)
|
||||||
console.log("LastClick ", f, "matches", matches)
|
console.log("LastClick ", f, "matches", matches)
|
||||||
return matches
|
return matches
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
new ShowDataLayer(this.map, {
|
new ShowDataLayer(this.map, {
|
||||||
features: new StaticFeatureSource(lastClickFiltered),
|
features: new StaticFeatureSource(lastClickFiltered),
|
||||||
layer: lastClickLayerConfig,
|
layer: lastClickLayerConfig,
|
||||||
|
@ -859,7 +854,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
this.mapProperties.rasterLayer,
|
this.mapProperties.rasterLayer,
|
||||||
this.availableLayers,
|
this.availableLayers,
|
||||||
this.featureSwitches.backgroundLayerId,
|
this.featureSwitches.backgroundLayerId,
|
||||||
this.userRelatedState.preferredBackgroundLayer
|
this.userRelatedState.preferredBackgroundLayer,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -867,4 +862,21 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
this.guistate.closeAll()
|
this.guistate.closeAll()
|
||||||
this.selectedElement.setData(this.currentView.features?.data?.[0])
|
this.selectedElement.setData(this.currentView.features?.data?.[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async reportError(message: string | Error) {
|
||||||
|
console.log(">>> Reporting error to",Constants.ErrorReportServer, message)
|
||||||
|
let stacktrace: string = new Error().stack
|
||||||
|
|
||||||
|
await fetch(Constants.ErrorReportServer, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
stacktrace,
|
||||||
|
message: ""+message,
|
||||||
|
layout: this.layout.id,
|
||||||
|
username: this.osmConnection.userDetails.data?.name,
|
||||||
|
userid: this.osmConnection.userDetails.data?.uid,
|
||||||
|
pendingChanges: this.changes.pendingChanges.data,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
import Tr from "./Tr.svelte"
|
import Tr from "./Tr.svelte"
|
||||||
import { ImmutableStore, UIEventSource } from "../../Logic/UIEventSource"
|
import { ImmutableStore, UIEventSource } from "../../Logic/UIEventSource"
|
||||||
import Invalid from "../../assets/svg/Invalid.svelte"
|
import Invalid from "../../assets/svg/Invalid.svelte"
|
||||||
|
import ArrowPath from "@babeard/svelte-heroicons/mini/ArrowPath"
|
||||||
|
|
||||||
export let state: {
|
export let state: {
|
||||||
osmConnection: OsmConnection
|
osmConnection: OsmConnection
|
||||||
|
@ -40,9 +41,13 @@
|
||||||
</slot>
|
</slot>
|
||||||
{:else if !silentFail && $loadingStatus === "error"}
|
{:else if !silentFail && $loadingStatus === "error"}
|
||||||
<slot name="error">
|
<slot name="error">
|
||||||
<div class="alert max-w-64 flex items-center">
|
<div class="alert max-w-64 flex items-center ">
|
||||||
<Invalid class="m-2 h-8 w-8 shrink-0" />
|
<Invalid class="m-2 h-8 w-8 shrink-0" />
|
||||||
<Tr t={offlineModes[$apiState]} />
|
<Tr t={offlineModes[$apiState] ?? t.loginFailedOfflineOther} />
|
||||||
|
<button class="justify-self-end" on:click={() => state.osmConnection.AttemptLogin()}>
|
||||||
|
<ArrowPath class="w-6 h-6"/>
|
||||||
|
Retry
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</slot>
|
</slot>
|
||||||
{:else if $loadingStatus === "logged-in"}
|
{:else if $loadingStatus === "logged-in"}
|
||||||
|
|
|
@ -95,6 +95,7 @@ export interface SpecialVisualizationState {
|
||||||
readonly geolocation: GeoLocationHandler
|
readonly geolocation: GeoLocationHandler
|
||||||
|
|
||||||
showCurrentLocationOn(map: Store<MlMap>): ShowDataLayer
|
showCurrentLocationOn(map: Store<MlMap>): ShowDataLayer
|
||||||
|
reportError(message: string): Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SpecialVisualization {
|
export interface SpecialVisualization {
|
||||||
|
|
Loading…
Reference in a new issue