Merge branch 'develop'

This commit is contained in:
Pieter Vander Vennet 2024-06-16 13:06:42 +02:00
commit cf3ca610b0
41 changed files with 334 additions and 195 deletions

View file

@ -1,14 +1,4 @@
cache.mapcomplete.org {
reverse_proxy /summary/* {
to http://127.0.0.1:2345
}
reverse_proxy /extractgraph {
to http://127.0.0.1:2346
}
reverse_proxy /* {
to http://127.0.0.1:7800
}
reverse_proxy /summary/* 127.0.0.1:2345
reverse_proxy /* 127.0.0.1:7800
}

View file

@ -10,6 +10,6 @@ https://dynamicdns.park-your-domain.com/update?host=cache&domain=mapcomplete.org
## Setup
See SettingUpPSQL.md
See [SettingUpPSQL.md](../SettingUpPSQL.md)

View file

@ -3,7 +3,6 @@ hosted.mapcomplete.org {
file_server
header {
+Permissions-Policy "interest-cohort=()"
+Report-To `\{"group":"csp-endpoint", "max_age": 86400,"endpoints": [\{"url": "https://report.mapcomplete.org/csp"}], "include_subdomains": true}`
}
}
@ -11,12 +10,11 @@ countrycoder.mapcomplete.org {
root * tiles/
file_server
header {
+Permissions-Policy "interest-cohort=()"
+Access-Control-Allow-Origin https://hosted.mapcomplete.org https://dev.mapcomplete.org https://mapcomplete.org
}
+Permissions-Policy "interest-cohort=()"
+Access-Control-Allow-Origin https://hosted.mapcomplete.org https://dev.mapcomplete.org https://mapcomplete.org
}
}
report.mapcomplete.org {
reverse_proxy {
to http://127.0.0.1:2600
@ -30,25 +28,7 @@ studio.mapcomplete.org {
}
lod.mapcomplete.org {
reverse_proxy /extractgraph {
to http://127.0.0.1:2346
}
}
proxy.mapcomplete.org {
reverse_proxy {
to http://127.0.0.1:1237
}
}
bounce.mapcomplete.org {
reverse_proxy {
to http://127.0.0.1:1236
}
}
cache.mapcomplete.org {
reverse_proxy /summary/* {
to http://127.0.0.1:2345
}
reverse_proxy /extractgraph {
to http://127.0.0.1:2346
}
}

View file

@ -59,11 +59,13 @@ HP ProLiant DL360 G7 (1U): 2Rx4 DDR3-memory (PC3)
## Deploying a tile server
pg_tileserv kan hier gedownload worden: https://github.com/CrunchyData/pg_tileserv
pg_tileserv can be downloaded here: https://github.com/CrunchyData/pg_tileserv
In the directory where it is downloaded (e.g. `~/data`), run
````
export DATABASE_URL=postgresql://user:password@localhost:5444/osm-poi
nohup ./pg_tileserv &
nohup ./pg_tileserv > pg_tileserv.log &
````
Tiles are available at:
@ -74,6 +76,10 @@ map.addSource("drinking_water", {
})
````
# Starting the summary server
`npm run summary-server` in the git repo
# Rebooting:
-> Restart the docker container

View file

@ -201,7 +201,7 @@
},
{
"#": "ignore-image-in-then",
"if": "context:website~*",
"if": "contact:website~*",
"then": "<a href='{contact:website}' target='_blank' rel='noopener'><img textmode='🌐' alt='website' src='./assets/layers/icons/website.svg'/></a>"
}
]

View file

@ -364,5 +364,8 @@
}
}
],
"allowMove": true
"allowMove": true,
"deletion": {
"neededChangesets": 0
}
}

View file

@ -426,6 +426,36 @@
}
]
},
{
"id": "copyshop-binding",
"condition": {
"or": [
"shop~.*copyshop.*",
"shop~.*stationery.*",
"service:print=yes"
]
},
"question": {
"en": "Does this shop offer a binding service?"
},
"questionHint": {
"en": "Does this shop bind a bundle of pages into a small book, e.g. with a comb, a spiral, wire or by gluing?"
},
"mappings": [
{
"if": "service:binding=yes",
"then": {
"en": "This shop binds papers into a booklet"
}
},
{
"if": "service:binding=no",
"then": {
"en": "This shop does bind books"
}
}
]
},
{
"id": "key_cutter",
"question": {

56
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "mapcomplete",
"version": "0.42.6",
"version": "0.43.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "mapcomplete",
"version": "0.42.6",
"version": "0.43.1",
"license": "GPL-3.0-or-later",
"dependencies": {
"@comunica/core": "^3.0.1",
@ -7682,10 +7682,11 @@
}
},
"node_modules/braces": {
"version": "3.0.2",
"license": "MIT",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dependencies": {
"fill-range": "^7.0.1"
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
@ -9756,8 +9757,9 @@
}
},
"node_modules/fill-range": {
"version": "7.0.1",
"license": "MIT",
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dependencies": {
"to-regex-range": "^5.0.1"
},
@ -10770,6 +10772,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/is-path-inside": {
"version": "3.0.3",
"dev": true,
@ -17710,7 +17720,8 @@
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"license": "MIT",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dependencies": {
"is-number": "^7.0.0"
},
@ -17718,13 +17729,6 @@
"node": ">=8.0"
}
},
"node_modules/to-regex-range/node_modules/is-number": {
"version": "7.0.0",
"license": "MIT",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/topojson-client": {
"version": "3.1.0",
"license": "ISC",
@ -24931,9 +24935,11 @@
}
},
"braces": {
"version": "3.0.2",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"requires": {
"fill-range": "^7.0.1"
"fill-range": "^7.1.1"
}
},
"brorand": {
@ -26246,7 +26252,9 @@
}
},
"fill-range": {
"version": "7.0.1",
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"requires": {
"to-regex-range": "^5.0.1"
}
@ -26866,6 +26874,11 @@
"define-properties": "^1.1.3"
}
},
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
},
"is-path-inside": {
"version": "3.0.3",
"dev": true
@ -31326,13 +31339,10 @@
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"requires": {
"is-number": "^7.0.0"
},
"dependencies": {
"is-number": {
"version": "7.0.0"
}
}
},
"topojson-client": {

View file

@ -22,6 +22,8 @@
"url": "https://www.openstreetmap.org"
},
"mvt_layer_server": "https://cache.mapcomplete.org/public.{type}_{layer}/{z}/{x}/{y}.pbf",
"#summary_server": "Should be the endpoint; appending status.json should work",
"summary_server": "https://cache.mapcomplete.org/",
"disabled:oauth_credentials": {
"##": "DEV",
"#": "This client-id is registered by 'MapComplete' on https://master.apis.dev.openstreetmap.org/",

View file

@ -5,6 +5,7 @@ import Script from "./Script"
import { GeoOperations } from "../src/Logic/GeoOperations"
import { Feature, Polygon } from "geojson"
import { Tiles } from "../src/Models/TileRange"
import { BBox } from "../src/Logic/BBox"
class StatsDownloader {
private readonly urlTemplate =
@ -123,7 +124,7 @@ class StatsDownloader {
Referer:
"https://osmcha.org/?filters=%7B%22date__gte%22%3A%5B%7B%22label%22%3A%222020-07-05%22%2C%22value%22%3A%222020-07-05%22%7D%5D%2C%22editor%22%3A%5B%7B%22label%22%3A%22mapcomplete%22%2C%22value%22%3A%22mapcomplete%22%7D%5D%7D",
"Content-Type": "application/json",
Authorization: "Token 6e422e2afedb79ef66573982012000281f03dc91",
Authorization: "Token 9cc11ad2868778272eadbb1a423ebb507184bc04",
DNT: "1",
Connection: "keep-alive",
TE: "Trailers",
@ -135,7 +136,7 @@ class StatsDownloader {
ScriptUtils.erasableLog(
`Downloading stats for ${year}-${month}-${day}, page ${page} ${url}`
)
const result = await Utils.downloadJson(url, headers)
const result = await Utils.downloadJson<{features: [], next: string}>(url, headers)
page++
allFeatures.push(...result.features)
if (result.features === undefined) {
@ -201,11 +202,11 @@ class GenerateSeries extends Script {
const targetDir = args[0] ?? "../../git/MapComplete-data"
await this.downloadStatistics(targetDir + "/changeset-metadata")
await this.generateCenterPoints(
this.generateCenterPoints(
targetDir + "/changeset-metadata",
targetDir + "/mapcomplete-changes/",
{
zoomlevel: 8,
zoomlevel: 8
}
)
}
@ -248,10 +249,8 @@ class GenerateSeries extends Script {
const allPaths = readdirSync(sourceDir).filter(
(p) => p.startsWith("stats.") && p.endsWith(".json")
)
let allFeatures: ChangeSetData[] = [].concat(
...allPaths.map(
let allFeatures: ChangeSetData[] = allPaths.flatMap(
(path) => JSON.parse(readFileSync(sourceDir + "/" + path, "utf-8")).features
)
)
allFeatures = allFeatures.filter(
(f) =>
@ -270,8 +269,24 @@ class GenerateSeries extends Script {
f.properties.editor.toLowerCase().startsWith("mapcomplete"))
)
allFeatures = allFeatures.filter((f) => f.properties.metadata?.theme !== "EMPTY CS")
const centerpoints = allFeatures.map((f) => GeoOperations.centerpoint(f))
allFeatures = allFeatures.filter((f) => f.properties.metadata?.theme !== "EMPTY CS" && f.geometry.coordinates.length > 0)
const centerpointsAll = allFeatures.map((f) => {
const centerpoint = GeoOperations.centerpoint(f)
const c = centerpoint.geometry.coordinates
// OsmCha doesn't adhere to the Geojson standard and uses `lat` `lon` as coordinates instead of `lon`, `lat`
centerpoint.geometry.coordinates = [c[1], c[0]]
return centerpoint
})
const centerpoints = centerpointsAll.filter(p => {
const bbox= BBox.get(p)
if(bbox.minLat === -90 && bbox.maxLat === -90){
// Due to some bug somewhere, those invalid bboxes might appear if the latitude is < 90
// This crashes the 'spreadIntoBBoxes
// As workaround, we simply ignore them for now
return false
}
return true
})
console.log("Found", centerpoints.length, " changesets in total")
const perBbox = GeoOperations.spreadIntoBboxes(centerpoints, options.zoomlevel)

View file

@ -392,6 +392,8 @@ class GenerateLayouts extends Script {
}
}
hosts.add("http://www.schema.org") // Schema.org is _not_ encrypted and thus needs an exception
if (hosts.has("*")) {
throw "* is not allowed as connect-src"
}

View file

@ -65,6 +65,10 @@ class Compare extends Script {
async main(args: string[]): Promise<void> {
let [velopark, osm, key] = args
if(velopark === undefined || osm === undefined){
console.log("Needed argument: velopark.geojson osm.geojson [key]\nThe key is optional and will be `ref:velopark` by default\nUse overpass to get a geojson with ref:velopark")
return
}
key ??= "ref:velopark"
const veloparkData: FeatureCollection = JSON.parse(fs.readFileSync(velopark, "utf-8"))
const osmData: FeatureCollection = JSON.parse(fs.readFileSync(osm, "utf-8"))
@ -82,6 +86,7 @@ class Compare extends Script {
console.error("No velopark entry found for", veloId)
continue
}
console.log("Veloparking is", veloparking)
diffs.push(this.compare(veloId, parking, veloparking))
}
console.log(

View file

@ -0,0 +1,63 @@
import Script from "../Script"
import { readFileSync, writeFileSync } from "fs"
import { OsmId } from "../../src/Models/OsmFeature"
import { Utils } from "../../src/Utils"
interface DiffItem {
/**
* Velopark-id
*/
"ref": string,
"osmid": OsmId,
"distance": number,
"diffs": {
key: string,
/**
* The value in OpenStreetMap
* Might be undefined if OSM doesn't have an appropriate value
*/
osm?: string,
velopark: string | number } []
}
export class DiffToCsv extends Script {
constructor() {
super("Converts a 'report.diff' to a CSV file for people who prefer LibreOffice Calc (or other Spreadsheet Software)")
}
async main(args: string[]): Promise<void> {
const file = args[0] ?? "report_diff.json"
const json = <{diffs:DiffItem[], distanceBinds: number[]}> JSON.parse(readFileSync(file, "utf8"))
const diffs = json.diffs
const allKeys = Utils.Dedup(diffs.flatMap(item => item.diffs.map(d => d.key)))
allKeys.sort()
const header = ["osm_id","velopark_id", "distance",...allKeys.flatMap(k => ["osm:"+k, "velopark:"+k])]
const lines = [header]
for (const diffItem of diffs) {
const line = []
lines.push(line)
line.push(diffItem.osmid)
line.push(diffItem.ref)
line.push(diffItem.distance)
const d = diffItem.diffs
for (const k of allKeys) {
const found = d.find(i => i.key === k)
if(!found){
line.push("","")
continue
}
line.push(found.osm, found.velopark)
}
}
const path = "report_diff.csv"
writeFileSync(path,
lines.map(l => l.join(",")).join("\n")
,"utf8")
console.log("Written", path)
}
}
new DiffToCsv().run()

View file

@ -23,9 +23,9 @@ class VeloParkToGeojson extends Script {
JSON.stringify(
extension === ".geojson"
? {
type: "FeatureCollection",
features,
}
type: "FeatureCollection",
features
}
: features,
null,
" "
@ -34,34 +34,14 @@ class VeloParkToGeojson extends Script {
console.log("Written", file, "(" + features.length, " features)")
}
public static sumProperties(data: object, addTo: Record<string, Set<string>>) {
delete data["@context"]
for (const k in data) {
if (k === "@graph") {
for (const obj of data["@graph"]) {
this.sumProperties(obj, addTo)
}
continue
}
if (addTo[k] === undefined) {
addTo[k] = new Set<string>()
}
addTo[k].add(data[k])
}
}
private static async downloadDataFor(
url: string,
allProperties: Record<string, Set<string>>
url: string
): Promise<Feature[]> {
const cachePath = "/home/pietervdvn/data/velopark_cache/" + url.replace(/[/:.]/g, "_")
if (!fs.existsSync(cachePath)) {
const data = await Utils.downloadJson(url)
fs.writeFileSync(cachePath, JSON.stringify(data), "utf-8")
console.log("Saved a backup to", cachePath)
const cachePath = "/home/pietervdvn/data/velopark_cache_refined/" + url.replace(/[/:.]/g, "_")
if (fs.existsSync(cachePath)) {
return JSON.parse(fs.readFileSync(cachePath, "utf8"))
}
this.sumProperties(JSON.parse(fs.readFileSync(cachePath, "utf-8")), allProperties)
console.log("Fetching data for", url)
const linkedData = await LinkedDataLoader.fetchVeloparkEntry(url)
const allVelopark: Feature[] = []
@ -70,9 +50,12 @@ class VeloParkToGeojson extends Script {
if (Object.keys(sectionInfo).length === 0) {
console.warn("No result for", url)
}
sectionInfo["ref:velopark"] = [sectionId ?? url]
if(!sectionInfo.geometry?.coordinates){
throw "Invalid properties!"
}
allVelopark.push(sectionInfo)
}
fs.writeFileSync(cachePath, JSON.stringify(allVelopark), "utf8")
return allVelopark
}
@ -85,20 +68,24 @@ class VeloParkToGeojson extends Script {
let failed = 0
console.log("Got", allVeloparkRaw.length, "items")
const allVelopark: Feature[] = []
const allProperties = {}
for (let i = 0; i < allVeloparkRaw.length; i++) {
const f = allVeloparkRaw[i]
console.log("Handling", i + "/" + allVeloparkRaw.length)
try {
const sections: Feature[] = await VeloParkToGeojson.downloadDataFor(
f.url,
allProperties
)
allVelopark.push(...sections)
} catch (e) {
console.error("Loading ", f.url, " failed due to", e)
failed++
}
const batchSize = 50
for (let i = 0; i < allVeloparkRaw.length; i += batchSize) {
await Promise.all(Utils.TimesT(batchSize, j => j).map(
async j => {
const f = allVeloparkRaw[i + j]
if (!f) {
return
}
try {
const sections: Feature[] = await VeloParkToGeojson.downloadDataFor(f.url)
allVelopark.push(...sections)
} catch (e) {
console.error("Loading ", f.url, " failed due to", e)
failed++
}
}
))
}
console.log(
"Fetching data done, got ",
@ -107,11 +94,6 @@ class VeloParkToGeojson extends Script {
failed
)
VeloParkToGeojson.exportGeojsonTo("velopark_all", allVelopark)
for (const k in allProperties) {
allProperties[k] = Array.from(allProperties[k])
}
fs.writeFileSync("all_properties_mashup.json", JSON.stringify(allProperties, null, " "))
return allVelopark
}
@ -158,7 +140,7 @@ class VeloParkToGeojson extends Script {
private static async createDiff(allVelopark: Feature[]) {
const bboxBelgium = new BBox([
[2.51357303225, 49.5294835476],
[6.15665815596, 51.4750237087],
[6.15665815596, 51.4750237087]
])
const alreadyLinkedQuery = new Overpass(
@ -201,7 +183,7 @@ class VeloParkToGeojson extends Script {
VeloParkToGeojson.exportExtraAmenities(allVelopark)
await VeloParkToGeojson.createDiff(allVelopark)
console.log(
"Use vite-node script/velopark/compare to compare the results and generate a diff file"
"Use vite-node scripts/velopark/compare.ts to compare the results and generate a diff file"
)
}
}

View file

@ -16,6 +16,18 @@ export class BBox {
/***
* Coordinates should be [[lon, lat],[lon, lat]]
* @param coordinates
*
* const bb = new BBox([[5,6]])
* bb.minLat // => 6
* bb.minLon // => 5
* bb.maxLat // => 6
* bb.maxLon // => 5
*
* const bb = new BBox([[-100,-100]])
* bb.minLat // => -90
* bb.minLon // => -100
* bb.maxLat // => -90
* bb.maxLon // => -100
*/
constructor(coordinates: [number, number][]) {
this.maxLat = -90
@ -45,13 +57,24 @@ export class BBox {
])
}
/**
* Gets the bbox from a feature and caches it (by monkeypatching) the relevant feature
*
*
* const f = {type:"Feature",properties: {}, geometry: {type: "Point", coordinates: [-100,45]}}
* const bb = BBox.get(f)
* bb.minLat // => 45
* bb.minLon // => -100
*
*/
static get(feature: Feature): BBox {
const f = <any>feature
if (f.bbox?.overlapsWith === undefined) {
const turfBbox: number[] = turf.bbox(feature)
const [minX, minY, maxX, maxY]: number[] = turf.bbox(feature)
// Note: x is longitude
f["bbox"] = new BBox([
[turfBbox[0], turfBbox[1]],
[turfBbox[2], turfBbox[3]],
[minX, minY],
[maxX, maxY],
])
}
return f["bbox"]

View file

@ -26,7 +26,6 @@ export default class LayoutSource extends FeatureSourceMerger {
private readonly supportsForceDownload: UpdatableFeatureSource[]
private readonly fromCache: Map<string, LocalStorageFeatureSource>
public static readonly fromCacheZoomLevel = 15
constructor(
layers: LayerConfig[],

View file

@ -734,7 +734,6 @@ export class GeoOperations {
const splitted: Feature<LineString>[] = [].concat(...lines)
const kept: Feature<LineString>[] = []
for (const f of splitted) {
console.log("Checking", f)
if (!GeoOperations.inside(GeoOperations.centerpointCoordinates(f), boundary)) {
continue
}

View file

@ -316,14 +316,16 @@ export default class LinkedDataLoader {
input: Record<string, Set<string>>
): Record<string, string[]> {
const output: Record<string, string[]> = {}
console.log("Input for patchVelopark:", input)
for (const k in input) {
output[k] = Array.from(input[k])
}
if (output["type"][0] === "https://data.velopark.be/openvelopark/terms#BicycleLocker") {
if (output["type"]?.[0] === "https://data.velopark.be/openvelopark/terms#BicycleLocker") {
output["bicycle_parking"] = ["lockers"]
}
if(output["type"] === undefined){
console.error("No type given for", output)
}
delete output["type"]
function on(key: string, applyF: (s: string) => string) {
@ -506,7 +508,6 @@ export default class LinkedDataLoader {
" ?parking a <http://schema.mobivoc.org/BicycleParkingStation>",
"?parking " + property + " " + (variable ?? "")
)
console.log("Fetching a velopark property gave", property, results)
return results
}

View file

@ -164,6 +164,7 @@ export default class Constants {
*/
public static VectorTileServer: string | undefined = Constants.config.mvt_layer_server
public static readonly maptilerApiKey = "GvoVAJgu46I5rZapJuAy"
public static readonly SummaryServer: string = Constants.config.summary_server
private static isRetina(): boolean {
if (Utils.runningFromConsole) {

View file

@ -80,11 +80,14 @@ export class AvailableRasterLayers {
return GeoOperations.inside(lonlat, eliPolygon)
})
matching.unshift(AvailableRasterLayers.osmCarto)
matching.push(AvailableRasterLayers.defaultBackgroundLayer)
if (enableBing?.data) {
matching.push(AvailableRasterLayers.bing)
}
matching.push(...AvailableRasterLayers.globalLayers)
if(!matching.some(l => l.id === AvailableRasterLayers.defaultBackgroundLayer.properties.id)){
matching.push(AvailableRasterLayers.defaultBackgroundLayer)
}
return matching
},
[enableBing]

View file

@ -690,9 +690,8 @@ export default class ThemeViewState implements SpecialVisualizationState {
l.source.geojsonSource === undefined &&
l.doCount
)
const url = new URL(Constants.VectorTileServer)
const summaryTileSource = new SummaryTileSource(
url.protocol + "//" + url.host + "/summary",
Constants.SummaryServer,
layers.map((l) => l.id),
this.mapProperties.zoom.map((z) => Math.max(Math.floor(z), 0)),
this.mapProperties,

View file

@ -1,4 +1,5 @@
import SvelteUIElement from "./UI/Base/SvelteUIElement"
import StylesheetTestGui from "./UI/StylesheetTestGui.svelte"
new SvelteUIElement(StylesheetTestGui, {}).AttachTo("main")
new StylesheetTestGui({
target: document.getElementById("maindiv")
})

View file

@ -65,6 +65,7 @@
}
</script>
<main>
<div class="m-4 flex flex-col">
<LanguagePicker
clss="self-end max-w-full"
@ -165,3 +166,4 @@
v{Constants.vNumber}
</div>
</div>
</main>

View file

@ -16,6 +16,10 @@
* If set, 'loading' will act as if we are already logged in.
*/
export let ignoreLoading: boolean = false
/**
* Only show the 'successful' state, don't show loading or error messages
*/
export let silentFail : boolean = false
let loadingStatus = state?.osmConnection?.loadingStatus ?? new ImmutableStore("logged-in")
let badge = state?.featureSwitches?.featureSwitchEnableLogin ?? new ImmutableStore(true)
const t = Translations.t.general
@ -30,11 +34,11 @@
</script>
{#if $badge}
{#if !ignoreLoading && $loadingStatus === "loading"}
{#if !ignoreLoading && !silentFail && $loadingStatus === "loading"}
<slot name="loading">
<Loading />
</slot>
{:else if $loadingStatus === "error"}
{:else if !silentFail && $loadingStatus === "error"}
<slot name="error">
<div class="alert max-w-64 flex items-center">
<Invalid class="m-2 h-8 w-8 shrink-0" />
@ -43,7 +47,7 @@
</slot>
{:else if $loadingStatus === "logged-in"}
<slot />
{:else if $loadingStatus === "not-attempted"}
{:else if !silentFail && $loadingStatus === "not-attempted"}
<slot name="not-logged-in" />
{/if}
{/if}

View file

@ -13,6 +13,7 @@
import type { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider"
import AttributedImage from "./AttributedImage.svelte"
import SpecialTranslation from "../Popup/TagRendering/SpecialTranslation.svelte"
import LoginToggle from "../Base/LoginToggle.svelte"
export let tags: UIEventSource<OsmTags>
export let state: SpecialVisualizationState
@ -68,10 +69,12 @@
previewedImage={state.previewedImage}
/>
</div>
<LoginToggle {state} silentFail={true} >
{#if linkable}
<label>
<input bind:checked={$isLinked} type="checkbox" />
<SpecialTranslation t={t.link} {tags} {state} {layer} {feature} />
</label>
{/if}
</LoginToggle>
</div>

View file

@ -30,7 +30,7 @@
lon,
lat,
allowSpherical: new UIEventSource<boolean>(false),
blacklist: AllImageProviders.LoadImagesFor(tags),
blacklist: AllImageProviders.LoadImagesFor(tags)
},
state.indexedFeatures
)
@ -39,26 +39,24 @@
let allDone = imagesProvider.allDone
</script>
<LoginToggle {state}>
<div class="interactive border-interactive rounded-2xl p-2">
<div class="flex justify-between">
<h4>
<Tr t={Translations.t.image.nearby.title} />
</h4>
<slot name="corner" />
</div>
{#if !$allDone}
<Loading />
{:else if $images.length === 0}
<Tr t={Translations.t.image.nearby.noNearbyImages} cls="alert" />
{:else}
<div class="flex w-full space-x-1 overflow-x-auto" style="scroll-snap-type: x proximity">
{#each $images as image (image.pictureUrl)}
<div class="interactive border-interactive rounded-2xl p-2">
<div class="flex justify-between">
<h4>
<Tr t={Translations.t.image.nearby.title} />
</h4>
<slot name="corner" />
</div>
{#if !$allDone}
<Loading />
{:else if $images.length === 0}
<Tr t={Translations.t.image.nearby.noNearbyImages} cls="alert" />
{:else}
<div class="flex w-full space-x-1 overflow-x-auto" style="scroll-snap-type: x proximity">
{#each $images as image (image.pictureUrl)}
<span class="w-fit shrink-0" style="scroll-snap-align: start">
<LinkableImage {tags} {image} {state} {feature} {layer} {linkable} />
</span>
{/each}
</div>
{/if}
</div>
</LoginToggle>
{/each}
</div>
{/if}
</div>

View file

@ -25,7 +25,6 @@
let expanded = false
</script>
<LoginToggle {state}>
<div class="my-4">
{#if expanded}
<NearbyImages {tags} {state} {lon} {lat} {feature} {linkable} {layer}>
@ -54,4 +53,3 @@
</button>
{/if}
</div>
</LoginToggle>

View file

@ -30,7 +30,7 @@
}
> = UIEventSource.FromPromise(Utils.downloadJsonCached(source))
</script>
<main>
<h1>Contributed images with MapComplete: leaderboard</h1>
{#if $data}
@ -67,3 +67,4 @@
<div>
Logged in as {$loggedInContributor}
</div>
</main>

View file

@ -5,6 +5,7 @@
console.log("???")
</script>
<main>
<div class="flex flex-col">
<Tr t={Translations.t.general["404"]} />
<BackButton
@ -18,3 +19,4 @@
</div>
</BackButton>
</div>
</main>

View file

@ -63,7 +63,7 @@
}
</script>
<span {lang}>
{#if lang === "*"}
{#each specs as specpart}
{#if typeof specpart === "string"}
<span>
@ -74,4 +74,17 @@
<ToSvelte construct={() => createVisualisation(specpart)} />
{/if}
{/each}
</span>
{:else}
<span {lang}>
{#each specs as specpart}
{#if typeof specpart === "string"}
<span>
{@html Utils.purify(Utils.SubstituteKeys(specpart, $tags)) }
<WeblateLink context={t.context} />
</span>
{:else if $tags !== undefined}
<ToSvelte construct={() => createVisualisation(specpart)} />
{/if}
{/each}
</span>
{/if}

View file

@ -16,7 +16,7 @@
userRelatedState: new UserRelatedState(osmConnection)
}
</script>
<main>
<div class="flex h-screen flex-col overflow-hidden px-4">
<div class="flex justify-between">
<h2 class="flex items-center">
@ -33,3 +33,4 @@
<Tr t={Translations.t.general.backToIndex} />
</a>
</div>
</main>

View file

@ -6,6 +6,7 @@
import { UIEventSource } from "../Logic/UIEventSource"
</script>
<main>
<div>
<h1>Stylesheet testing grounds</h1>
@ -167,3 +168,4 @@
</select>
</div>
</div>
</main>

View file

@ -1,16 +1,5 @@
<script lang="ts">
import { Translation } from "./i18n/Translation"
import LanguagePicker from "./InputElement/LanguagePicker.svelte"
import Tr from "./Base/Tr.svelte"
import ToSvelte from "./Base/ToSvelte.svelte"
const m = new Map<string, string>()
m.set("nl", "Nederlands")
const tr = Translation.fromMap(m, true)
</script>
<LanguagePicker/>
<h1>Svelte native</h1>
<Tr t={tr}/>
<h1>ToSvelte</h1>
<ToSvelte construct={tr}/>
<main>
</main>

View file

@ -203,6 +203,7 @@
</script>
<main>
<div class="absolute top-0 left-0 h-screen w-screen overflow-hidden">
<MaplibreMap map={maplibremap} mapProperties={mapproperties} />
</div>
@ -688,3 +689,4 @@
<CloseAnimation isOpened={selectedElement.map(sl => sl !== undefined && sl?.properties?.id === LastClickFeatureSource.newPointElementId)} moveTo={openNewElementButton} debug="newElement"/>
<CloseAnimation isOpened={state.guistate.filtersPanelIsOpened} moveTo={openFilterButton} debug="filter"/>
<CloseAnimation isOpened={state.guistate.backgroundLayerSelectionIsOpened} moveTo={openBackgroundButton} debug="bg"/>
</main>

View file

@ -28,4 +28,6 @@ if (layout !== "") {
)
}
new SvelteUIElement(AllThemesGui, {}).AttachTo("main")
new AllThemesGui({
target: document.getElementById("main")
})

View file

@ -28,7 +28,7 @@ async function timeout(timeMS: number): Promise<{ layers: string[] }> {
async function getAvailableLayers(): Promise<Set<string>> {
try {
const host = new URL(Constants.VectorTileServer).host
const host = new URL(Constants.SummaryServer).host
const status: { layers: string[] } = await Promise.any([
Utils.downloadJson<{layers}>("https://" + host + "/summary/status.json"),
timeout(2500),
@ -52,8 +52,10 @@ async function main() {
])
console.log("The available layers on server are", Array.from(availableLayers))
const state = new ThemeViewState(layout, availableLayers)
const main = new SvelteUIElement(ThemeViewGUI, { state })
main.AttachTo("maindiv")
new ThemeViewGUI({
target: document.getElementById("maindiv"),
props: {state}
})
Array.from(document.getElementsByClassName("delete-on-load")).forEach((el) => {
el.parentElement.removeChild(el)
})

View file

@ -1,5 +1,4 @@
import ThemeViewState from "./src/Models/ThemeViewState"
import SvelteUIElement from "./src/UI/Base/SvelteUIElement"
import ThemeViewGUI from "./src/UI/ThemeViewGUI.svelte"
import LayoutConfig from "./src/Models/ThemeConfig/LayoutConfig";
import MetaTagging from "./src/Logic/MetaTagging";
@ -47,8 +46,10 @@ async function main() {
MetaTagging.setThemeMetatagging(new ThemeMetaTagging())
// LAYOUT.ADD_LAYERS
const state = new ThemeViewState(new LayoutConfig(<any> layout), availableLayers)
const main = new SvelteUIElement(ThemeViewGUI, { state })
main.AttachTo("maindiv")
new ThemeViewGUI({
target: document.getElementById("maindiv"),
props: {state}
})
Array.from(document.getElementsByClassName("delete-on-load")).forEach(el => {
el.parentElement.removeChild(el)
})

View file

@ -1,4 +1,5 @@
import SvelteUIElement from "./UI/Base/SvelteUIElement"
import Leaderboard from "./UI/Leaderboard.svelte"
new SvelteUIElement(Leaderboard, {}).AttachTo("main")
new Leaderboard({
target: document.getElementById("main")
})

View file

@ -1,4 +1,5 @@
import SvelteUIElement from "./UI/Base/SvelteUIElement"
import NotFound from "./UI/NotFound.svelte"
new SvelteUIElement(NotFound, {}).AttachTo("maindiv")
new NotFound({
target: document.getElementById("maindiv")
})

View file

@ -1,3 +1,4 @@
import SvelteUIElement from "./UI/Base/SvelteUIElement"
import PrivacyGui from "./UI/PrivacyGui.svelte"
new SvelteUIElement(PrivacyGui, {}).AttachTo("main")
new PrivacyGui({
target: document.getElementById("main")
})

View file

@ -1,4 +1,6 @@
import SvelteUIElement from "./UI/Base/SvelteUIElement"
import Test from "./UI/Test.svelte"
new SvelteUIElement(Test).AttachTo("maindiv")
new Test({
target: document.getElementById("maindiv")
})