forked from MapComplete/MapComplete
Merge branch 'develop'
This commit is contained in:
commit
cf3ca610b0
41 changed files with 334 additions and 195 deletions
14
Docs/ServerConfig/cache/Caddyfile
vendored
14
Docs/ServerConfig/cache/Caddyfile
vendored
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -364,5 +364,8 @@
|
|||
}
|
||||
}
|
||||
],
|
||||
"allowMove": true
|
||||
"allowMove": true,
|
||||
"deletion": {
|
||||
"neededChangesets": 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
56
package-lock.json
generated
|
@ -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": {
|
||||
|
|
|
@ -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/",
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
63
scripts/velopark/diffToCsv.ts
Normal file
63
scripts/velopark/diffToCsv.ts
Normal 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()
|
|
@ -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"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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[],
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
})
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -28,4 +28,6 @@ if (layout !== "") {
|
|||
)
|
||||
}
|
||||
|
||||
new SvelteUIElement(AllThemesGui, {}).AttachTo("main")
|
||||
new AllThemesGui({
|
||||
target: document.getElementById("main")
|
||||
})
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
|
|
|
@ -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")
|
||||
})
|
||||
|
|
|
@ -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")
|
||||
})
|
||||
|
|
|
@ -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")
|
||||
})
|
||||
|
|
|
@ -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")
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue