forked from MapComplete/MapComplete
		
	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…
	
	Add table
		Add a link
		
	
		Reference in a new issue