forked from MapComplete/MapComplete
Merge develop
This commit is contained in:
commit
e1a785ba9c
34 changed files with 1406 additions and 1306 deletions
|
@ -20,7 +20,7 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"title": {
|
"title": {
|
||||||
"render": {
|
"render": {
|
||||||
"en": "Ticket Machine",
|
"en": "Ticket Machine",
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
"source": {
|
"source": {
|
||||||
"osmTags": "amenity=ticket_validator"
|
"osmTags": "amenity=ticket_validator"
|
||||||
},
|
},
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"title": {
|
"title": {
|
||||||
"render": {
|
"render": {
|
||||||
"en": "Ticket Validator",
|
"en": "Ticket Validator",
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
"osmTags": "building~*",
|
"osmTags": "building~*",
|
||||||
"maxCacheAge": 0
|
"maxCacheAge": 0
|
||||||
},
|
},
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"calculatedTags": [
|
"calculatedTags": [
|
||||||
"_surface:strict:=feat(get)('_surface')"
|
"_surface:strict:=feat(get)('_surface')"
|
||||||
],
|
],
|
||||||
|
@ -115,10 +115,23 @@
|
||||||
},
|
},
|
||||||
"maxCacheAge": 0
|
"maxCacheAge": 0
|
||||||
},
|
},
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"pointRendering": [],
|
"pointRendering": [
|
||||||
"lineRendering": [
|
{
|
||||||
{}
|
"label": {
|
||||||
|
"render": "<div style='color: black' class='rounded-full p-1 font-bold relative'>{addr:housenumber}</div>",
|
||||||
|
"condition": "addr:housenumber~*"
|
||||||
|
},
|
||||||
|
"location": [
|
||||||
|
"point",
|
||||||
|
"centroid"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": {
|
||||||
|
"render": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -142,7 +155,7 @@
|
||||||
"osmTags": "identificatie~*",
|
"osmTags": "identificatie~*",
|
||||||
"maxCacheAge": 0
|
"maxCacheAge": 0
|
||||||
},
|
},
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"calculatedTags": [
|
"calculatedTags": [
|
||||||
"_overlaps_with_buildings=overlapWith(feat)('osm:buildings').filter(f => f.feat.properties.id.indexOf('-') < 0)",
|
"_overlaps_with_buildings=overlapWith(feat)('osm:buildings').filter(f => f.feat.properties.id.indexOf('-') < 0)",
|
||||||
"_overlaps_with=feat(get)('_overlaps_with_buildings').find(f => f.overlap > 1 /* square meter */ )",
|
"_overlaps_with=feat(get)('_overlaps_with_buildings').find(f => f.overlap > 1 /* square meter */ )",
|
||||||
|
@ -309,7 +322,7 @@
|
||||||
"osmTags": "identificatie~*",
|
"osmTags": "identificatie~*",
|
||||||
"maxCacheAge": 0
|
"maxCacheAge": 0
|
||||||
},
|
},
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"calculatedTags": [
|
"calculatedTags": [
|
||||||
"_closed_osm_addr:=closest(feat)('osm:adresses').properties",
|
"_closed_osm_addr:=closest(feat)('osm:adresses').properties",
|
||||||
"_bag_obj:addr:housenumber=`${feat.properties.huisnummer}${feat.properties.huisletter}${(feat.properties.toevoeging != '') ? '-' : ''}${feat.properties.toevoeging}`",
|
"_bag_obj:addr:housenumber=`${feat.properties.huisnummer}${feat.properties.huisletter}${(feat.properties.toevoeging != '') ? '-' : ''}${feat.properties.toevoeging}`",
|
||||||
|
|
|
@ -68,7 +68,7 @@
|
||||||
{
|
{
|
||||||
"builtin": "kerbs",
|
"builtin": "kerbs",
|
||||||
"override": {
|
"override": {
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"mapRendering": [
|
"mapRendering": [
|
||||||
{
|
{
|
||||||
"iconBadges": [
|
"iconBadges": [
|
||||||
|
|
|
@ -468,7 +468,10 @@
|
||||||
"guidepost"
|
"guidepost"
|
||||||
],
|
],
|
||||||
"override": {
|
"override": {
|
||||||
"minzoom": 15
|
"minzoom": 15,
|
||||||
|
"mapRendering": [{
|
||||||
|
"iconSize": "30,30"
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
14
assets/themes/guideposts/guideposts.json
Normal file
14
assets/themes/guideposts/guideposts.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"id": "guideposts",
|
||||||
|
"title": {
|
||||||
|
"en": "Guideposts"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"en": "Guideposts (also known as fingerposts or finger posts) are often found along official hiking, cycling, skiing or horseback riding routes to indicate the directions to different destinations. Additionally, they are often named after a region or place and show the altitude.\n\nThe position of a signpost can be used by a hiker/biker/rider/skier as a confirmation of the current position, especially if they use a printed map without a GPS receiver. "
|
||||||
|
},
|
||||||
|
"icon": "./assets/layers/guidepost/guidepost.svg",
|
||||||
|
"startZoom": 2,
|
||||||
|
"layers": [
|
||||||
|
"guidepost"
|
||||||
|
]
|
||||||
|
}
|
|
@ -111,7 +111,7 @@
|
||||||
"=presets": [],
|
"=presets": [],
|
||||||
"=name": null,
|
"=name": null,
|
||||||
"override": {
|
"override": {
|
||||||
"minzoom": 19
|
"minzoom": 18
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
{
|
{
|
||||||
"builtin": "indoors",
|
"builtin": "indoors",
|
||||||
"override": {
|
"override": {
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"name": null,
|
"name": null,
|
||||||
"passAllFeatures": true
|
"passAllFeatures": true
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@
|
||||||
"name": null,
|
"name": null,
|
||||||
"tagRendering": null,
|
"tagRendering": null,
|
||||||
"title": "null",
|
"title": "null",
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"shownByDefault": false
|
"shownByDefault": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -71,7 +71,7 @@
|
||||||
{
|
{
|
||||||
"builtin": "entrance",
|
"builtin": "entrance",
|
||||||
"override": {
|
"override": {
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"mapRendering": [
|
"mapRendering": [
|
||||||
{
|
{
|
||||||
"icon": "circle:white;./assets/themes/onwheels/entrance.svg"
|
"icon": "circle:white;./assets/themes/onwheels/entrance.svg"
|
||||||
|
@ -131,7 +131,7 @@
|
||||||
{
|
{
|
||||||
"builtin": "kerbs",
|
"builtin": "kerbs",
|
||||||
"override": {
|
"override": {
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"syncSelection": "theme-only",
|
"syncSelection": "theme-only",
|
||||||
"mapRendering": [
|
"mapRendering": [
|
||||||
{
|
{
|
||||||
|
@ -289,7 +289,7 @@
|
||||||
{
|
{
|
||||||
"builtin": "toilet",
|
"builtin": "toilet",
|
||||||
"override": {
|
"override": {
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"syncSelection": "theme-only",
|
"syncSelection": "theme-only",
|
||||||
"mapRendering": [
|
"mapRendering": [
|
||||||
{
|
{
|
||||||
|
@ -349,7 +349,7 @@
|
||||||
{
|
{
|
||||||
"builtin": "reception_desk",
|
"builtin": "reception_desk",
|
||||||
"override": {
|
"override": {
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"syncSelection": "theme-only"
|
"syncSelection": "theme-only"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -357,7 +357,7 @@
|
||||||
{
|
{
|
||||||
"builtin": "elevator",
|
"builtin": "elevator",
|
||||||
"override": {
|
"override": {
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"syncSelection": "theme-only",
|
"syncSelection": "theme-only",
|
||||||
"mapRendering": [
|
"mapRendering": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -165,7 +165,7 @@
|
||||||
{
|
{
|
||||||
"builtin": "food",
|
"builtin": "food",
|
||||||
"override": {
|
"override": {
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"filter": null,
|
"filter": null,
|
||||||
"name": null
|
"name": null
|
||||||
}
|
}
|
||||||
|
@ -181,7 +181,7 @@
|
||||||
{
|
{
|
||||||
"builtin": "shops",
|
"builtin": "shops",
|
||||||
"override": {
|
"override": {
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"filter": null,
|
"filter": null,
|
||||||
"presets": [
|
"presets": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
{
|
{
|
||||||
"builtin": "indoors",
|
"builtin": "indoors",
|
||||||
"override": {
|
"override": {
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"passAllFeatures": true,
|
"passAllFeatures": true,
|
||||||
"mapRendering": [
|
"mapRendering": [
|
||||||
{},
|
{},
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
{
|
{
|
||||||
"builtin": "stairs",
|
"builtin": "stairs",
|
||||||
"override": {
|
"override": {
|
||||||
"minzoom": 19
|
"minzoom": 18
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -114,7 +114,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"presets": null,
|
"presets": null,
|
||||||
"minzoom": 19
|
"minzoom": 18
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -127,7 +127,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"presets": null,
|
"presets": null,
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"mapRendering": [
|
"mapRendering": [
|
||||||
{
|
{
|
||||||
"icon": "circle:white;./assets/themes/stations/bicycle_parking.svg"
|
"icon": "circle:white;./assets/themes/stations/bicycle_parking.svg"
|
||||||
|
@ -145,7 +145,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"presets": null,
|
"presets": null,
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"mapRendering": [
|
"mapRendering": [
|
||||||
{
|
{
|
||||||
"icon": "circle:white;./assets/themes/stations/rental_bicycle.svg"
|
"icon": "circle:white;./assets/themes/stations/rental_bicycle.svg"
|
||||||
|
@ -163,7 +163,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"presets": null,
|
"presets": null,
|
||||||
"minzoom": 19
|
"minzoom": 18
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -179,7 +179,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"presets": null,
|
"presets": null,
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"mapRendering+": [
|
"mapRendering+": [
|
||||||
{
|
{
|
||||||
"color": "#00f",
|
"color": "#00f",
|
||||||
|
@ -198,7 +198,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"presets": null,
|
"presets": null,
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"mapRendering+": [
|
"mapRendering+": [
|
||||||
{
|
{
|
||||||
"color": "yellow",
|
"color": "yellow",
|
||||||
|
@ -219,13 +219,13 @@
|
||||||
"clock"
|
"clock"
|
||||||
],
|
],
|
||||||
"override": {
|
"override": {
|
||||||
"minzoom": 19
|
"minzoom": 18
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"builtin": "bench",
|
"builtin": "bench",
|
||||||
"override": {
|
"override": {
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"mapRendering": [
|
"mapRendering": [
|
||||||
{
|
{
|
||||||
"icon": "./assets/themes/stations/bench.svg"
|
"icon": "./assets/themes/stations/bench.svg"
|
||||||
|
@ -236,7 +236,7 @@
|
||||||
{
|
{
|
||||||
"builtin": "drinking_water",
|
"builtin": "drinking_water",
|
||||||
"override": {
|
"override": {
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"mapRendering": [
|
"mapRendering": [
|
||||||
{
|
{
|
||||||
"icon": "circle:white;./assets/themes/stations/drinking_water.svg"
|
"icon": "circle:white;./assets/themes/stations/drinking_water.svg"
|
||||||
|
@ -277,7 +277,7 @@
|
||||||
"zh_Hant": "時刻表"
|
"zh_Hant": "時刻表"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"source": {
|
"source": {
|
||||||
"osmTags": {
|
"osmTags": {
|
||||||
"and": [
|
"and": [
|
||||||
|
|
|
@ -220,7 +220,7 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"title": {
|
"title": {
|
||||||
"render": {
|
"render": {
|
||||||
"en": "Street",
|
"en": "Street",
|
||||||
|
|
|
@ -36,22 +36,19 @@
|
||||||
{
|
{
|
||||||
"builtin": "bike_parking",
|
"builtin": "bike_parking",
|
||||||
"override": {
|
"override": {
|
||||||
"minzoom": 19,
|
"minzoom": 18
|
||||||
"minzoomVisible": 19
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"builtin": "parking",
|
"builtin": "parking",
|
||||||
"override": {
|
"override": {
|
||||||
"minzoom": 19,
|
"minzoom": 18
|
||||||
"minzoomVisible": 19
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"builtin": "shelter",
|
"builtin": "shelter",
|
||||||
"override": {
|
"override": {
|
||||||
"minzoom": 19,
|
"minzoom": 18,
|
||||||
"minzoomVisible": 19,
|
|
||||||
"source": {
|
"source": {
|
||||||
"osmTags": {
|
"osmTags": {
|
||||||
"and": [
|
"and": [
|
||||||
|
|
|
@ -124,7 +124,7 @@
|
||||||
"pleaseLogin": "Please log in to add a new feature",
|
"pleaseLogin": "Please log in to add a new feature",
|
||||||
"presetInfo": "The new POI will have {tags}",
|
"presetInfo": "The new POI will have {tags}",
|
||||||
"stillLoading": "The data is still loading. Please wait a bit before you add a new feature.",
|
"stillLoading": "The data is still loading. Please wait a bit before you add a new feature.",
|
||||||
"title": "Add a new feature?",
|
"title": "Add a new feature",
|
||||||
"warnVisibleForEveryone": "Your addition will be visible for everyone",
|
"warnVisibleForEveryone": "Your addition will be visible for everyone",
|
||||||
"wrongType": "This feature is not a node or a way and can not be imported",
|
"wrongType": "This feature is not a node or a way and can not be imported",
|
||||||
"zoomInFurther": "Zoom in further to add a feature.",
|
"zoomInFurther": "Zoom in further to add a feature.",
|
||||||
|
|
|
@ -124,7 +124,7 @@
|
||||||
"pleaseLogin": "Gelieve je aan te melden om een object toe te voegen",
|
"pleaseLogin": "Gelieve je aan te melden om een object toe te voegen",
|
||||||
"presetInfo": "Het nieuwe object krijgt de attributen {tags}",
|
"presetInfo": "Het nieuwe object krijgt de attributen {tags}",
|
||||||
"stillLoading": "De data worden nog geladen. Nog even geduld en dan kan je een object toevoegen.",
|
"stillLoading": "De data worden nog geladen. Nog even geduld en dan kan je een object toevoegen.",
|
||||||
"title": "Nieuw object toevoegen?",
|
"title": "Nieuw object toevoegen",
|
||||||
"warnVisibleForEveryone": "Je toevoeging is voor iedereen zichtbaar",
|
"warnVisibleForEveryone": "Je toevoeging is voor iedereen zichtbaar",
|
||||||
"wrongType": "Dit object is geen punt of lijn en kan daarom niet geïmporteerd worden",
|
"wrongType": "Dit object is geen punt of lijn en kan daarom niet geïmporteerd worden",
|
||||||
"zoomInFurther": "Gelieve verder in te zoomen om een object toe te voegen.",
|
"zoomInFurther": "Gelieve verder in te zoomen om een object toe te voegen.",
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "npm run generate:layeroverview && npm run strt",
|
"start": "npm run generate:layeroverview && npm run strt",
|
||||||
"strt": "vite --host",
|
"strt": "vite --host | sed 's/localhost:/127.0.0.1:/g'",
|
||||||
"strttest": "export NODE_OPTIONS=--max_old_space_size=8364 && parcel serve test.html assets/templates/*.svg assets/templates/fonts/*.ttf",
|
"strttest": "export NODE_OPTIONS=--max_old_space_size=8364 && parcel serve test.html assets/templates/*.svg assets/templates/fonts/*.ttf",
|
||||||
"watch:css": "tailwindcss -i src/index.css -o public/css/index-tailwind-output.css --watch",
|
"watch:css": "tailwindcss -i src/index.css -o public/css/index-tailwind-output.css --watch",
|
||||||
"generate:css": "tailwindcss -i src/index.css -o public/css/index-tailwind-output.css",
|
"generate:css": "tailwindcss -i src/index.css -o public/css/index-tailwind-output.css",
|
||||||
|
|
|
@ -8,8 +8,7 @@ rm -rf dist/*
|
||||||
rm -rf .cache
|
rm -rf .cache
|
||||||
mkdir dist 2> /dev/null
|
mkdir dist 2> /dev/null
|
||||||
mkdir dist/assets 2> /dev/null
|
mkdir dist/assets 2> /dev/null
|
||||||
mkdir dist/assets/langs 2> /dev/null
|
|
||||||
mkdir dist/assets/langs/layers 2> /dev/null
|
|
||||||
|
|
||||||
export NODE_OPTIONS="--max-old-space-size=8192"
|
export NODE_OPTIONS="--max-old-space-size=8192"
|
||||||
|
|
||||||
|
@ -48,11 +47,12 @@ fi
|
||||||
|
|
||||||
export NODE_OPTIONS=--max-old-space-size=7000
|
export NODE_OPTIONS=--max-old-space-size=7000
|
||||||
vite build $SRC_MAPS
|
vite build $SRC_MAPS
|
||||||
|
|
||||||
|
|
||||||
# Copy the layer files, as these might contain assets (e.g. svgs)
|
# Copy the layer files, as these might contain assets (e.g. svgs)
|
||||||
cp -r assets/layers/ dist/assets/layers/
|
cp -r assets/layers/ dist/assets/layers/
|
||||||
cp -r assets/themes/ dist/assets/themes/
|
cp -r assets/themes/ dist/assets/themes/
|
||||||
cp -r assets/svg/ dist/assets/svg/
|
cp -r assets/svg/ dist/assets/svg/
|
||||||
cp -r langs/layers/ dist/assets/langs/layers/
|
mkdir dist/assets/langs
|
||||||
|
mkdir dist/assets/langs/layers
|
||||||
|
cp -r langs/layers/ dist/assets/langs/
|
||||||
|
ls dist/assets/langs/layers/
|
||||||
export NODE_OPTIONS=""
|
export NODE_OPTIONS=""
|
||||||
|
|
|
@ -1,24 +1,21 @@
|
||||||
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFile, writeFileSync } from "fs"
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFile, writeFileSync } from "fs";
|
||||||
import Locale from "../src/UI/i18n/Locale"
|
import Locale from "../src/UI/i18n/Locale";
|
||||||
import Translations from "../src/UI/i18n/Translations"
|
import Translations from "../src/UI/i18n/Translations";
|
||||||
import { Translation } from "../src/UI/i18n/Translation"
|
import { Translation } from "../src/UI/i18n/Translation";
|
||||||
import all_known_layouts from "../src/assets/generated/known_themes.json"
|
import all_known_layouts from "../src/assets/generated/known_themes.json";
|
||||||
import { LayoutConfigJson } from "../src/Models/ThemeConfig/Json/LayoutConfigJson"
|
import { LayoutConfigJson } from "../src/Models/ThemeConfig/Json/LayoutConfigJson";
|
||||||
import LayoutConfig from "../src/Models/ThemeConfig/LayoutConfig"
|
import LayoutConfig from "../src/Models/ThemeConfig/LayoutConfig";
|
||||||
import xml2js from "xml2js"
|
import xml2js from "xml2js";
|
||||||
import ScriptUtils from "./ScriptUtils"
|
import ScriptUtils from "./ScriptUtils";
|
||||||
import { Utils } from "../src/Utils"
|
import { Utils } from "../src/Utils";
|
||||||
import SpecialVisualizations from "../src/UI/SpecialVisualizations"
|
import SpecialVisualizations from "../src/UI/SpecialVisualizations";
|
||||||
import Constants from "../src/Models/Constants"
|
import Constants from "../src/Models/Constants";
|
||||||
import {
|
import { AvailableRasterLayers, RasterLayerPolygon } from "../src/Models/RasterLayers";
|
||||||
AvailableRasterLayers,
|
import { ImmutableStore } from "../src/Logic/UIEventSource";
|
||||||
EditorLayerIndexProperties,
|
import * as crypto from "crypto";
|
||||||
RasterLayerPolygon,
|
import * as eli from "../src/assets/editor-layer-index.json";
|
||||||
} from "../src/Models/RasterLayers"
|
import * as eli_global from "../src/assets/global-raster-layers.json";
|
||||||
import { ImmutableStore } from "../src/Logic/UIEventSource"
|
|
||||||
import * as crypto from "crypto"
|
|
||||||
import * as eli from "../src/assets/editor-layer-index.json"
|
|
||||||
import dom from "svelte/types/compiler/compile/render_dom"
|
|
||||||
const sharp = require("sharp")
|
const sharp = require("sharp")
|
||||||
const template = readFileSync("theme.html", "utf8")
|
const template = readFileSync("theme.html", "utf8")
|
||||||
const codeTemplate = readFileSync("src/index_theme.ts.template", "utf8")
|
const codeTemplate = readFileSync("src/index_theme.ts.template", "utf8")
|
||||||
|
@ -219,7 +216,8 @@ function eliUrls(): string[] {
|
||||||
}
|
}
|
||||||
const urls: string[] = []
|
const urls: string[] = []
|
||||||
const regex = /{switch:([^}]+)}/
|
const regex = /{switch:([^}]+)}/
|
||||||
for (const feature of eli.features) {
|
const rasterLayers = [...AvailableRasterLayers.vectorLayers, ...eli.features, ...eli_global.layers.map(properties => ({properties})) ]
|
||||||
|
for (const feature of rasterLayers) {
|
||||||
const url = (<RasterLayerPolygon>feature).properties.url
|
const url = (<RasterLayerPolygon>feature).properties.url
|
||||||
const match = url.match(regex)
|
const match = url.match(regex)
|
||||||
if (match) {
|
if (match) {
|
||||||
|
@ -245,8 +243,6 @@ function generateCsp(
|
||||||
...Constants.defaultOverpassUrls,
|
...Constants.defaultOverpassUrls,
|
||||||
Constants.countryCoderEndpoint,
|
Constants.countryCoderEndpoint,
|
||||||
Constants.nominatimEndpoint,
|
Constants.nominatimEndpoint,
|
||||||
AvailableRasterLayers.maptilerCarto.properties.url,
|
|
||||||
AvailableRasterLayers.maptilerDefaultLayer.properties.url,
|
|
||||||
"https://api.openstreetmap.org",
|
"https://api.openstreetmap.org",
|
||||||
"https://pietervdvn.goatcounter.com",
|
"https://pietervdvn.goatcounter.com",
|
||||||
]
|
]
|
||||||
|
|
|
@ -28,4 +28,3 @@ studio.mapcomplete.org {
|
||||||
to http://127.0.0.1:1235
|
to http://127.0.0.1:1235
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { Feature, Point } from "geojson"
|
||||||
import { TagUtils } from "../../Tags/TagUtils"
|
import { TagUtils } from "../../Tags/TagUtils"
|
||||||
import BaseUIElement from "../../../UI/BaseUIElement"
|
import BaseUIElement from "../../../UI/BaseUIElement"
|
||||||
import { Utils } from "../../../Utils"
|
import { Utils } from "../../../Utils"
|
||||||
|
import { OsmTags } from "../../../Models/OsmFeature"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Highly specialized feature source.
|
* Highly specialized feature source.
|
||||||
|
@ -12,8 +13,14 @@ import { Utils } from "../../../Utils"
|
||||||
*/
|
*/
|
||||||
export class LastClickFeatureSource implements WritableFeatureSource {
|
export class LastClickFeatureSource implements WritableFeatureSource {
|
||||||
public readonly features: UIEventSource<Feature[]> = new UIEventSource<Feature[]>([])
|
public readonly features: UIEventSource<Feature[]> = new UIEventSource<Feature[]>([])
|
||||||
|
public readonly hasNoteLayer: boolean
|
||||||
|
public readonly renderings: string[]
|
||||||
|
public readonly hasPresets: boolean
|
||||||
|
private i: number = 0
|
||||||
|
|
||||||
constructor(location: Store<{ lon: number; lat: number }>, layout: LayoutConfig) {
|
constructor(location: Store<{ lon: number; lat: number }>, layout: LayoutConfig) {
|
||||||
|
this.hasNoteLayer = layout.layers.some((l) => l.id === "note")
|
||||||
|
this.hasPresets = layout.layers.some((l) => l.presets?.length > 0)
|
||||||
const allPresets: BaseUIElement[] = []
|
const allPresets: BaseUIElement[] = []
|
||||||
for (const layer of layout.layers)
|
for (const layer of layout.layers)
|
||||||
for (let i = 0; i < (layer.presets ?? []).length; i++) {
|
for (let i = 0; i < (layer.presets ?? []).length; i++) {
|
||||||
|
@ -26,27 +33,30 @@ export class LastClickFeatureSource implements WritableFeatureSource {
|
||||||
allPresets.push(html)
|
allPresets.push(html)
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderings = Utils.Dedup(
|
this.renderings = Utils.Dedup(
|
||||||
allPresets.map((uiElem) =>
|
allPresets.map((uiElem) =>
|
||||||
Utils.runningFromConsole ? "" : uiElem.ConstructElement().innerHTML
|
Utils.runningFromConsole ? "" : uiElem.ConstructElement().innerHTML
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
let i = 0
|
|
||||||
|
|
||||||
location.addCallbackAndRunD(({ lon, lat }) => {
|
location.addCallbackAndRunD(({ lon, lat }) => {
|
||||||
const properties = {
|
this.features.setData([this.createFeature(lon, lat)])
|
||||||
lastclick: "yes",
|
})
|
||||||
id: "last_click_" + i,
|
|
||||||
has_note_layer: layout.layers.some((l) => l.id === "note") ? "yes" : "no",
|
|
||||||
has_presets: layout.layers.some((l) => l.presets?.length > 0) ? "yes" : "no",
|
|
||||||
renderings: renderings.join(""),
|
|
||||||
number_of_presets: "" + renderings.length,
|
|
||||||
first_preset: renderings[0],
|
|
||||||
}
|
}
|
||||||
i++
|
|
||||||
|
|
||||||
const point = <Feature<Point>>{
|
public createFeature(lon: number, lat: number): Feature<Point, OsmTags> {
|
||||||
|
const properties: OsmTags = {
|
||||||
|
lastclick: "yes",
|
||||||
|
id: "last_click_" + this.i,
|
||||||
|
has_note_layer: this.hasNoteLayer ? "yes" : "no",
|
||||||
|
has_presets: this.hasPresets ? "yes" : "no",
|
||||||
|
renderings: this.renderings.join(""),
|
||||||
|
number_of_presets: "" + this.renderings.length,
|
||||||
|
first_preset: this.renderings[0],
|
||||||
|
}
|
||||||
|
this.i++
|
||||||
|
|
||||||
|
return <Feature<Point, OsmTags>>{
|
||||||
type: "Feature",
|
type: "Feature",
|
||||||
properties,
|
properties,
|
||||||
geometry: {
|
geometry: {
|
||||||
|
@ -54,7 +64,5 @@ export class LastClickFeatureSource implements WritableFeatureSource {
|
||||||
coordinates: [lon, lat],
|
coordinates: [lon, lat],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
this.features.setData([point])
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
|
||||||
)
|
)
|
||||||
this.featureSwitchCommunityIndex = FeatureSwitchUtils.initSwitch(
|
this.featureSwitchCommunityIndex = FeatureSwitchUtils.initSwitch(
|
||||||
"fs-community-index",
|
"fs-community-index",
|
||||||
true,
|
this.featureSwitchEnableLogin.data,
|
||||||
"Disables/enables the button to get in touch with the community"
|
"Disables/enables the button to get in touch with the community"
|
||||||
)
|
)
|
||||||
this.featureSwitchExtraLinkEnabled = FeatureSwitchUtils.initSwitch(
|
this.featureSwitchExtraLinkEnabled = FeatureSwitchUtils.initSwitch(
|
||||||
|
|
|
@ -175,7 +175,6 @@ export default class ThemeViewStateHashActor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private back() {
|
private back() {
|
||||||
console.log("Got a back event")
|
|
||||||
const state = this._state
|
const state = this._state
|
||||||
// history.pushState(null, null, window.location.pathname);
|
// history.pushState(null, null, window.location.pathname);
|
||||||
if (state.selectedElement.data) {
|
if (state.selectedElement.data) {
|
||||||
|
|
|
@ -58,7 +58,7 @@ export default class Constants {
|
||||||
|
|
||||||
importHelperUnlock: 5000,
|
importHelperUnlock: 5000,
|
||||||
}
|
}
|
||||||
static readonly minZoomLevelToAddNewPoint = Constants.isRetina() ? 18 : 19
|
static readonly minZoomLevelToAddNewPoint = Constants.isRetina() ? 17 : 18
|
||||||
/**
|
/**
|
||||||
* Used by 'PendingChangesUploader', which waits this amount of seconds to upload changes.
|
* Used by 'PendingChangesUploader', which waits this amount of seconds to upload changes.
|
||||||
* (Note that pendingChanges might upload sooner if the popup is closed or similar)
|
* (Note that pendingChanges might upload sooner if the popup is closed or similar)
|
||||||
|
|
|
@ -1,41 +1,41 @@
|
||||||
import { Feature, Polygon } from "geojson"
|
import { Feature, Polygon } from "geojson";
|
||||||
import * as editorlayerindex from "../assets/editor-layer-index.json"
|
import * as editorlayerindex from "../assets/editor-layer-index.json";
|
||||||
import * as globallayers from "../assets/global-raster-layers.json"
|
import * as globallayers from "../assets/global-raster-layers.json";
|
||||||
import { BBox } from "../Logic/BBox"
|
import { BBox } from "../Logic/BBox";
|
||||||
import { Store, Stores } from "../Logic/UIEventSource"
|
import { Store, Stores } from "../Logic/UIEventSource";
|
||||||
import { GeoOperations } from "../Logic/GeoOperations"
|
import { GeoOperations } from "../Logic/GeoOperations";
|
||||||
import { RasterLayerProperties } from "./RasterLayerProperties"
|
import { RasterLayerProperties } from "./RasterLayerProperties";
|
||||||
|
|
||||||
export class AvailableRasterLayers {
|
export class AvailableRasterLayers {
|
||||||
public static EditorLayerIndex: (Feature<Polygon, EditorLayerIndexProperties> &
|
public static EditorLayerIndex: (Feature<Polygon, EditorLayerIndexProperties> &
|
||||||
RasterLayerPolygon)[] = <any>editorlayerindex.features
|
RasterLayerPolygon)[] = <any>editorlayerindex.features;
|
||||||
public static globalLayers: RasterLayerPolygon[] = globallayers.layers.map(
|
public static globalLayers: RasterLayerPolygon[] = globallayers.layers.map(
|
||||||
(properties) =>
|
(properties) =>
|
||||||
<RasterLayerPolygon>{
|
<RasterLayerPolygon>{
|
||||||
type: "Feature",
|
type: "Feature",
|
||||||
properties,
|
properties,
|
||||||
geometry: BBox.global.asGeometry(),
|
geometry: BBox.global.asGeometry()
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
public static readonly osmCartoProperties: RasterLayerProperties = {
|
public static readonly osmCartoProperties: RasterLayerProperties = {
|
||||||
id: "osm",
|
id: "osm",
|
||||||
name: "OpenStreetMap",
|
name: "OpenStreetMap",
|
||||||
url: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
url: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||||
attribution: {
|
attribution: {
|
||||||
text: "OpenStreetMap",
|
text: "OpenStreetMap",
|
||||||
url: "https://openStreetMap.org/copyright",
|
url: "https://openStreetMap.org/copyright"
|
||||||
},
|
},
|
||||||
best: true,
|
best: true,
|
||||||
max_zoom: 19,
|
max_zoom: 19,
|
||||||
min_zoom: 0,
|
min_zoom: 0,
|
||||||
category: "osmbasedmap",
|
category: "osmbasedmap"
|
||||||
}
|
};
|
||||||
|
|
||||||
public static readonly osmCarto: RasterLayerPolygon = {
|
public static readonly osmCarto: RasterLayerPolygon = {
|
||||||
type: "Feature",
|
type: "Feature",
|
||||||
properties: AvailableRasterLayers.osmCartoProperties,
|
properties: AvailableRasterLayers.osmCartoProperties,
|
||||||
geometry: BBox.global.asGeometry(),
|
geometry: BBox.global.asGeometry()
|
||||||
}
|
};
|
||||||
|
|
||||||
public static readonly maptilerDefaultLayer: RasterLayerPolygon = {
|
public static readonly maptilerDefaultLayer: RasterLayerPolygon = {
|
||||||
type: "Feature",
|
type: "Feature",
|
||||||
|
@ -47,11 +47,11 @@ export class AvailableRasterLayers {
|
||||||
type: "vector",
|
type: "vector",
|
||||||
attribution: {
|
attribution: {
|
||||||
text: "Maptiler",
|
text: "Maptiler",
|
||||||
url: "https://www.maptiler.com/copyright/",
|
url: "https://www.maptiler.com/copyright/"
|
||||||
},
|
|
||||||
},
|
|
||||||
geometry: BBox.global.asGeometry(),
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
geometry: BBox.global.asGeometry()
|
||||||
|
};
|
||||||
|
|
||||||
public static readonly maptilerCarto: RasterLayerPolygon = {
|
public static readonly maptilerCarto: RasterLayerPolygon = {
|
||||||
type: "Feature",
|
type: "Feature",
|
||||||
|
@ -63,11 +63,11 @@ export class AvailableRasterLayers {
|
||||||
type: "vector",
|
type: "vector",
|
||||||
attribution: {
|
attribution: {
|
||||||
text: "Maptiler",
|
text: "Maptiler",
|
||||||
url: "https://www.maptiler.com/copyright/",
|
url: "https://www.maptiler.com/copyright/"
|
||||||
},
|
|
||||||
},
|
|
||||||
geometry: BBox.global.asGeometry(),
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
geometry: BBox.global.asGeometry()
|
||||||
|
};
|
||||||
|
|
||||||
public static readonly maptilerBackdrop: RasterLayerPolygon = {
|
public static readonly maptilerBackdrop: RasterLayerPolygon = {
|
||||||
type: "Feature",
|
type: "Feature",
|
||||||
|
@ -79,11 +79,11 @@ export class AvailableRasterLayers {
|
||||||
type: "vector",
|
type: "vector",
|
||||||
attribution: {
|
attribution: {
|
||||||
text: "Maptiler",
|
text: "Maptiler",
|
||||||
url: "https://www.maptiler.com/copyright/",
|
url: "https://www.maptiler.com/copyright/"
|
||||||
},
|
|
||||||
},
|
|
||||||
geometry: BBox.global.asGeometry(),
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
geometry: BBox.global.asGeometry()
|
||||||
|
};
|
||||||
public static readonly americana: RasterLayerPolygon = {
|
public static readonly americana: RasterLayerPolygon = {
|
||||||
type: "Feature",
|
type: "Feature",
|
||||||
properties: {
|
properties: {
|
||||||
|
@ -94,45 +94,47 @@ export class AvailableRasterLayers {
|
||||||
type: "vector",
|
type: "vector",
|
||||||
attribution: {
|
attribution: {
|
||||||
text: "Americana",
|
text: "Americana",
|
||||||
url: "https://github.com/ZeLonewolf/openstreetmap-americana/",
|
url: "https://github.com/ZeLonewolf/openstreetmap-americana/"
|
||||||
},
|
|
||||||
},
|
|
||||||
geometry: BBox.global.asGeometry(),
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
geometry: BBox.global.asGeometry()
|
||||||
|
};
|
||||||
|
|
||||||
|
public static readonly vectorLayers = [
|
||||||
|
AvailableRasterLayers.maptilerDefaultLayer,
|
||||||
|
AvailableRasterLayers.osmCarto,
|
||||||
|
AvailableRasterLayers.maptilerCarto,
|
||||||
|
AvailableRasterLayers.maptilerBackdrop,
|
||||||
|
AvailableRasterLayers.americana
|
||||||
|
];
|
||||||
|
|
||||||
public static layersAvailableAt(
|
public static layersAvailableAt(
|
||||||
location: Store<{ lon: number; lat: number }>
|
location: Store<{ lon: number; lat: number }>
|
||||||
): Store<RasterLayerPolygon[]> {
|
): Store<RasterLayerPolygon[]> {
|
||||||
const availableLayersBboxes = Stores.ListStabilized(
|
const availableLayersBboxes = Stores.ListStabilized(
|
||||||
location.mapD((loc) => {
|
location.mapD((loc) => {
|
||||||
const lonlat: [number, number] = [loc.lon, loc.lat]
|
const lonlat: [number, number] = [loc.lon, loc.lat];
|
||||||
return AvailableRasterLayers.EditorLayerIndex.filter((eliPolygon) =>
|
return AvailableRasterLayers.EditorLayerIndex.filter((eliPolygon) =>
|
||||||
BBox.get(eliPolygon).contains(lonlat)
|
BBox.get(eliPolygon).contains(lonlat)
|
||||||
)
|
);
|
||||||
})
|
})
|
||||||
)
|
);
|
||||||
const available = Stores.ListStabilized(
|
const available = Stores.ListStabilized(
|
||||||
availableLayersBboxes.map((eliPolygons) => {
|
availableLayersBboxes.map((eliPolygons) => {
|
||||||
const loc = location.data
|
const loc = location.data;
|
||||||
const lonlat: [number, number] = [loc.lon, loc.lat]
|
const lonlat: [number, number] = [loc.lon, loc.lat];
|
||||||
const matching: RasterLayerPolygon[] = eliPolygons.filter((eliPolygon) => {
|
const matching: RasterLayerPolygon[] = eliPolygons.filter((eliPolygon) => {
|
||||||
if (eliPolygon.geometry === null) {
|
if (eliPolygon.geometry === null) {
|
||||||
return true // global ELI-layer
|
return true; // global ELI-layer
|
||||||
}
|
}
|
||||||
return GeoOperations.inside(lonlat, eliPolygon)
|
return GeoOperations.inside(lonlat, eliPolygon);
|
||||||
|
});
|
||||||
|
matching.push(...AvailableRasterLayers.globalLayers);
|
||||||
|
matching.unshift(...AvailableRasterLayers.vectorLayers);
|
||||||
|
return matching;
|
||||||
})
|
})
|
||||||
matching.push(...AvailableRasterLayers.globalLayers)
|
);
|
||||||
matching.unshift(
|
return available;
|
||||||
AvailableRasterLayers.maptilerDefaultLayer,
|
|
||||||
AvailableRasterLayers.osmCarto,
|
|
||||||
AvailableRasterLayers.maptilerCarto,
|
|
||||||
AvailableRasterLayers.maptilerBackdrop,
|
|
||||||
AvailableRasterLayers.americana
|
|
||||||
)
|
|
||||||
return matching
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return available
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,22 +152,22 @@ export class RasterLayerUtils {
|
||||||
preferredCategory: string,
|
preferredCategory: string,
|
||||||
ignoreLayer?: RasterLayerPolygon
|
ignoreLayer?: RasterLayerPolygon
|
||||||
): RasterLayerPolygon {
|
): RasterLayerPolygon {
|
||||||
let secondBest: RasterLayerPolygon = undefined
|
let secondBest: RasterLayerPolygon = undefined;
|
||||||
for (const rasterLayer of available) {
|
for (const rasterLayer of available) {
|
||||||
if (rasterLayer === ignoreLayer) {
|
if (rasterLayer === ignoreLayer) {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
const p = rasterLayer.properties
|
const p = rasterLayer.properties;
|
||||||
if (p.category === preferredCategory) {
|
if (p.category === preferredCategory) {
|
||||||
if (p.best) {
|
if (p.best) {
|
||||||
return rasterLayer
|
return rasterLayer;
|
||||||
}
|
}
|
||||||
if (!secondBest) {
|
if (!secondBest) {
|
||||||
secondBest = rasterLayer
|
secondBest = rasterLayer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return secondBest
|
return secondBest;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,11 +183,11 @@ export interface EditorLayerIndexProperties extends RasterLayerProperties {
|
||||||
/**
|
/**
|
||||||
* The name of the imagery source
|
* The name of the imagery source
|
||||||
*/
|
*/
|
||||||
readonly name: string
|
readonly name: string;
|
||||||
/**
|
/**
|
||||||
* Whether the imagery name should be translated
|
* Whether the imagery name should be translated
|
||||||
*/
|
*/
|
||||||
readonly i18n?: boolean
|
readonly i18n?: boolean;
|
||||||
readonly type:
|
readonly type:
|
||||||
| "tms"
|
| "tms"
|
||||||
| "wms"
|
| "wms"
|
||||||
|
@ -193,7 +195,7 @@ export interface EditorLayerIndexProperties extends RasterLayerProperties {
|
||||||
| "scanex"
|
| "scanex"
|
||||||
| "wms_endpoint"
|
| "wms_endpoint"
|
||||||
| "wmts"
|
| "wmts"
|
||||||
| "vector" /* Vector is not actually part of the ELI-spec, we add it for vector layers */
|
| "vector"; /* Vector is not actually part of the ELI-spec, we add it for vector layers */
|
||||||
/**
|
/**
|
||||||
* A rough categorisation of different types of layers. See https://github.com/osmlab/editor-layer-index/blob/gh-pages/CONTRIBUTING.md#categories for a description of the individual categories.
|
* A rough categorisation of different types of layers. See https://github.com/osmlab/editor-layer-index/blob/gh-pages/CONTRIBUTING.md#categories for a description of the individual categories.
|
||||||
*/
|
*/
|
||||||
|
@ -205,53 +207,53 @@ export interface EditorLayerIndexProperties extends RasterLayerProperties {
|
||||||
| "historicphoto"
|
| "historicphoto"
|
||||||
| "qa"
|
| "qa"
|
||||||
| "elevation"
|
| "elevation"
|
||||||
| "other"
|
| "other";
|
||||||
/**
|
/**
|
||||||
* A URL template for imagery tiles
|
* A URL template for imagery tiles
|
||||||
*/
|
*/
|
||||||
readonly url: string
|
readonly url: string;
|
||||||
readonly min_zoom?: number
|
readonly min_zoom?: number;
|
||||||
readonly max_zoom?: number
|
readonly max_zoom?: number;
|
||||||
/**
|
/**
|
||||||
* explicit/implicit permission by the owner for use in OSM
|
* explicit/implicit permission by the owner for use in OSM
|
||||||
*/
|
*/
|
||||||
readonly permission_osm?: "explicit" | "implicit" | "no"
|
readonly permission_osm?: "explicit" | "implicit" | "no";
|
||||||
/**
|
/**
|
||||||
* A URL for the license or permissions for the imagery
|
* A URL for the license or permissions for the imagery
|
||||||
*/
|
*/
|
||||||
readonly license_url?: string
|
readonly license_url?: string;
|
||||||
/**
|
/**
|
||||||
* A URL for the privacy policy of the operator or false if there is no existing privacy policy for tis imagery.
|
* A URL for the privacy policy of the operator or false if there is no existing privacy policy for tis imagery.
|
||||||
*/
|
*/
|
||||||
readonly privacy_policy_url?: string | boolean
|
readonly privacy_policy_url?: string | boolean;
|
||||||
/**
|
/**
|
||||||
* A unique identifier for the source; used in imagery_used changeset tag
|
* A unique identifier for the source; used in imagery_used changeset tag
|
||||||
*/
|
*/
|
||||||
readonly id: string
|
readonly id: string;
|
||||||
/**
|
/**
|
||||||
* A short English-language description of the source
|
* A short English-language description of the source
|
||||||
*/
|
*/
|
||||||
readonly description?: string
|
readonly description?: string;
|
||||||
/**
|
/**
|
||||||
* The ISO 3166-1 alpha-2 two letter country code in upper case. Use ZZ for unknown or multiple.
|
* The ISO 3166-1 alpha-2 two letter country code in upper case. Use ZZ for unknown or multiple.
|
||||||
*/
|
*/
|
||||||
readonly country_code?: string
|
readonly country_code?: string;
|
||||||
/**
|
/**
|
||||||
* Whether this imagery should be shown in the default world-wide menu
|
* Whether this imagery should be shown in the default world-wide menu
|
||||||
*/
|
*/
|
||||||
readonly default?: boolean
|
readonly default?: boolean;
|
||||||
/**
|
/**
|
||||||
* Whether this imagery is the best source for the region
|
* Whether this imagery is the best source for the region
|
||||||
*/
|
*/
|
||||||
readonly best?: boolean
|
readonly best?: boolean;
|
||||||
/**
|
/**
|
||||||
* The age of the oldest imagery or data in the source, as an RFC3339 date or leading portion of one
|
* The age of the oldest imagery or data in the source, as an RFC3339 date or leading portion of one
|
||||||
*/
|
*/
|
||||||
readonly start_date?: string
|
readonly start_date?: string;
|
||||||
/**
|
/**
|
||||||
* The age of the newest imagery or data in the source, as an RFC3339 date or leading portion of one
|
* The age of the newest imagery or data in the source, as an RFC3339 date or leading portion of one
|
||||||
*/
|
*/
|
||||||
readonly end_date?: string
|
readonly end_date?: string;
|
||||||
/**
|
/**
|
||||||
* HTTP header to check for information if the tile is invalid
|
* HTTP header to check for information if the tile is invalid
|
||||||
*/
|
*/
|
||||||
|
@ -261,61 +263,61 @@ export interface EditorLayerIndexProperties extends RasterLayerProperties {
|
||||||
* via the `patternProperty` "^.*$".
|
* via the `patternProperty` "^.*$".
|
||||||
*/
|
*/
|
||||||
[k: string]: string[] | null
|
[k: string]: string[] | null
|
||||||
}
|
};
|
||||||
/**
|
/**
|
||||||
* 'true' if tiles are transparent and can be overlaid on another source
|
* 'true' if tiles are transparent and can be overlaid on another source
|
||||||
*/
|
*/
|
||||||
readonly overlay?: boolean & string
|
readonly overlay?: boolean & string;
|
||||||
readonly available_projections?: string[]
|
readonly available_projections?: string[];
|
||||||
readonly attribution?: {
|
readonly attribution?: {
|
||||||
readonly url?: string
|
readonly url?: string
|
||||||
readonly text?: string
|
readonly text?: string
|
||||||
readonly html?: string
|
readonly html?: string
|
||||||
readonly required?: boolean
|
readonly required?: boolean
|
||||||
}
|
};
|
||||||
/**
|
/**
|
||||||
* A URL for an image, that can be displayed in the list of imagery layers next to the name
|
* A URL for an image, that can be displayed in the list of imagery layers next to the name
|
||||||
*/
|
*/
|
||||||
readonly icon?: string
|
readonly icon?: string;
|
||||||
/**
|
/**
|
||||||
* A link to an EULA text that has to be accepted by the user, before the imagery source is added. Can contain {lang} to be replaced by a current user language wiki code (like FR:) or an empty string for the default English text.
|
* A link to an EULA text that has to be accepted by the user, before the imagery source is added. Can contain {lang} to be replaced by a current user language wiki code (like FR:) or an empty string for the default English text.
|
||||||
*/
|
*/
|
||||||
readonly eula?: string
|
readonly eula?: string;
|
||||||
/**
|
/**
|
||||||
* A URL for an image, that is displayed in the mapview for attribution
|
* A URL for an image, that is displayed in the mapview for attribution
|
||||||
*/
|
*/
|
||||||
readonly "logo-image"?: string
|
readonly "logo-image"?: string;
|
||||||
/**
|
/**
|
||||||
* Customized text for the terms of use link (default is "Background Terms of Use")
|
* Customized text for the terms of use link (default is "Background Terms of Use")
|
||||||
*/
|
*/
|
||||||
readonly "terms-of-use-text"?: string
|
readonly "terms-of-use-text"?: string;
|
||||||
/**
|
/**
|
||||||
* Specify a checksum for tiles, which aren't real tiles. `type` is the digest type and can be MD5, SHA-1, SHA-256, SHA-384 and SHA-512, value is the hex encoded checksum in lower case. To create a checksum save the tile as file and upload it to e.g. https://defuse.ca/checksums.htm.
|
* Specify a checksum for tiles, which aren't real tiles. `type` is the digest type and can be MD5, SHA-1, SHA-256, SHA-384 and SHA-512, value is the hex encoded checksum in lower case. To create a checksum save the tile as file and upload it to e.g. https://defuse.ca/checksums.htm.
|
||||||
*/
|
*/
|
||||||
readonly "no-tile-checksum"?: string
|
readonly "no-tile-checksum"?: string;
|
||||||
/**
|
/**
|
||||||
* header-name attribute specifies a header returned by tile server, that will be shown as `metadata-key` attribute in Show Tile Info dialog
|
* header-name attribute specifies a header returned by tile server, that will be shown as `metadata-key` attribute in Show Tile Info dialog
|
||||||
*/
|
*/
|
||||||
readonly "metadata-header"?: string
|
readonly "metadata-header"?: string;
|
||||||
/**
|
/**
|
||||||
* Set to `true` if imagery source is properly aligned and does not need imagery offset adjustments. This is used for OSM based sources too.
|
* Set to `true` if imagery source is properly aligned and does not need imagery offset adjustments. This is used for OSM based sources too.
|
||||||
*/
|
*/
|
||||||
readonly "valid-georeference"?: boolean
|
readonly "valid-georeference"?: boolean;
|
||||||
/**
|
/**
|
||||||
* Size of individual tiles delivered by a TMS service
|
* Size of individual tiles delivered by a TMS service
|
||||||
*/
|
*/
|
||||||
readonly "tile-size"?: number
|
readonly "tile-size"?: number;
|
||||||
/**
|
/**
|
||||||
* Whether tiles status can be accessed by appending /status to the tile URL and can be submitted for re-rendering by appending /dirty.
|
* Whether tiles status can be accessed by appending /status to the tile URL and can be submitted for re-rendering by appending /dirty.
|
||||||
*/
|
*/
|
||||||
readonly "mod-tile-features"?: string
|
readonly "mod-tile-features"?: string;
|
||||||
/**
|
/**
|
||||||
* HTTP headers to be sent to server. It has two attributes header-name and header-value. May be specified multiple times.
|
* HTTP headers to be sent to server. It has two attributes header-name and header-value. May be specified multiple times.
|
||||||
*/
|
*/
|
||||||
readonly "custom-http-headers"?: {
|
readonly "custom-http-headers"?: {
|
||||||
readonly "header-name"?: string
|
readonly "header-name"?: string
|
||||||
readonly "header-value"?: string
|
readonly "header-value"?: string
|
||||||
}
|
};
|
||||||
/**
|
/**
|
||||||
* Default layer to open (when using WMS_ENDPOINT type). Contains list of layer tag with two attributes - name and style, e.g. `"default-layers": ["layer": { name="Basisdata_NP_Basiskart_JanMayen_WMTS_25829" "style":"default" } ]` (not allowed in `mirror` attribute)
|
* Default layer to open (when using WMS_ENDPOINT type). Contains list of layer tag with two attributes - name and style, e.g. `"default-layers": ["layer": { name="Basisdata_NP_Basiskart_JanMayen_WMTS_25829" "style":"default" } ]` (not allowed in `mirror` attribute)
|
||||||
*/
|
*/
|
||||||
|
@ -326,17 +328,17 @@ export interface EditorLayerIndexProperties extends RasterLayerProperties {
|
||||||
[k: string]: unknown
|
[k: string]: unknown
|
||||||
}
|
}
|
||||||
[k: string]: unknown
|
[k: string]: unknown
|
||||||
}[]
|
}[];
|
||||||
/**
|
/**
|
||||||
* format to use when connecting tile server (when using WMS_ENDPOINT type)
|
* format to use when connecting tile server (when using WMS_ENDPOINT type)
|
||||||
*/
|
*/
|
||||||
readonly format?: string
|
readonly format?: string;
|
||||||
/**
|
/**
|
||||||
* If `true` transparent tiles will be requested from WMS server
|
* If `true` transparent tiles will be requested from WMS server
|
||||||
*/
|
*/
|
||||||
readonly transparent?: boolean & string
|
readonly transparent?: boolean & string;
|
||||||
/**
|
/**
|
||||||
* minimum expiry time for tiles in seconds. The larger the value, the longer entry in cache will be considered valid
|
* minimum expiry time for tiles in seconds. The larger the value, the longer entry in cache will be considered valid
|
||||||
*/
|
*/
|
||||||
readonly "minimum-tile-expire"?: number
|
readonly "minimum-tile-expire"?: number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,6 +95,9 @@ export default class LayoutConfig implements LayoutInformation {
|
||||||
}
|
}
|
||||||
const context = this.id
|
const context = this.id
|
||||||
this.credits = json.credits
|
this.credits = json.credits
|
||||||
|
if(!json.title){
|
||||||
|
throw `The theme ${json.id} does not have a title defined.`
|
||||||
|
}
|
||||||
this.language = json.mustHaveLanguage ?? Object.keys(json.title)
|
this.language = json.mustHaveLanguage ?? Object.keys(json.title)
|
||||||
this.usedImages = Array.from(
|
this.usedImages = Array.from(
|
||||||
new ExtractImages(official, undefined)
|
new ExtractImages(official, undefined)
|
||||||
|
|
|
@ -1,62 +1,58 @@
|
||||||
import LayoutConfig from "./ThemeConfig/LayoutConfig"
|
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 { FeatureSource, IndexedFeatureSource, WritableFeatureSource } from "../Logic/FeatureSource/FeatureSource";
|
||||||
|
import { OsmConnection } from "../Logic/Osm/OsmConnection";
|
||||||
|
import { ExportableMap, MapProperties } from "./MapProperties";
|
||||||
|
import LayerState from "../Logic/State/LayerState";
|
||||||
|
import { Feature, Point, Polygon } from "geojson";
|
||||||
|
import FullNodeDatabaseSource from "../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource";
|
||||||
|
import { Map as MlMap } from "maplibre-gl";
|
||||||
|
import InitialMapPositioning from "../Logic/Actors/InitialMapPositioning";
|
||||||
|
import { MapLibreAdaptor } from "../UI/Map/MapLibreAdaptor";
|
||||||
|
import { GeoLocationState } from "../Logic/State/GeoLocationState";
|
||||||
|
import FeatureSwitchState from "../Logic/State/FeatureSwitchState";
|
||||||
|
import { QueryParameters } from "../Logic/Web/QueryParameters";
|
||||||
|
import UserRelatedState from "../Logic/State/UserRelatedState";
|
||||||
|
import LayerConfig from "./ThemeConfig/LayerConfig";
|
||||||
|
import GeoLocationHandler from "../Logic/Actors/GeoLocationHandler";
|
||||||
|
import { AvailableRasterLayers, RasterLayerPolygon, RasterLayerUtils } from "./RasterLayers";
|
||||||
|
import LayoutSource from "../Logic/FeatureSource/Sources/LayoutSource";
|
||||||
|
import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource";
|
||||||
|
import FeaturePropertiesStore from "../Logic/FeatureSource/Actors/FeaturePropertiesStore";
|
||||||
|
import PerLayerFeatureSourceSplitter from "../Logic/FeatureSource/PerLayerFeatureSourceSplitter";
|
||||||
|
import FilteringFeatureSource from "../Logic/FeatureSource/Sources/FilteringFeatureSource";
|
||||||
|
import ShowDataLayer from "../UI/Map/ShowDataLayer";
|
||||||
|
import TitleHandler from "../Logic/Actors/TitleHandler";
|
||||||
|
import ChangeToElementsActor from "../Logic/Actors/ChangeToElementsActor";
|
||||||
|
import PendingChangesUploader from "../Logic/Actors/PendingChangesUploader";
|
||||||
|
import SelectedElementTagsUpdater from "../Logic/Actors/SelectedElementTagsUpdater";
|
||||||
|
import { BBox } from "../Logic/BBox";
|
||||||
|
import Constants from "./Constants";
|
||||||
|
import Hotkeys from "../UI/Base/Hotkeys";
|
||||||
|
import Translations from "../UI/i18n/Translations";
|
||||||
|
import { GeoIndexedStoreForLayer } from "../Logic/FeatureSource/Actors/GeoIndexedStore";
|
||||||
|
import { LastClickFeatureSource } from "../Logic/FeatureSource/Sources/LastClickFeatureSource";
|
||||||
|
import { MenuState } from "./MenuState";
|
||||||
|
import MetaTagging from "../Logic/MetaTagging";
|
||||||
|
import ChangeGeometryApplicator from "../Logic/FeatureSource/Sources/ChangeGeometryApplicator";
|
||||||
import {
|
import {
|
||||||
FeatureSource,
|
NewGeometryFromChangesFeatureSource
|
||||||
IndexedFeatureSource,
|
} from "../Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource";
|
||||||
WritableFeatureSource,
|
import OsmObjectDownloader from "../Logic/Osm/OsmObjectDownloader";
|
||||||
} from "../Logic/FeatureSource/FeatureSource"
|
import ShowOverlayRasterLayer from "../UI/Map/ShowOverlayRasterLayer";
|
||||||
import { OsmConnection } from "../Logic/Osm/OsmConnection"
|
import { Utils } from "../Utils";
|
||||||
import { ExportableMap, MapProperties } from "./MapProperties"
|
import { EliCategory } from "./RasterLayerProperties";
|
||||||
import LayerState from "../Logic/State/LayerState"
|
import BackgroundLayerResetter from "../Logic/Actors/BackgroundLayerResetter";
|
||||||
import { Feature, Point, Polygon } from "geojson"
|
import SaveFeatureSourceToLocalStorage from "../Logic/FeatureSource/Actors/SaveFeatureSourceToLocalStorage";
|
||||||
import FullNodeDatabaseSource from "../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
|
import BBoxFeatureSource from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource";
|
||||||
import { Map as MlMap } from "maplibre-gl"
|
import ThemeViewStateHashActor from "../Logic/Web/ThemeViewStateHashActor";
|
||||||
import InitialMapPositioning from "../Logic/Actors/InitialMapPositioning"
|
import NoElementsInViewDetector, { FeatureViewState } from "../Logic/Actors/NoElementsInViewDetector";
|
||||||
import { MapLibreAdaptor } from "../UI/Map/MapLibreAdaptor"
|
import FilteredLayer from "./FilteredLayer";
|
||||||
import { GeoLocationState } from "../Logic/State/GeoLocationState"
|
import { PreferredRasterLayerSelector } from "../Logic/Actors/PreferredRasterLayerSelector";
|
||||||
import FeatureSwitchState from "../Logic/State/FeatureSwitchState"
|
import { ImageUploadManager } from "../Logic/ImageProviders/ImageUploadManager";
|
||||||
import { QueryParameters } from "../Logic/Web/QueryParameters"
|
import { Imgur } from "../Logic/ImageProviders/Imgur";
|
||||||
import UserRelatedState from "../Logic/State/UserRelatedState"
|
|
||||||
import LayerConfig from "./ThemeConfig/LayerConfig"
|
|
||||||
import GeoLocationHandler from "../Logic/Actors/GeoLocationHandler"
|
|
||||||
import { AvailableRasterLayers, RasterLayerPolygon, RasterLayerUtils } from "./RasterLayers"
|
|
||||||
import LayoutSource from "../Logic/FeatureSource/Sources/LayoutSource"
|
|
||||||
import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource"
|
|
||||||
import FeaturePropertiesStore from "../Logic/FeatureSource/Actors/FeaturePropertiesStore"
|
|
||||||
import PerLayerFeatureSourceSplitter from "../Logic/FeatureSource/PerLayerFeatureSourceSplitter"
|
|
||||||
import FilteringFeatureSource from "../Logic/FeatureSource/Sources/FilteringFeatureSource"
|
|
||||||
import ShowDataLayer from "../UI/Map/ShowDataLayer"
|
|
||||||
import TitleHandler from "../Logic/Actors/TitleHandler"
|
|
||||||
import ChangeToElementsActor from "../Logic/Actors/ChangeToElementsActor"
|
|
||||||
import PendingChangesUploader from "../Logic/Actors/PendingChangesUploader"
|
|
||||||
import SelectedElementTagsUpdater from "../Logic/Actors/SelectedElementTagsUpdater"
|
|
||||||
import { BBox } from "../Logic/BBox"
|
|
||||||
import Constants from "./Constants"
|
|
||||||
import Hotkeys from "../UI/Base/Hotkeys"
|
|
||||||
import Translations from "../UI/i18n/Translations"
|
|
||||||
import { GeoIndexedStoreForLayer } from "../Logic/FeatureSource/Actors/GeoIndexedStore"
|
|
||||||
import { LastClickFeatureSource } from "../Logic/FeatureSource/Sources/LastClickFeatureSource"
|
|
||||||
import { MenuState } from "./MenuState"
|
|
||||||
import MetaTagging from "../Logic/MetaTagging"
|
|
||||||
import ChangeGeometryApplicator from "../Logic/FeatureSource/Sources/ChangeGeometryApplicator"
|
|
||||||
import { NewGeometryFromChangesFeatureSource } from "../Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource"
|
|
||||||
import OsmObjectDownloader from "../Logic/Osm/OsmObjectDownloader"
|
|
||||||
import ShowOverlayRasterLayer from "../UI/Map/ShowOverlayRasterLayer"
|
|
||||||
import { Utils } from "../Utils"
|
|
||||||
import { EliCategory } from "./RasterLayerProperties"
|
|
||||||
import BackgroundLayerResetter from "../Logic/Actors/BackgroundLayerResetter"
|
|
||||||
import SaveFeatureSourceToLocalStorage from "../Logic/FeatureSource/Actors/SaveFeatureSourceToLocalStorage"
|
|
||||||
import BBoxFeatureSource from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource"
|
|
||||||
import ThemeViewStateHashActor from "../Logic/Web/ThemeViewStateHashActor"
|
|
||||||
import NoElementsInViewDetector, {
|
|
||||||
FeatureViewState,
|
|
||||||
} from "../Logic/Actors/NoElementsInViewDetector"
|
|
||||||
import FilteredLayer from "./FilteredLayer"
|
|
||||||
import { PreferredRasterLayerSelector } from "../Logic/Actors/PreferredRasterLayerSelector"
|
|
||||||
import { ImageUploadManager } from "../Logic/ImageProviders/ImageUploadManager"
|
|
||||||
import { Imgur } from "../Logic/ImageProviders/Imgur"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -67,71 +63,74 @@ import { Imgur } from "../Logic/ImageProviders/Imgur"
|
||||||
* It ties up all the needed elements and starts some actors.
|
* It ties up all the needed elements and starts some actors.
|
||||||
*/
|
*/
|
||||||
export default class ThemeViewState implements SpecialVisualizationState {
|
export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
readonly layout: LayoutConfig
|
readonly layout: LayoutConfig;
|
||||||
readonly map: UIEventSource<MlMap>
|
readonly map: UIEventSource<MlMap>;
|
||||||
readonly changes: Changes
|
readonly changes: Changes;
|
||||||
readonly featureSwitches: FeatureSwitchState
|
readonly featureSwitches: FeatureSwitchState;
|
||||||
readonly featureSwitchIsTesting: Store<boolean>
|
readonly featureSwitchIsTesting: Store<boolean>;
|
||||||
readonly featureSwitchUserbadge: Store<boolean>
|
readonly featureSwitchUserbadge: Store<boolean>;
|
||||||
|
|
||||||
readonly featureProperties: FeaturePropertiesStore
|
readonly featureProperties: FeaturePropertiesStore;
|
||||||
|
|
||||||
readonly osmConnection: OsmConnection
|
readonly osmConnection: OsmConnection;
|
||||||
readonly selectedElement: UIEventSource<Feature>
|
readonly selectedElement: UIEventSource<Feature>;
|
||||||
readonly selectedElementAndLayer: Store<{ feature: Feature; layer: LayerConfig }>
|
readonly selectedElementAndLayer: Store<{ feature: Feature; layer: LayerConfig }>;
|
||||||
readonly mapProperties: MapProperties & ExportableMap
|
readonly mapProperties: MapProperties & ExportableMap;
|
||||||
readonly osmObjectDownloader: OsmObjectDownloader
|
readonly osmObjectDownloader: OsmObjectDownloader;
|
||||||
|
|
||||||
readonly dataIsLoading: Store<boolean>
|
readonly dataIsLoading: Store<boolean>;
|
||||||
/**
|
/**
|
||||||
* Indicates if there is _some_ data in view, even if it is not shown due to the filters
|
* Indicates if there is _some_ data in view, even if it is not shown due to the filters
|
||||||
*/
|
*/
|
||||||
readonly hasDataInView: Store<FeatureViewState>
|
readonly hasDataInView: Store<FeatureViewState>;
|
||||||
|
|
||||||
readonly guistate: MenuState
|
readonly guistate: MenuState;
|
||||||
readonly fullNodeDatabase?: FullNodeDatabaseSource
|
readonly fullNodeDatabase?: FullNodeDatabaseSource;
|
||||||
|
|
||||||
readonly historicalUserLocations: WritableFeatureSource<Feature<Point>>
|
readonly historicalUserLocations: WritableFeatureSource<Feature<Point>>;
|
||||||
readonly indexedFeatures: IndexedFeatureSource & LayoutSource
|
readonly indexedFeatures: IndexedFeatureSource & LayoutSource;
|
||||||
readonly currentView: FeatureSource<Feature<Polygon>>
|
readonly currentView: FeatureSource<Feature<Polygon>>;
|
||||||
readonly featuresInView: FeatureSource
|
readonly featuresInView: FeatureSource;
|
||||||
readonly newFeatures: WritableFeatureSource
|
readonly newFeatures: WritableFeatureSource;
|
||||||
readonly layerState: LayerState
|
readonly layerState: LayerState;
|
||||||
readonly perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer>
|
readonly perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer>;
|
||||||
readonly perLayerFiltered: ReadonlyMap<string, FilteringFeatureSource>
|
readonly perLayerFiltered: ReadonlyMap<string, FilteringFeatureSource>;
|
||||||
|
|
||||||
readonly availableLayers: Store<RasterLayerPolygon[]>
|
readonly availableLayers: Store<RasterLayerPolygon[]>;
|
||||||
readonly selectedLayer: UIEventSource<LayerConfig>
|
readonly selectedLayer: UIEventSource<LayerConfig>;
|
||||||
readonly userRelatedState: UserRelatedState
|
readonly userRelatedState: UserRelatedState;
|
||||||
readonly geolocation: GeoLocationHandler
|
readonly geolocation: GeoLocationHandler;
|
||||||
|
|
||||||
readonly imageUploadManager: ImageUploadManager
|
readonly imageUploadManager: ImageUploadManager;
|
||||||
|
|
||||||
readonly lastClickObject: WritableFeatureSource
|
readonly addNewPoint: UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
||||||
|
|
||||||
|
readonly lastClickObject: LastClickFeatureSource;
|
||||||
readonly overlayLayerStates: ReadonlyMap<
|
readonly overlayLayerStates: ReadonlyMap<
|
||||||
string,
|
string,
|
||||||
{ readonly isDisplayed: UIEventSource<boolean> }
|
{ readonly isDisplayed: UIEventSource<boolean> }
|
||||||
>
|
>;
|
||||||
/**
|
/**
|
||||||
* All 'level'-tags that are available with the current features
|
* All 'level'-tags that are available with the current features
|
||||||
*/
|
*/
|
||||||
readonly floors: Store<string[]>
|
readonly floors: Store<string[]>;
|
||||||
|
private readonly newPointDialog: FilteredLayer;
|
||||||
|
|
||||||
constructor(layout: LayoutConfig) {
|
constructor(layout: LayoutConfig) {
|
||||||
Utils.initDomPurify()
|
Utils.initDomPurify();
|
||||||
this.layout = layout
|
this.layout = layout;
|
||||||
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 initial = new InitialMapPositioning(layout)
|
const initial = new InitialMapPositioning(layout);
|
||||||
this.mapProperties = new MapLibreAdaptor(this.map, initial)
|
this.mapProperties = new MapLibreAdaptor(this.map, initial);
|
||||||
const geolocationState = new GeoLocationState()
|
const geolocationState = new GeoLocationState();
|
||||||
|
|
||||||
this.featureSwitchIsTesting = this.featureSwitches.featureSwitchIsTesting
|
this.featureSwitchIsTesting = this.featureSwitches.featureSwitchIsTesting;
|
||||||
this.featureSwitchUserbadge = this.featureSwitches.featureSwitchEnableLogin
|
this.featureSwitchUserbadge = this.featureSwitches.featureSwitchEnableLogin;
|
||||||
|
|
||||||
this.osmConnection = new OsmConnection({
|
this.osmConnection = new OsmConnection({
|
||||||
dryRun: this.featureSwitches.featureSwitchIsTesting,
|
dryRun: this.featureSwitches.featureSwitchIsTesting,
|
||||||
|
@ -140,57 +139,57 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
"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?.language,
|
layout?.language,
|
||||||
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");
|
||||||
})
|
});
|
||||||
this.selectedElement = new UIEventSource<Feature | undefined>(undefined, "Selected element")
|
this.selectedElement = new UIEventSource<Feature | undefined>(undefined, "Selected element");
|
||||||
this.selectedLayer = new UIEventSource<LayerConfig>(undefined, "Selected layer")
|
this.selectedLayer = new UIEventSource<LayerConfig>(undefined, "Selected layer");
|
||||||
|
|
||||||
this.selectedElementAndLayer = this.selectedElement.mapD(
|
this.selectedElementAndLayer = this.selectedElement.mapD(
|
||||||
(feature) => {
|
(feature) => {
|
||||||
const layer = this.selectedLayer.data
|
const layer = this.selectedLayer.data;
|
||||||
if (!layer) {
|
if (!layer) {
|
||||||
return undefined
|
return undefined;
|
||||||
}
|
}
|
||||||
return { layer, feature }
|
return { layer, feature };
|
||||||
},
|
},
|
||||||
[this.selectedLayer]
|
[this.selectedLayer]
|
||||||
)
|
);
|
||||||
|
|
||||||
this.geolocation = new GeoLocationHandler(
|
this.geolocation = new GeoLocationHandler(
|
||||||
geolocationState,
|
geolocationState,
|
||||||
this.selectedElement,
|
this.selectedElement,
|
||||||
this.mapProperties,
|
this.mapProperties,
|
||||||
this.userRelatedState.gpsLocationHistoryRetentionTime
|
this.userRelatedState.gpsLocationHistoryRetentionTime
|
||||||
)
|
);
|
||||||
|
|
||||||
this.availableLayers = AvailableRasterLayers.layersAvailableAt(this.mapProperties.location)
|
this.availableLayers = AvailableRasterLayers.layersAvailableAt(this.mapProperties.location);
|
||||||
|
|
||||||
const self = this
|
const self = this;
|
||||||
this.layerState = new LayerState(this.osmConnection, layout.layers, layout.id)
|
this.layerState = new LayerState(this.osmConnection, layout.layers, layout.id);
|
||||||
|
|
||||||
{
|
{
|
||||||
const overlayLayerStates = new Map<string, { isDisplayed: UIEventSource<boolean> }>()
|
const overlayLayerStates = new Map<string, { isDisplayed: UIEventSource<boolean> }>();
|
||||||
for (const rasterInfo of this.layout.tileLayerSources) {
|
for (const rasterInfo of this.layout.tileLayerSources) {
|
||||||
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);
|
||||||
new ShowOverlayRasterLayer(rasterInfo, this.map, this.mapProperties, state)
|
new ShowOverlayRasterLayer(rasterInfo, this.map, this.mapProperties, state);
|
||||||
}
|
}
|
||||||
this.overlayLayerStates = overlayLayerStates
|
this.overlayLayerStates = overlayLayerStates;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -199,7 +198,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (this.layout.layers.some((l) => l._needsFullNodeDatabase)) {
|
if (this.layout.layers.some((l) => l._needsFullNodeDatabase)) {
|
||||||
this.fullNodeDatabase = new FullNodeDatabaseSource()
|
this.fullNodeDatabase = new FullNodeDatabaseSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
const layoutSource = new LayoutSource(
|
const layoutSource = new LayoutSource(
|
||||||
|
@ -209,49 +208,49 @@ 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,
|
||||||
this.fullNodeDatabase
|
this.fullNodeDatabase
|
||||||
)
|
);
|
||||||
|
|
||||||
this.indexedFeatures = layoutSource
|
this.indexedFeatures = layoutSource;
|
||||||
|
|
||||||
const empty = []
|
|
||||||
let currentViewIndex = 0
|
let currentViewIndex = 0
|
||||||
|
const empty = [];
|
||||||
this.currentView = new StaticFeatureSource(
|
this.currentView = new StaticFeatureSource(
|
||||||
this.mapProperties.bounds.map((bbox) => {
|
this.mapProperties.bounds.map((bbox) => {
|
||||||
if (!bbox) {
|
if (!bbox) {
|
||||||
return empty
|
return empty;
|
||||||
}
|
}
|
||||||
currentViewIndex++
|
currentViewIndex++;
|
||||||
return <Feature[]>[
|
return <Feature[]>[
|
||||||
bbox.asGeoJson({
|
bbox.asGeoJson({
|
||||||
zoom: this.mapProperties.zoom.data,
|
zoom: this.mapProperties.zoom.data,
|
||||||
...this.mapProperties.location.data,
|
...this.mapProperties.location.data,
|
||||||
id: "current_view",
|
id: "current_view_"+currentViewIndex
|
||||||
}),
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
)
|
];
|
||||||
this.featuresInView = new BBoxFeatureSource(layoutSource, this.mapProperties.bounds)
|
})
|
||||||
this.dataIsLoading = layoutSource.isLoading
|
);
|
||||||
|
this.featuresInView = new BBoxFeatureSource(layoutSource, this.mapProperties.bounds);
|
||||||
|
this.dataIsLoading = layoutSource.isLoading;
|
||||||
|
|
||||||
const indexedElements = this.indexedFeatures
|
const indexedElements = this.indexedFeatures;
|
||||||
this.featureProperties = new FeaturePropertiesStore(indexedElements)
|
this.featureProperties = new FeaturePropertiesStore(indexedElements);
|
||||||
this.changes = new Changes(
|
this.changes = new Changes(
|
||||||
{
|
{
|
||||||
dryRun: this.featureSwitches.featureSwitchIsTesting,
|
dryRun: this.featureSwitches.featureSwitchIsTesting,
|
||||||
allElements: indexedElements,
|
allElements: indexedElements,
|
||||||
featurePropertiesStore: this.featureProperties,
|
featurePropertiesStore: this.featureProperties,
|
||||||
osmConnection: this.osmConnection,
|
osmConnection: this.osmConnection,
|
||||||
historicalUserLocations: this.geolocation.historicalUserLocations,
|
historicalUserLocations: this.geolocation.historicalUserLocations
|
||||||
},
|
},
|
||||||
layout?.isLeftRightSensitive() ?? false
|
layout?.isLeftRightSensitive() ?? false
|
||||||
)
|
);
|
||||||
this.historicalUserLocations = this.geolocation.historicalUserLocations
|
this.historicalUserLocations = this.geolocation.historicalUserLocations;
|
||||||
this.newFeatures = new NewGeometryFromChangesFeatureSource(
|
this.newFeatures = new NewGeometryFromChangesFeatureSource(
|
||||||
this.changes,
|
this.changes,
|
||||||
indexedElements,
|
indexedElements,
|
||||||
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(
|
||||||
|
@ -267,11 +266,11 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
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;
|
||||||
}
|
}
|
||||||
this.perLayer.forEach((fs) => {
|
this.perLayer.forEach((fs) => {
|
||||||
new SaveFeatureSourceToLocalStorage(
|
new SaveFeatureSourceToLocalStorage(
|
||||||
|
@ -281,80 +280,80 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
fs,
|
fs,
|
||||||
this.featureProperties,
|
this.featureProperties,
|
||||||
fs.layer.layerDef.maxAgeOfCache
|
fs.layer.layerDef.maxAgeOfCache
|
||||||
)
|
);
|
||||||
})
|
});
|
||||||
|
this.newPointDialog = this.layerState.filteredLayers.get("last_click");
|
||||||
|
|
||||||
this.floors = this.featuresInView.features.stabilized(500).map((features) => {
|
this.floors = this.featuresInView.features.stabilized(500).map((features) => {
|
||||||
if (!features) {
|
if (!features) {
|
||||||
return []
|
return [];
|
||||||
}
|
}
|
||||||
const floors = new Set<string>()
|
const floors = new Set<string>();
|
||||||
for (const feature of features) {
|
for (const feature of features) {
|
||||||
let level = feature.properties["_level"]
|
let level = feature.properties["_level"];
|
||||||
if (level) {
|
if (level) {
|
||||||
const levels = level.split(";")
|
const levels = level.split(";");
|
||||||
for (const l of levels) {
|
for (const l of levels) {
|
||||||
floors.add(l)
|
floors.add(l);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
floors.add("0") // '0' is the default and is thus _always_ present
|
floors.add("0"); // '0' is the default and is thus _always_ present
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const sorted = Array.from(floors)
|
const sorted = Array.from(floors);
|
||||||
// Sort alphabetically first, to deal with floor "A", "B" and "C"
|
// Sort alphabetically first, to deal with floor "A", "B" and "C"
|
||||||
sorted.sort()
|
sorted.sort();
|
||||||
sorted.sort((a, b) => {
|
sorted.sort((a, b) => {
|
||||||
// We use the laxer 'parseInt' to deal with floor '1A'
|
// We use the laxer 'parseInt' to deal with floor '1A'
|
||||||
const na = parseInt(a)
|
const na = parseInt(a);
|
||||||
const nb = parseInt(b)
|
const nb = parseInt(b);
|
||||||
if (isNaN(na) || isNaN(nb)) {
|
if (isNaN(na) || isNaN(nb)) {
|
||||||
return 0
|
return 0;
|
||||||
}
|
}
|
||||||
return na - nb
|
return na - nb;
|
||||||
})
|
});
|
||||||
sorted.reverse(/* new list, no side-effects */)
|
sorted.reverse(/* new list, no side-effects */);
|
||||||
return sorted
|
return sorted;
|
||||||
})
|
});
|
||||||
|
|
||||||
const lastClick = (this.lastClickObject = new LastClickFeatureSource(
|
const lastClick = (this.lastClickObject = new LastClickFeatureSource(
|
||||||
this.mapProperties.lastClickLocation,
|
this.mapProperties.lastClickLocation,
|
||||||
this.layout
|
this.layout
|
||||||
))
|
));
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
this.hasDataInView = new NoElementsInViewDetector(this).hasFeatureInView
|
this.hasDataInView = new NoElementsInViewDetector(this).hasFeatureInView;
|
||||||
this.imageUploadManager = new ImageUploadManager(
|
this.imageUploadManager = new ImageUploadManager(
|
||||||
layout,
|
layout,
|
||||||
Imgur.singleton,
|
Imgur.singleton,
|
||||||
this.featureProperties,
|
this.featureProperties,
|
||||||
this.osmConnection,
|
this.osmConnection,
|
||||||
this.changes
|
this.changes
|
||||||
)
|
);
|
||||||
|
|
||||||
this.initActors()
|
this.initActors();
|
||||||
this.addLastClick(lastClick)
|
this.drawSpecialLayers();
|
||||||
this.drawSpecialLayers()
|
this.initHotkeys();
|
||||||
this.initHotkeys()
|
this.miscSetup();
|
||||||
this.miscSetup()
|
|
||||||
if (!Utils.runningFromConsole) {
|
if (!Utils.runningFromConsole) {
|
||||||
console.log("State setup completed", this)
|
console.log("State setup completed", this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public showNormalDataOn(map: Store<MlMap>): ReadonlyMap<string, FilteringFeatureSource> {
|
public showNormalDataOn(map: Store<MlMap>): ReadonlyMap<string, FilteringFeatureSource> {
|
||||||
const filteringFeatureSource = new Map<string, FilteringFeatureSource>()
|
const filteringFeatureSource = new Map<string, FilteringFeatureSource>();
|
||||||
this.perLayer.forEach((fs, layerName) => {
|
this.perLayer.forEach((fs, layerName) => {
|
||||||
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) {
|
||||||
/* This layer is hidden and there is no way to enable it (filterview is disabled or this layer doesn't show up in the filter view as the name is not defined)
|
/* This layer is hidden and there is no way to enable it (filterview is disabled or this layer doesn't show up in the filter view as the name is not defined)
|
||||||
|
@ -364,15 +363,15 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
* Note: it is tempting to also permanently disable the layer if it is not visible _and_ the layer name is hidden.
|
* Note: it is tempting to also permanently disable the layer if it is not visible _and_ the layer name is hidden.
|
||||||
* However, this is _not_ correct: the layer might be hidden because zoom is not enough. Zooming in more _will_ reveal the layer!
|
* However, this is _not_ correct: the layer might be hidden because zoom is not enough. Zooming in more _will_ reveal the layer!
|
||||||
* */
|
* */
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const filtered = new FilteringFeatureSource(
|
const filtered = new FilteringFeatureSource(
|
||||||
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);
|
||||||
|
|
||||||
new ShowDataLayer(map, {
|
new ShowDataLayer(map, {
|
||||||
layer: fs.layer.layerDef,
|
layer: fs.layer.layerDef,
|
||||||
|
@ -380,30 +379,30 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
doShowLayer,
|
doShowLayer,
|
||||||
selectedElement: this.selectedElement,
|
selectedElement: this.selectedElement,
|
||||||
selectedLayer: this.selectedLayer,
|
selectedLayer: this.selectedLayer,
|
||||||
fetchStore: (id) => this.featureProperties.getStore(id),
|
fetchStore: (id) => this.featureProperties.getStore(id)
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
return filteringFeatureSource
|
return filteringFeatureSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Various small methods that need to be called
|
* Various small methods that need to be called
|
||||||
*/
|
*/
|
||||||
private miscSetup() {
|
private miscSetup() {
|
||||||
this.userRelatedState.markLayoutAsVisited(this.layout)
|
this.userRelatedState.markLayoutAsVisited(this.layout);
|
||||||
|
|
||||||
this.selectedElement.addCallbackAndRunD((feature) => {
|
this.selectedElement.addCallbackAndRunD((feature) => {
|
||||||
// As soon as we have a selected element, we clear the selected element
|
// As soon as we have a selected element, we clear the selected element
|
||||||
// This is to work around maplibre, which'll _first_ register the click on the map and only _then_ on the feature
|
// This is to work around maplibre, which'll _first_ register the click on the map and only _then_ on the feature
|
||||||
// The only exception is if the last element is the 'add_new'-button, as we don't want it to disappear
|
// The only exception is if the last element is the 'add_new'-button, as we don't want it to disappear
|
||||||
if (feature.properties.id === "last_click") {
|
if (feature.properties.id === "last_click") {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
this.lastClickObject.features.setData([])
|
this.lastClickObject.features.setData([]);
|
||||||
})
|
});
|
||||||
|
|
||||||
if (this.layout.customCss !== undefined && window.location.pathname.indexOf("theme") >= 0) {
|
if (this.layout.customCss !== undefined && window.location.pathname.indexOf("theme") >= 0) {
|
||||||
Utils.LoadCustomCss(this.layout.customCss)
|
Utils.LoadCustomCss(this.layout.customCss);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -412,97 +411,116 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
{ nomod: "Escape", onUp: true },
|
{ nomod: "Escape", onUp: true },
|
||||||
Translations.t.hotkeyDocumentation.closeSidebar,
|
Translations.t.hotkeyDocumentation.closeSidebar,
|
||||||
() => {
|
() => {
|
||||||
this.selectedElement.setData(undefined)
|
this.selectedElement.setData(undefined);
|
||||||
this.guistate.closeAll()
|
this.guistate.closeAll();
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
|
|
||||||
|
this.featureSwitches.featureSwitchBackgroundSelection.addCallbackAndRun(enable => {
|
||||||
|
if(!enable){
|
||||||
|
return
|
||||||
|
}
|
||||||
Hotkeys.RegisterHotkey(
|
Hotkeys.RegisterHotkey(
|
||||||
{
|
{
|
||||||
nomod: "b",
|
nomod: "b"
|
||||||
},
|
},
|
||||||
Translations.t.hotkeyDocumentation.openLayersPanel,
|
Translations.t.hotkeyDocumentation.openLayersPanel,
|
||||||
() => {
|
() => {
|
||||||
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;
|
||||||
const current = this.mapProperties.rasterLayer
|
const current = this.mapProperties.rasterLayer;
|
||||||
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);
|
||||||
}
|
};
|
||||||
|
|
||||||
Hotkeys.RegisterHotkey(
|
Hotkeys.RegisterHotkey(
|
||||||
{ nomod: "O" },
|
{ nomod: "O" },
|
||||||
Translations.t.hotkeyDocumentation.selectOsmbasedmap,
|
Translations.t.hotkeyDocumentation.selectOsmbasedmap,
|
||||||
() => setLayerCategory("osmbasedmap")
|
() => setLayerCategory("osmbasedmap")
|
||||||
)
|
);
|
||||||
|
|
||||||
Hotkeys.RegisterHotkey({ nomod: "M" }, Translations.t.hotkeyDocumentation.selectMap, () =>
|
Hotkeys.RegisterHotkey({ nomod: "M" }, 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")
|
||||||
)
|
);
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private addLastClick(last_click: LastClickFeatureSource) {
|
private addLastClick(last_click: LastClickFeatureSource) {
|
||||||
// The last_click gets a _very_ special treatment as it interacts with various parts
|
// The last_click gets a _very_ special treatment as it interacts with various parts
|
||||||
|
|
||||||
const last_click_layer = this.layerState.filteredLayers.get("last_click")
|
this.featureProperties.trackFeatureSource(last_click);
|
||||||
this.featureProperties.trackFeatureSource(last_click)
|
this.indexedFeatures.addSource(last_click);
|
||||||
this.indexedFeatures.addSource(last_click)
|
|
||||||
|
|
||||||
last_click.features.addCallbackAndRunD((features) => {
|
last_click.features.addCallbackAndRunD((features) => {
|
||||||
if (this.selectedLayer.data?.id === "last_click") {
|
if (this.selectedLayer.data?.id === "last_click") {
|
||||||
// The last-click location moved, but we have selected the last click of the previous location
|
// The last-click location moved, but we have selected the last click of the previous location
|
||||||
// So, we update _after_ clearing the selection to make sure no stray data is sticking around
|
// So, we update _after_ clearing the selection to make sure no stray data is sticking around
|
||||||
this.selectedElement.setData(undefined)
|
this.selectedElement.setData(undefined);
|
||||||
this.selectedElement.setData(features[0])
|
this.selectedElement.setData(features[0]);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
new ShowDataLayer(this.map, {
|
new ShowDataLayer(this.map, {
|
||||||
features: new FilteringFeatureSource(last_click_layer, last_click),
|
features: new FilteringFeatureSource(this.newPointDialog, last_click),
|
||||||
doShowLayer: this.featureSwitches.featureSwitchEnableLogin,
|
doShowLayer: this.featureSwitches.featureSwitchEnableLogin,
|
||||||
layer: last_click_layer.layerDef,
|
layer: this.newPointDialog.layerDef,
|
||||||
selectedElement: this.selectedElement,
|
selectedElement: this.selectedElement,
|
||||||
selectedLayer: this.selectedLayer,
|
selectedLayer: this.selectedLayer,
|
||||||
onClick: (feature: Feature) => {
|
onClick: (feature: Feature) => {
|
||||||
if (this.mapProperties.zoom.data < Constants.minZoomLevelToAddNewPoint) {
|
if (this.mapProperties.zoom.data < Constants.minZoomLevelToAddNewPoint) {
|
||||||
this.map.data.flyTo({
|
this.map.data.flyTo({
|
||||||
zoom: Constants.minZoomLevelToAddNewPoint,
|
zoom: Constants.minZoomLevelToAddNewPoint,
|
||||||
center: this.mapProperties.lastClickLocation.data,
|
center: this.mapProperties.lastClickLocation.data
|
||||||
})
|
});
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
// We first clear the selection to make sure no weird state is around
|
// We first clear the selection to make sure no weird state is around
|
||||||
this.selectedLayer.setData(undefined)
|
this.selectedLayer.setData(undefined);
|
||||||
this.selectedElement.setData(undefined)
|
this.selectedElement.setData(undefined);
|
||||||
|
|
||||||
this.selectedElement.setData(feature)
|
this.selectedElement.setData(feature);
|
||||||
this.selectedLayer.setData(last_click_layer.layerDef)
|
this.selectedLayer.setData(this.newPointDialog.layerDef);
|
||||||
},
|
}
|
||||||
})
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public openNewDialog() {
|
||||||
|
this.selectedLayer.setData(undefined);
|
||||||
|
this.selectedElement.setData(undefined);
|
||||||
|
|
||||||
|
const { lon, lat } = this.mapProperties.location.data;
|
||||||
|
const feature = this.lastClickObject.createFeature(lon, lat)
|
||||||
|
this.featureProperties.trackFeature(feature)
|
||||||
|
this.selectedElement.setData(feature);
|
||||||
|
this.selectedLayer.setData(this.newPointDialog.layerDef);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -510,7 +528,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
*/
|
*/
|
||||||
private drawSpecialLayers() {
|
private drawSpecialLayers() {
|
||||||
type AddedByDefaultTypes = (typeof Constants.added_by_default)[number]
|
type AddedByDefaultTypes = (typeof Constants.added_by_default)[number]
|
||||||
const empty = []
|
const empty = [];
|
||||||
/**
|
/**
|
||||||
* A listing which maps the layerId onto the featureSource
|
* A listing which maps the layerId onto the featureSource
|
||||||
*/
|
*/
|
||||||
|
@ -530,21 +548,21 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
bbox === undefined ? empty : <Feature[]>[bbox.asGeoJson({ id: "range" })]
|
bbox === undefined ? empty : <Feature[]>[bbox.asGeoJson({ id: "range" })]
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
current_view: this.currentView,
|
current_view: this.currentView
|
||||||
}
|
};
|
||||||
if (this.layout?.lockLocation) {
|
if (this.layout?.lockLocation) {
|
||||||
const bbox = new BBox(this.layout.lockLocation)
|
const bbox = new BBox(this.layout.lockLocation);
|
||||||
this.mapProperties.maxbounds.setData(bbox)
|
this.mapProperties.maxbounds.setData(bbox);
|
||||||
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");
|
||||||
if (currentViewLayer?.tagRenderings?.length > 0) {
|
if (currentViewLayer?.tagRenderings?.length > 0) {
|
||||||
const params = MetaTagging.createExtraFuncParams(this)
|
const params = MetaTagging.createExtraFuncParams(this);
|
||||||
this.featureProperties.trackFeatureSource(specialLayers.current_view)
|
this.featureProperties.trackFeatureSource(specialLayers.current_view);
|
||||||
specialLayers.current_view.features.addCallbackAndRunD((features) => {
|
specialLayers.current_view.features.addCallbackAndRunD((features) => {
|
||||||
MetaTagging.addMetatags(
|
MetaTagging.addMetatags(
|
||||||
features,
|
features,
|
||||||
|
@ -553,36 +571,36 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
this.layout,
|
this.layout,
|
||||||
this.osmObjectDownloader,
|
this.osmObjectDownloader,
|
||||||
this.featureProperties
|
this.featureProperties
|
||||||
)
|
);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const rangeFLayer: FilteredLayer = this.layerState.filteredLayers.get("range")
|
const rangeFLayer: FilteredLayer = this.layerState.filteredLayers.get("range");
|
||||||
|
|
||||||
const rangeIsDisplayed = rangeFLayer?.isDisplayed
|
const rangeIsDisplayed = rangeFLayer?.isDisplayed;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!QueryParameters.wasInitialized(FilteredLayer.queryParameterKey(rangeFLayer.layerDef))
|
!QueryParameters.wasInitialized(FilteredLayer.queryParameterKey(rangeFLayer.layerDef))
|
||||||
) {
|
) {
|
||||||
rangeIsDisplayed?.syncWith(this.featureSwitches.featureSwitchIsTesting, true)
|
rangeIsDisplayed?.syncWith(this.featureSwitches.featureSwitchIsTesting, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.layerState.filteredLayers.forEach((flayer) => {
|
this.layerState.filteredLayers.forEach((flayer) => {
|
||||||
const id = flayer.layerDef.id
|
const id = flayer.layerDef.id;
|
||||||
const features: FeatureSource = specialLayers[id]
|
const features: FeatureSource = specialLayers[id];
|
||||||
if (features === undefined) {
|
if (features === undefined) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.featureProperties.trackFeatureSource(features)
|
this.featureProperties.trackFeatureSource(features);
|
||||||
new ShowDataLayer(this.map, {
|
new ShowDataLayer(this.map, {
|
||||||
features,
|
features,
|
||||||
doShowLayer: flayer.isDisplayed,
|
doShowLayer: flayer.isDisplayed,
|
||||||
layer: flayer.layerDef,
|
layer: flayer.layerDef,
|
||||||
selectedElement: this.selectedElement,
|
selectedElement: this.selectedElement,
|
||||||
selectedLayer: this.selectedLayer,
|
selectedLayer: this.selectedLayer
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -591,35 +609,35 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
private initActors() {
|
private initActors() {
|
||||||
// Unselect the selected element if it is panned out of view
|
// Unselect the selected element if it is panned out of view
|
||||||
this.mapProperties.bounds.stabilized(250).addCallbackD((bounds) => {
|
this.mapProperties.bounds.stabilized(250).addCallbackD((bounds) => {
|
||||||
const selected = this.selectedElement.data
|
const selected = this.selectedElement.data;
|
||||||
if (selected === undefined) {
|
if (selected === undefined) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const bbox = BBox.get(selected)
|
const bbox = BBox.get(selected);
|
||||||
if (!bbox.overlapsWith(bounds)) {
|
if (!bbox.overlapsWith(bounds)) {
|
||||||
this.selectedElement.setData(undefined)
|
this.selectedElement.setData(undefined);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
this.selectedElement.addCallback((selected) => {
|
this.selectedElement.addCallback((selected) => {
|
||||||
if (selected === undefined) {
|
if (selected === undefined) {
|
||||||
// We did _unselect_ an item - we always remove the lastclick-object
|
// We did _unselect_ an item - we always remove the lastclick-object
|
||||||
this.lastClickObject.features.setData([])
|
this.lastClickObject.features.setData([]);
|
||||||
this.selectedLayer.setData(undefined)
|
this.selectedLayer.setData(undefined);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
new ThemeViewStateHashActor(this)
|
new ThemeViewStateHashActor(this);
|
||||||
new MetaTagging(this)
|
new MetaTagging(this);
|
||||||
new TitleHandler(this.selectedElement, this.selectedLayer, this.featureProperties, this)
|
new TitleHandler(this.selectedElement, this.selectedLayer, this.featureProperties, this);
|
||||||
new ChangeToElementsActor(this.changes, this.featureProperties)
|
new ChangeToElementsActor(this.changes, this.featureProperties);
|
||||||
new PendingChangesUploader(this.changes, this.selectedElement)
|
new PendingChangesUploader(this.changes, this.selectedElement);
|
||||||
new SelectedElementTagsUpdater(this)
|
new SelectedElementTagsUpdater(this);
|
||||||
new BackgroundLayerResetter(this.mapProperties.rasterLayer, this.availableLayers)
|
new BackgroundLayerResetter(this.mapProperties.rasterLayer, this.availableLayers);
|
||||||
new PreferredRasterLayerSelector(
|
new PreferredRasterLayerSelector(
|
||||||
this.mapProperties.rasterLayer,
|
this.mapProperties.rasterLayer,
|
||||||
this.availableLayers,
|
this.availableLayers,
|
||||||
this.featureSwitches.backgroundLayerId,
|
this.featureSwitches.backgroundLayerId,
|
||||||
this.userRelatedState.preferredBackgroundLayer
|
this.userRelatedState.preferredBackgroundLayer
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,28 @@
|
||||||
let id = Math.random() * 1000000000 + ""
|
let id = Math.random() * 1000000000 + ""
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<form>
|
<form on:change|preventDefault={() => {
|
||||||
|
drawAttention = false
|
||||||
|
dispatcher("submit", inputElement.files)
|
||||||
|
}}
|
||||||
|
on:dragend={() => {
|
||||||
|
console.log("Drag end")
|
||||||
|
drawAttention = false
|
||||||
|
}}
|
||||||
|
on:dragenter|preventDefault|stopPropagation={(e) => {
|
||||||
|
console.log("Dragging enter")
|
||||||
|
drawAttention = true
|
||||||
|
e.dataTransfer.drop = "copy"
|
||||||
|
}}
|
||||||
|
on:dragstart={() => {
|
||||||
|
console.log("DragStart")
|
||||||
|
drawAttention = false
|
||||||
|
}}
|
||||||
|
on:drop|preventDefault|stopPropagation={(e) => {
|
||||||
|
console.log("Got a 'drop'")
|
||||||
|
drawAttention = false
|
||||||
|
dispatcher("submit", e.dataTransfer.files)
|
||||||
|
}}>
|
||||||
<label class={twMerge(cls, drawAttention ? "glowing-shadow" : "")} for={"fileinput" + id}>
|
<label class={twMerge(cls, drawAttention ? "glowing-shadow" : "")} for={"fileinput" + id}>
|
||||||
<slot />
|
<slot />
|
||||||
</label>
|
</label>
|
||||||
|
@ -23,26 +44,7 @@
|
||||||
id={"fileinput" + id}
|
id={"fileinput" + id}
|
||||||
{multiple}
|
{multiple}
|
||||||
name="file-input"
|
name="file-input"
|
||||||
on:change|preventDefault={() => {
|
|
||||||
drawAttention = false
|
|
||||||
dispatcher("submit", inputElement.files)
|
|
||||||
}}
|
|
||||||
on:dragend={() => {
|
|
||||||
drawAttention = false
|
|
||||||
}}
|
|
||||||
on:dragover|preventDefault|stopPropagation={(e) => {
|
|
||||||
console.log("Dragging over!")
|
|
||||||
drawAttention = true
|
|
||||||
e.dataTransfer.drop = "copy"
|
|
||||||
}}
|
|
||||||
on:dragstart={() => {
|
|
||||||
drawAttention = false
|
|
||||||
}}
|
|
||||||
on:drop|preventDefault|stopPropagation={(e) => {
|
|
||||||
console.log("Got a 'drop'")
|
|
||||||
drawAttention = false
|
|
||||||
dispatcher("submit", e.dataTransfer.files)
|
|
||||||
}}
|
|
||||||
type="file"
|
type="file"
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -11,8 +11,9 @@
|
||||||
<div
|
<div
|
||||||
class="absolute top-0 right-0 h-screen w-screen p-4 md:p-6"
|
class="absolute top-0 right-0 h-screen w-screen p-4 md:p-6"
|
||||||
style="background-color: #00000088"
|
style="background-color: #00000088"
|
||||||
|
on:click={() => {dispatch("close")}}
|
||||||
>
|
>
|
||||||
<div class="content normal-background">
|
<div class="content normal-background" on:click|stopPropagation={() => {}}>
|
||||||
<div class="h-full rounded-xl">
|
<div class="h-full rounded-xl">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -22,7 +22,14 @@ export default class Hotkeys {
|
||||||
}[]
|
}[]
|
||||||
>([])
|
>([])
|
||||||
|
|
||||||
private static textElementSelected(): boolean {
|
private static textElementSelected(event: KeyboardEvent): boolean {
|
||||||
|
if(event.ctrlKey || event.altKey){
|
||||||
|
// This is an event with a modifier-key, lets not ignore it
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if(event.key === "Escape"){
|
||||||
|
return false // Another not-printable character that should not be ignored
|
||||||
|
}
|
||||||
return ["input", "textarea"].includes(document?.activeElement?.tagName?.toLowerCase())
|
return ["input", "textarea"].includes(document?.activeElement?.tagName?.toLowerCase())
|
||||||
}
|
}
|
||||||
public static RegisterHotkey(
|
public static RegisterHotkey(
|
||||||
|
@ -68,7 +75,7 @@ export default class Hotkeys {
|
||||||
})
|
})
|
||||||
} else if (key["shift"] !== undefined) {
|
} else if (key["shift"] !== undefined) {
|
||||||
document.addEventListener(type, function (event) {
|
document.addEventListener(type, function (event) {
|
||||||
if (Hotkeys.textElementSelected()) {
|
if (Hotkeys.textElementSelected(event)) {
|
||||||
// A text element is selected, we don't do anything special
|
// A text element is selected, we don't do anything special
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -86,7 +93,7 @@ export default class Hotkeys {
|
||||||
})
|
})
|
||||||
} else if (key["nomod"] !== undefined) {
|
} else if (key["nomod"] !== undefined) {
|
||||||
document.addEventListener(type, function (event) {
|
document.addEventListener(type, function (event) {
|
||||||
if (Hotkeys.textElementSelected()) {
|
if (Hotkeys.textElementSelected(event)) {
|
||||||
// A text element is selected, we don't do anything special
|
// A text element is selected, we don't do anything special
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,28 @@
|
||||||
* Thin wrapper around 'TabGroup' which binds the state
|
* Thin wrapper around 'TabGroup' which binds the state
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@rgossiaux/svelte-headlessui"
|
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@rgossiaux/svelte-headlessui";
|
||||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource";
|
||||||
import { twJoin } from "tailwind-merge"
|
import { twJoin } from "tailwind-merge";
|
||||||
|
|
||||||
export let tab: UIEventSource<number>
|
/**
|
||||||
let tabElements: HTMLElement[] = []
|
* If a condition is given for a certain tab, it will only be shown if this condition is true.
|
||||||
$: tabElements[$tab]?.click()
|
* E.g.
|
||||||
|
* condition3 = new ImmutableStore(false) will always hide tab3 (the fourth tab)
|
||||||
|
*/
|
||||||
|
let tr = new ImmutableStore(true)
|
||||||
|
export let condition0: Store<boolean> = tr
|
||||||
|
export let condition1: Store<boolean> = tr
|
||||||
|
export let condition2: Store<boolean> = tr
|
||||||
|
export let condition3: Store<boolean> = tr
|
||||||
|
export let condition4: Store<boolean> = tr
|
||||||
|
|
||||||
|
export let tab: UIEventSource<number>;
|
||||||
|
let tabElements: HTMLElement[] = [];
|
||||||
|
$: tabElements[$tab]?.click();
|
||||||
$: {
|
$: {
|
||||||
if (tabElements[tab.data]) {
|
if (tabElements[tab.data]) {
|
||||||
window.setTimeout(() => tabElements[tab.data].click(), 50)
|
window.setTimeout(() => tabElements[tab.data].click(), 50);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -29,41 +41,31 @@
|
||||||
>
|
>
|
||||||
<div class="interactive sticky top-0 flex items-center justify-between">
|
<div class="interactive sticky top-0 flex items-center justify-between">
|
||||||
<TabList class="flex flex-wrap">
|
<TabList class="flex flex-wrap">
|
||||||
{#if $$slots.title1}
|
<Tab class={({ selected }) => twJoin("tab", selected && "primary", !$condition0 && "hidden")}>
|
||||||
<Tab class={({ selected }) => twJoin("tab", selected && "primary")}>
|
|
||||||
<div bind:this={tabElements[0]} class="flex">
|
<div bind:this={tabElements[0]} class="flex">
|
||||||
<slot name="title0">Tab 0</slot>
|
<slot name="title0">Tab 0</slot>
|
||||||
</div>
|
</div>
|
||||||
</Tab>
|
</Tab>
|
||||||
{/if}
|
<Tab class={({ selected }) => twJoin("tab", selected && "primary", !$condition1 && "hidden")}>
|
||||||
{#if $$slots.title1}
|
|
||||||
<Tab class={({ selected }) => twJoin("tab", selected && "primary")}>
|
|
||||||
<div bind:this={tabElements[1]} class="flex">
|
<div bind:this={tabElements[1]} class="flex">
|
||||||
<slot name="title1" />
|
<slot name="title1" />
|
||||||
</div>
|
</div>
|
||||||
</Tab>
|
</Tab>
|
||||||
{/if}
|
<Tab class={({ selected }) => twJoin("tab", selected && "primary", !$condition2 && "hidden")}>
|
||||||
{#if $$slots.title2}
|
|
||||||
<Tab class={({ selected }) => twJoin("tab", selected && "primary")}>
|
|
||||||
<div bind:this={tabElements[2]} class="flex">
|
<div bind:this={tabElements[2]} class="flex">
|
||||||
<slot name="title2" />
|
<slot name="title2" />
|
||||||
</div>
|
</div>
|
||||||
</Tab>
|
</Tab>
|
||||||
{/if}
|
<Tab class={({ selected }) => twJoin("tab", selected && "primary", !$condition3 && "hidden")}>
|
||||||
{#if $$slots.title3}
|
|
||||||
<Tab class={({ selected }) => twJoin("tab", selected && "primary")}>
|
|
||||||
<div bind:this={tabElements[3]} class="flex">
|
<div bind:this={tabElements[3]} class="flex">
|
||||||
<slot name="title3" />
|
<slot name="title3" />
|
||||||
</div>
|
</div>
|
||||||
</Tab>
|
</Tab>
|
||||||
{/if}
|
<Tab class={({ selected }) => twJoin("tab", selected && "primary", !$condition4 && "hidden")}>
|
||||||
{#if $$slots.title4}
|
|
||||||
<Tab class={({ selected }) => twJoin("tab", selected && "primary")}>
|
|
||||||
<div bind:this={tabElements[4]} class="flex">
|
<div bind:this={tabElements[4]} class="flex">
|
||||||
<slot name="title4" />
|
<slot name="title4" />
|
||||||
</div>
|
</div>
|
||||||
</Tab>
|
</Tab>
|
||||||
{/if}
|
|
||||||
</TabList>
|
</TabList>
|
||||||
<slot name="post-tablist" />
|
<slot name="post-tablist" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -75,16 +77,24 @@
|
||||||
</slot>
|
</slot>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel class="tabpanel">
|
<TabPanel class="tabpanel">
|
||||||
<slot name="content1" />
|
<slot name="content1">
|
||||||
|
<div />
|
||||||
|
</slot>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel class="tabpanel">
|
<TabPanel class="tabpanel">
|
||||||
<slot name="content2" />
|
<slot name="content2">
|
||||||
|
<div />
|
||||||
|
</slot>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel class="tabpanel">
|
<TabPanel class="tabpanel">
|
||||||
<slot name="content3" />
|
<slot name="content3">
|
||||||
|
<div />
|
||||||
|
</slot>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel class="tabpanel">
|
<TabPanel class="tabpanel">
|
||||||
<slot name="content4" />
|
<slot name="content4">
|
||||||
|
<div />
|
||||||
|
</slot>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabPanels>
|
</TabPanels>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,45 +1,46 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Translations from "../i18n/Translations"
|
import Translations from "../i18n/Translations";
|
||||||
import Svg from "../../Svg"
|
import Svg from "../../Svg";
|
||||||
import Tr from "../Base/Tr.svelte"
|
import Tr from "../Base/Tr.svelte";
|
||||||
import NextButton from "../Base/NextButton.svelte"
|
import NextButton from "../Base/NextButton.svelte";
|
||||||
import Geosearch from "./Geosearch.svelte"
|
import Geosearch from "./Geosearch.svelte";
|
||||||
import ToSvelte from "../Base/ToSvelte.svelte"
|
import ToSvelte from "../Base/ToSvelte.svelte";
|
||||||
import ThemeViewState from "../../Models/ThemeViewState"
|
import ThemeViewState from "../../Models/ThemeViewState";
|
||||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
import { Store, UIEventSource } from "../../Logic/UIEventSource";
|
||||||
import { SearchIcon } from "@rgossiaux/svelte-heroicons/solid"
|
import { SearchIcon } from "@rgossiaux/svelte-heroicons/solid";
|
||||||
import { twJoin } from "tailwind-merge"
|
import { twJoin } from "tailwind-merge";
|
||||||
import { Utils } from "../../Utils"
|
import { Utils } from "../../Utils";
|
||||||
import type { GeolocationPermissionState } from "../../Logic/State/GeoLocationState"
|
import type { GeolocationPermissionState } from "../../Logic/State/GeoLocationState";
|
||||||
|
import If from "../Base/If.svelte";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The theme introduction panel
|
* The theme introduction panel
|
||||||
*/
|
*/
|
||||||
export let state: ThemeViewState
|
export let state: ThemeViewState;
|
||||||
let layout = state.layout
|
let layout = state.layout;
|
||||||
let selectedElement = state.selectedElement
|
let selectedElement = state.selectedElement;
|
||||||
let selectedLayer = state.selectedLayer
|
let selectedLayer = state.selectedLayer;
|
||||||
|
|
||||||
let triggerSearch: UIEventSource<any> = new UIEventSource<any>(undefined)
|
let triggerSearch: UIEventSource<any> = new UIEventSource<any>(undefined);
|
||||||
let searchEnabled = false
|
let searchEnabled = false;
|
||||||
|
|
||||||
let geopermission: Store<GeolocationPermissionState> =
|
let geopermission: Store<GeolocationPermissionState> =
|
||||||
state.geolocation.geolocationState.permission
|
state.geolocation.geolocationState.permission;
|
||||||
let currentGPSLocation = state.geolocation.geolocationState.currentGPSLocation
|
let currentGPSLocation = state.geolocation.geolocationState.currentGPSLocation;
|
||||||
|
|
||||||
geopermission.addCallback((perm) => console.log(">>>> Permission", perm))
|
geopermission.addCallback((perm) => console.log(">>>> Permission", perm));
|
||||||
|
|
||||||
function jumpToCurrentLocation() {
|
function jumpToCurrentLocation() {
|
||||||
const glstate = state.geolocation.geolocationState
|
const glstate = state.geolocation.geolocationState;
|
||||||
if (glstate.currentGPSLocation.data !== undefined) {
|
if (glstate.currentGPSLocation.data !== undefined) {
|
||||||
const c: GeolocationCoordinates = glstate.currentGPSLocation.data
|
const c: GeolocationCoordinates = glstate.currentGPSLocation.data;
|
||||||
state.guistate.themeIsOpened.setData(false)
|
state.guistate.themeIsOpened.setData(false);
|
||||||
const coor = { lon: c.longitude, lat: c.latitude }
|
const coor = { lon: c.longitude, lat: c.latitude };
|
||||||
state.mapProperties.location.setData(coor)
|
state.mapProperties.location.setData(coor);
|
||||||
}
|
}
|
||||||
if (glstate.permission.data !== "granted") {
|
if (glstate.permission.data !== "granted") {
|
||||||
glstate.requestPermission()
|
glstate.requestPermission();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -62,7 +63,9 @@
|
||||||
</div>
|
</div>
|
||||||
</NextButton>
|
</NextButton>
|
||||||
|
|
||||||
|
|
||||||
<div class="flex w-full flex-wrap sm:flex-nowrap">
|
<div class="flex w-full flex-wrap sm:flex-nowrap">
|
||||||
|
<If condition={state.featureSwitches.featureSwitchGeolocation}>
|
||||||
{#if $currentGPSLocation !== undefined || $geopermission === "prompt"}
|
{#if $currentGPSLocation !== undefined || $geopermission === "prompt"}
|
||||||
<button class="flex w-full items-center gap-x-2" on:click={jumpToCurrentLocation}>
|
<button class="flex w-full items-center gap-x-2" on:click={jumpToCurrentLocation}>
|
||||||
<ToSvelte construct={Svg.crosshair_svg().SetClass("w-8 h-8")} />
|
<ToSvelte construct={Svg.crosshair_svg().SetClass("w-8 h-8")} />
|
||||||
|
@ -94,7 +97,10 @@
|
||||||
<Tr t={Translations.t.general.waitingForLocation} />
|
<Tr t={Translations.t.general.waitingForLocation} />
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
</If>
|
||||||
|
|
||||||
|
|
||||||
|
<If condition={state.featureSwitches.featureSwitchSearch}>
|
||||||
<div class=".button low-interaction m-1 flex w-full items-center gap-x-2 rounded border p-2">
|
<div class=".button low-interaction m-1 flex w-full items-center gap-x-2 rounded border p-2">
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<Geosearch
|
<Geosearch
|
||||||
|
@ -117,6 +123,7 @@
|
||||||
<SearchIcon class="h-6 w-6" />
|
<SearchIcon class="h-6 w-6" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</If>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
import { Store, UIEventSource } from "../../Logic/UIEventSource";
|
||||||
import type { Map as MLMap } from "maplibre-gl"
|
import type { Map as MLMap } from "maplibre-gl";
|
||||||
import { Map as MlMap, SourceSpecification } from "maplibre-gl"
|
import { Map as MlMap, SourceSpecification } from "maplibre-gl";
|
||||||
import { AvailableRasterLayers, RasterLayerPolygon } from "../../Models/RasterLayers"
|
import { AvailableRasterLayers, RasterLayerPolygon } from "../../Models/RasterLayers";
|
||||||
import { Utils } from "../../Utils"
|
import { Utils } from "../../Utils";
|
||||||
import { BBox } from "../../Logic/BBox"
|
import { BBox } from "../../Logic/BBox";
|
||||||
import { ExportableMap, MapProperties } from "../../Models/MapProperties"
|
import { ExportableMap, MapProperties } from "../../Models/MapProperties";
|
||||||
import SvelteUIElement from "../Base/SvelteUIElement"
|
import SvelteUIElement from "../Base/SvelteUIElement";
|
||||||
import MaplibreMap from "./MaplibreMap.svelte"
|
import MaplibreMap from "./MaplibreMap.svelte";
|
||||||
import { RasterLayerProperties } from "../../Models/RasterLayerProperties"
|
import { RasterLayerProperties } from "../../Models/RasterLayerProperties";
|
||||||
import * as htmltoimage from "html-to-image"
|
import * as htmltoimage from "html-to-image";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The 'MapLibreAdaptor' bridges 'MapLibre' with the various properties of the `MapProperties`
|
* The 'MapLibreAdaptor' bridges 'MapLibre' with the various properties of the `MapProperties`
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
|
||||||
import PerLayerFeatureSourceSplitter from "../../Logic/FeatureSource/PerLayerFeatureSourceSplitter"
|
import PerLayerFeatureSourceSplitter from "../../Logic/FeatureSource/PerLayerFeatureSourceSplitter"
|
||||||
import FilteredLayer from "../../Models/FilteredLayer"
|
import FilteredLayer from "../../Models/FilteredLayer"
|
||||||
import SimpleFeatureSource from "../../Logic/FeatureSource/Sources/SimpleFeatureSource"
|
import SimpleFeatureSource from "../../Logic/FeatureSource/Sources/SimpleFeatureSource"
|
||||||
|
import { CLIENT_RENEG_LIMIT } from "tls";
|
||||||
|
|
||||||
class PointRenderingLayer {
|
class PointRenderingLayer {
|
||||||
private readonly _config: PointRenderingConfig
|
private readonly _config: PointRenderingConfig
|
||||||
|
@ -406,13 +407,10 @@ class LineRenderingLayer {
|
||||||
} else {
|
} else {
|
||||||
const tags = this._fetchStore(id)
|
const tags = this._fetchStore(id)
|
||||||
this._listenerInstalledOn.add(id)
|
this._listenerInstalledOn.add(id)
|
||||||
map.setFeatureState(
|
tags.addCallbackAndRunD((properties) => {
|
||||||
{ source: this._layername, id },
|
// Make sure to use 'getSource' here, the layer names are different!
|
||||||
this.calculatePropsFor(feature.properties)
|
if(map.getSource(this._layername) === undefined){
|
||||||
)
|
return true
|
||||||
tags.addCallbackD((properties) => {
|
|
||||||
if (!map.getLayer(this._layername)) {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
map.setFeatureState(
|
map.setFeatureState(
|
||||||
{ source: this._layername, id },
|
{ source: this._layername, id },
|
||||||
|
|
|
@ -92,7 +92,7 @@
|
||||||
<LoginToggle ignoreLoading={true} {state}>
|
<LoginToggle ignoreLoading={true} {state}>
|
||||||
{#if currentState === "start"}
|
{#if currentState === "start"}
|
||||||
<button
|
<button
|
||||||
class="flex"
|
class="flex items-center"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
currentState = "confirm"
|
currentState = "confirm"
|
||||||
}}
|
}}
|
||||||
|
@ -112,7 +112,7 @@
|
||||||
<button
|
<button
|
||||||
slot="save-button"
|
slot="save-button"
|
||||||
on:click={onDelete}
|
on:click={onDelete}
|
||||||
class={twJoin(selectedTags === undefined && "disabled", "primary flex bg-red-600")}
|
class={twJoin(selectedTags === undefined && "disabled", "primary flex bg-red-600 items-center")}
|
||||||
>
|
>
|
||||||
<TrashIcon
|
<TrashIcon
|
||||||
class={twJoin(
|
class={twJoin(
|
||||||
|
@ -122,7 +122,7 @@
|
||||||
/>
|
/>
|
||||||
<Tr t={t.delete} />
|
<Tr t={t.delete} />
|
||||||
</button>
|
</button>
|
||||||
<button slot="cancel" on:click={() => (currentState = "start")}>
|
<button slot="cancel" class="items-center" on:click={() => (currentState = "start")}>
|
||||||
<Tr t={t.cancel} />
|
<Tr t={t.cancel} />
|
||||||
</button>
|
</button>
|
||||||
<XCircleIcon
|
<XCircleIcon
|
||||||
|
|
|
@ -1,114 +1,114 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Store, UIEventSource } from "../Logic/UIEventSource"
|
import { Store, UIEventSource } from "../Logic/UIEventSource";
|
||||||
import { Map as MlMap } from "maplibre-gl"
|
import { Map as MlMap } from "maplibre-gl";
|
||||||
import MaplibreMap from "./Map/MaplibreMap.svelte"
|
import MaplibreMap from "./Map/MaplibreMap.svelte";
|
||||||
import FeatureSwitchState from "../Logic/State/FeatureSwitchState"
|
import FeatureSwitchState from "../Logic/State/FeatureSwitchState";
|
||||||
import MapControlButton from "./Base/MapControlButton.svelte"
|
import MapControlButton from "./Base/MapControlButton.svelte";
|
||||||
import ToSvelte from "./Base/ToSvelte.svelte"
|
import ToSvelte from "./Base/ToSvelte.svelte";
|
||||||
import If from "./Base/If.svelte"
|
import If from "./Base/If.svelte";
|
||||||
import { GeolocationControl } from "./BigComponents/GeolocationControl"
|
import { GeolocationControl } from "./BigComponents/GeolocationControl";
|
||||||
import type { Feature } from "geojson"
|
import type { Feature } from "geojson";
|
||||||
import SelectedElementView from "./BigComponents/SelectedElementView.svelte"
|
import SelectedElementView from "./BigComponents/SelectedElementView.svelte";
|
||||||
import LayerConfig from "../Models/ThemeConfig/LayerConfig"
|
import LayerConfig from "../Models/ThemeConfig/LayerConfig";
|
||||||
import Filterview from "./BigComponents/Filterview.svelte"
|
import Filterview from "./BigComponents/Filterview.svelte";
|
||||||
import ThemeViewState from "../Models/ThemeViewState"
|
import ThemeViewState from "../Models/ThemeViewState";
|
||||||
import type { MapProperties } from "../Models/MapProperties"
|
import type { MapProperties } from "../Models/MapProperties";
|
||||||
import Geosearch from "./BigComponents/Geosearch.svelte"
|
import Geosearch from "./BigComponents/Geosearch.svelte";
|
||||||
import Translations from "./i18n/Translations"
|
import Translations from "./i18n/Translations";
|
||||||
import { CogIcon, EyeIcon, MenuIcon, XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"
|
import { CogIcon, EyeIcon, MenuIcon, XCircleIcon } from "@rgossiaux/svelte-heroicons/solid";
|
||||||
|
|
||||||
import Tr from "./Base/Tr.svelte"
|
import Tr from "./Base/Tr.svelte";
|
||||||
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"
|
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte";
|
||||||
import FloatOver from "./Base/FloatOver.svelte"
|
import FloatOver from "./Base/FloatOver.svelte";
|
||||||
import PrivacyPolicy from "./BigComponents/PrivacyPolicy"
|
import PrivacyPolicy from "./BigComponents/PrivacyPolicy";
|
||||||
import Constants from "../Models/Constants"
|
import Constants from "../Models/Constants";
|
||||||
import TabbedGroup from "./Base/TabbedGroup.svelte"
|
import TabbedGroup from "./Base/TabbedGroup.svelte";
|
||||||
import UserRelatedState from "../Logic/State/UserRelatedState"
|
import UserRelatedState from "../Logic/State/UserRelatedState";
|
||||||
import LoginToggle from "./Base/LoginToggle.svelte"
|
import LoginToggle from "./Base/LoginToggle.svelte";
|
||||||
import LoginButton from "./Base/LoginButton.svelte"
|
import LoginButton from "./Base/LoginButton.svelte";
|
||||||
import CopyrightPanel from "./BigComponents/CopyrightPanel"
|
import CopyrightPanel from "./BigComponents/CopyrightPanel";
|
||||||
import DownloadPanel from "./DownloadFlow/DownloadPanel.svelte"
|
import DownloadPanel from "./DownloadFlow/DownloadPanel.svelte";
|
||||||
import ModalRight from "./Base/ModalRight.svelte"
|
import ModalRight from "./Base/ModalRight.svelte";
|
||||||
import { Utils } from "../Utils"
|
import { Utils } from "../Utils";
|
||||||
import Hotkeys from "./Base/Hotkeys"
|
import Hotkeys from "./Base/Hotkeys";
|
||||||
import { VariableUiElement } from "./Base/VariableUIElement"
|
import { VariableUiElement } from "./Base/VariableUIElement";
|
||||||
import SvelteUIElement from "./Base/SvelteUIElement"
|
import SvelteUIElement from "./Base/SvelteUIElement";
|
||||||
import OverlayToggle from "./BigComponents/OverlayToggle.svelte"
|
import OverlayToggle from "./BigComponents/OverlayToggle.svelte";
|
||||||
import LevelSelector from "./BigComponents/LevelSelector.svelte"
|
import LevelSelector from "./BigComponents/LevelSelector.svelte";
|
||||||
import ExtraLinkButton from "./BigComponents/ExtraLinkButton"
|
import ExtraLinkButton from "./BigComponents/ExtraLinkButton";
|
||||||
import SelectedElementTitle from "./BigComponents/SelectedElementTitle.svelte"
|
import SelectedElementTitle from "./BigComponents/SelectedElementTitle.svelte";
|
||||||
import Svg from "../Svg"
|
import Svg from "../Svg";
|
||||||
import ThemeIntroPanel from "./BigComponents/ThemeIntroPanel.svelte"
|
import ThemeIntroPanel from "./BigComponents/ThemeIntroPanel.svelte";
|
||||||
import type { RasterLayerPolygon } from "../Models/RasterLayers"
|
import type { RasterLayerPolygon } from "../Models/RasterLayers";
|
||||||
import { AvailableRasterLayers } from "../Models/RasterLayers"
|
import { AvailableRasterLayers } from "../Models/RasterLayers";
|
||||||
import RasterLayerOverview from "./Map/RasterLayerOverview.svelte"
|
import RasterLayerOverview from "./Map/RasterLayerOverview.svelte";
|
||||||
import IfHidden from "./Base/IfHidden.svelte"
|
import IfHidden from "./Base/IfHidden.svelte";
|
||||||
import { onDestroy } from "svelte"
|
import { onDestroy } from "svelte";
|
||||||
import { OpenJosm } from "./BigComponents/OpenJosm"
|
import { OpenJosm } from "./BigComponents/OpenJosm";
|
||||||
import MapillaryLink from "./BigComponents/MapillaryLink.svelte"
|
import MapillaryLink from "./BigComponents/MapillaryLink.svelte";
|
||||||
import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte"
|
import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte";
|
||||||
import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte"
|
import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte";
|
||||||
import StateIndicator from "./BigComponents/StateIndicator.svelte"
|
import StateIndicator from "./BigComponents/StateIndicator.svelte";
|
||||||
import LanguagePicker from "./LanguagePicker"
|
import LanguagePicker from "./LanguagePicker";
|
||||||
import Locale from "./i18n/Locale"
|
import Locale from "./i18n/Locale";
|
||||||
import ShareScreen from "./BigComponents/ShareScreen.svelte"
|
import ShareScreen from "./BigComponents/ShareScreen.svelte";
|
||||||
|
|
||||||
export let state: ThemeViewState
|
export let state: ThemeViewState;
|
||||||
let layout = state.layout
|
let layout = state.layout;
|
||||||
|
|
||||||
let maplibremap: UIEventSource<MlMap> = state.map
|
let maplibremap: UIEventSource<MlMap> = state.map;
|
||||||
let selectedElement: UIEventSource<Feature> = state.selectedElement
|
let selectedElement: UIEventSource<Feature> = state.selectedElement;
|
||||||
let selectedLayer: UIEventSource<LayerConfig> = state.selectedLayer
|
let selectedLayer: UIEventSource<LayerConfig> = state.selectedLayer;
|
||||||
|
|
||||||
const selectedElementView = selectedElement.map(
|
const selectedElementView = selectedElement.map(
|
||||||
(selectedElement) => {
|
(selectedElement) => {
|
||||||
// Svelte doesn't properly reload some of the legacy UI-elements
|
// Svelte doesn't properly reload some of the legacy UI-elements
|
||||||
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
|
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
|
||||||
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
|
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
|
||||||
const layer = selectedLayer.data
|
const layer = selectedLayer.data;
|
||||||
if (selectedElement === undefined || layer === undefined) {
|
if (selectedElement === undefined || layer === undefined) {
|
||||||
return undefined
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(layer.tagRenderings?.length > 0) || layer.title === undefined) {
|
if (!(layer.tagRenderings?.length > 0) || layer.title === undefined) {
|
||||||
return undefined
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tags = state.featureProperties.getStore(selectedElement.properties.id)
|
const tags = state.featureProperties.getStore(selectedElement.properties.id);
|
||||||
return new SvelteUIElement(SelectedElementView, { state, layer, selectedElement, tags })
|
return new SvelteUIElement(SelectedElementView, { state, layer, selectedElement, tags });
|
||||||
},
|
},
|
||||||
[selectedLayer]
|
[selectedLayer]
|
||||||
)
|
);
|
||||||
|
|
||||||
const selectedElementTitle = selectedElement.map(
|
const selectedElementTitle = selectedElement.map(
|
||||||
(selectedElement) => {
|
(selectedElement) => {
|
||||||
// Svelte doesn't properly reload some of the legacy UI-elements
|
// Svelte doesn't properly reload some of the legacy UI-elements
|
||||||
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
|
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
|
||||||
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
|
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
|
||||||
const layer = selectedLayer.data
|
const layer = selectedLayer.data;
|
||||||
if (selectedElement === undefined || layer === undefined) {
|
if (selectedElement === undefined || layer === undefined) {
|
||||||
return undefined
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tags = state.featureProperties.getStore(selectedElement.properties.id)
|
const tags = state.featureProperties.getStore(selectedElement.properties.id);
|
||||||
return new SvelteUIElement(SelectedElementTitle, { state, layer, selectedElement, tags })
|
return new SvelteUIElement(SelectedElementTitle, { state, layer, selectedElement, tags });
|
||||||
},
|
},
|
||||||
[selectedLayer]
|
[selectedLayer]
|
||||||
)
|
);
|
||||||
|
|
||||||
let mapproperties: MapProperties = state.mapProperties
|
let mapproperties: MapProperties = state.mapProperties;
|
||||||
let featureSwitches: FeatureSwitchState = state.featureSwitches
|
let featureSwitches: FeatureSwitchState = state.featureSwitches;
|
||||||
let availableLayers = state.availableLayers
|
let availableLayers = state.availableLayers;
|
||||||
let userdetails = state.osmConnection.userDetails
|
let userdetails = state.osmConnection.userDetails;
|
||||||
let currentViewLayer = layout.layers.find((l) => l.id === "current_view")
|
let currentViewLayer = layout.layers.find((l) => l.id === "current_view");
|
||||||
let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer
|
let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer;
|
||||||
let rasterLayerName =
|
let rasterLayerName =
|
||||||
rasterLayer.data?.properties?.name ?? AvailableRasterLayers.maptilerDefaultLayer.properties.name
|
rasterLayer.data?.properties?.name ?? AvailableRasterLayers.maptilerDefaultLayer.properties.name;
|
||||||
onDestroy(
|
onDestroy(
|
||||||
rasterLayer.addCallbackAndRunD((l) => {
|
rasterLayer.addCallbackAndRunD((l) => {
|
||||||
rasterLayerName = l.properties.name
|
rasterLayerName = l.properties.name;
|
||||||
})
|
})
|
||||||
)
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="absolute top-0 left-0 h-screen w-screen overflow-hidden">
|
<div class="absolute top-0 left-0 h-screen w-screen overflow-hidden">
|
||||||
|
@ -168,9 +168,29 @@
|
||||||
<div class="pointer-events-none absolute bottom-0 left-0 mb-4 w-screen">
|
<div class="pointer-events-none absolute bottom-0 left-0 mb-4 w-screen">
|
||||||
<!-- bottom controls -->
|
<!-- bottom controls -->
|
||||||
<div class="flex w-full items-end justify-between px-4">
|
<div class="flex w-full items-end justify-between px-4">
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<If condition={featureSwitches.featureSwitchEnableLogin}>
|
||||||
|
{#if state.lastClickObject.hasPresets || state.lastClickObject.hasNoteLayer}
|
||||||
|
<button class="w-fit pointer-events-auto" on:click={() => {state.openNewDialog()}}>
|
||||||
|
{#if state.lastClickObject.hasPresets}
|
||||||
|
<Tr t={Translations.t.general.add.title} />
|
||||||
|
{:else}
|
||||||
|
<Tr t={Translations.t.notes.addAComment} />
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</If>
|
||||||
|
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<!-- bottom left elements -->
|
<!-- bottom left elements -->
|
||||||
|
<If condition={state.featureSwitches.featureSwitchFilter}>
|
||||||
|
<MapControlButton on:click={() => state.guistate.openFilterView()}>
|
||||||
|
<ToSvelte construct={Svg.filter_svg().SetClass("h-6 w-6")} />
|
||||||
|
</MapControlButton>
|
||||||
|
</If>
|
||||||
|
<If condition={state.featureSwitches.featureSwitchBackgroundSelection}>
|
||||||
<OpenBackgroundSelectorButton hideTooltip={true} {state} />
|
<OpenBackgroundSelectorButton hideTooltip={true} {state} />
|
||||||
|
</If>
|
||||||
<a
|
<a
|
||||||
class="bg-black-transparent pointer-events-auto h-fit max-h-12 cursor-pointer self-end overflow-hidden rounded-2xl pl-1 pr-2 text-white opacity-50 hover:opacity-100"
|
class="bg-black-transparent pointer-events-auto h-fit max-h-12 cursor-pointer self-end overflow-hidden rounded-2xl pl-1 pr-2 text-white opacity-50 hover:opacity-100"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
|
@ -181,6 +201,7 @@
|
||||||
© OpenStreetMap, <span class="w-24">{rasterLayerName}</span>
|
© OpenStreetMap, <span class="w-24">{rasterLayerName}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col items-end">
|
<div class="flex flex-col items-end">
|
||||||
<!-- bottom right elements -->
|
<!-- bottom right elements -->
|
||||||
|
@ -255,9 +276,9 @@
|
||||||
|
|
||||||
<If condition={state.guistate.themeIsOpened}>
|
<If condition={state.guistate.themeIsOpened}>
|
||||||
<!-- Theme menu -->
|
<!-- Theme menu -->
|
||||||
<FloatOver>
|
<FloatOver on:close={() => state.guistate.themeIsOpened.setData(false)}>
|
||||||
<span slot="close-button"><!-- Disable the close button --></span>
|
<span slot="close-button"><!-- Disable the close button --></span>
|
||||||
<TabbedGroup tab={state.guistate.themeViewTabIndex}>
|
<TabbedGroup condition1={state.featureSwitches.featureSwitchFilter} tab={state.guistate.themeViewTabIndex}>
|
||||||
<div slot="post-tablist">
|
<div slot="post-tablist">
|
||||||
<XCircleIcon
|
<XCircleIcon
|
||||||
class="mr-2 h-8 w-8"
|
class="mr-2 h-8 w-8"
|
||||||
|
@ -275,10 +296,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex" slot="title1">
|
<div class="flex" slot="title1">
|
||||||
<If condition={state.featureSwitches.featureSwitchFilter}>
|
|
||||||
<ToSvelte construct={Svg.filter_svg().SetClass("w-4 h-4")} />
|
<ToSvelte construct={Svg.filter_svg().SetClass("w-4 h-4")} />
|
||||||
<Tr t={Translations.t.general.menu.filter} />
|
<Tr t={Translations.t.general.menu.filter} />
|
||||||
</If>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="m-2 flex flex-col" slot="content1">
|
<div class="m-2 flex flex-col" slot="content1">
|
||||||
|
@ -298,6 +317,7 @@
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex" slot="title2">
|
<div class="flex" slot="title2">
|
||||||
<If condition={state.featureSwitches.featureSwitchEnableExport}>
|
<If condition={state.featureSwitches.featureSwitchEnableExport}>
|
||||||
<ToSvelte construct={Svg.download_svg().SetClass("w-4 h-4")} />
|
<ToSvelte construct={Svg.download_svg().SetClass("w-4 h-4")} />
|
||||||
|
@ -314,7 +334,7 @@
|
||||||
|
|
||||||
<ToSvelte construct={() => new CopyrightPanel(state)} slot="content3" />
|
<ToSvelte construct={() => new CopyrightPanel(state)} slot="content3" />
|
||||||
|
|
||||||
<div slot="title4" class="flex">
|
<div class="flex" slot="title4">
|
||||||
<ToSvelte construct={Svg.share_svg().SetClass("w-4 h-4")} />
|
<ToSvelte construct={Svg.share_svg().SetClass("w-4 h-4")} />
|
||||||
<Tr t={Translations.t.general.sharescreen.title} />
|
<Tr t={Translations.t.general.sharescreen.title} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -327,7 +347,7 @@
|
||||||
|
|
||||||
<IfHidden condition={state.guistate.backgroundLayerSelectionIsOpened}>
|
<IfHidden condition={state.guistate.backgroundLayerSelectionIsOpened}>
|
||||||
<!-- background layer selector -->
|
<!-- background layer selector -->
|
||||||
<FloatOver on:close={() => state.guistate.backgroundLayerSelectionIsOpened.setData(false)}>
|
<FloatOver on:close={() => {state.guistate.backgroundLayerSelectionIsOpened.setData(false)}}>
|
||||||
<div class="h-full p-2">
|
<div class="h-full p-2">
|
||||||
<RasterLayerOverview
|
<RasterLayerOverview
|
||||||
{availableLayers}
|
{availableLayers}
|
||||||
|
@ -342,9 +362,10 @@
|
||||||
|
|
||||||
<If condition={state.guistate.menuIsOpened}>
|
<If condition={state.guistate.menuIsOpened}>
|
||||||
<!-- Menu page -->
|
<!-- Menu page -->
|
||||||
<FloatOver>
|
<FloatOver on:close={() => state.guistate.menuIsOpened.setData(false) }>
|
||||||
<span slot="close-button"><!-- Hide the default close button --></span>
|
<span slot="close-button"><!-- Hide the default close button --></span>
|
||||||
<TabbedGroup tab={state.guistate.menuViewTabIndex}>
|
<TabbedGroup condition1={featureSwitches.featureSwitchEnableLogin} condition2={state.featureSwitches. featureSwitchCommunityIndex}
|
||||||
|
tab={state.guistate.menuViewTabIndex}>
|
||||||
<div slot="post-tablist">
|
<div slot="post-tablist">
|
||||||
<XCircleIcon
|
<XCircleIcon
|
||||||
class="mr-2 h-8 w-8"
|
class="mr-2 h-8 w-8"
|
||||||
|
@ -419,7 +440,6 @@
|
||||||
<div class="m-2" slot="content2">
|
<div class="m-2" slot="content2">
|
||||||
<CommunityIndexView location={state.mapProperties.location} />
|
<CommunityIndexView location={state.mapProperties.location} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex" slot="title3">
|
<div class="flex" slot="title3">
|
||||||
<EyeIcon class="w-6" />
|
<EyeIcon class="w-6" />
|
||||||
<Tr t={Translations.t.privacy.title} />
|
<Tr t={Translations.t.privacy.title} />
|
||||||
|
@ -430,12 +450,15 @@
|
||||||
|
|
||||||
<Tr slot="title4" t={Translations.t.advanced.title} />
|
<Tr slot="title4" t={Translations.t.advanced.title} />
|
||||||
<div class="m-2 flex flex-col" slot="content4">
|
<div class="m-2 flex flex-col" slot="content4">
|
||||||
|
<If condition={featureSwitches.featureSwitchEnableLogin}>
|
||||||
<OpenIdEditor mapProperties={state.mapProperties} />
|
<OpenIdEditor mapProperties={state.mapProperties} />
|
||||||
<ToSvelte
|
<ToSvelte
|
||||||
construct={() =>
|
construct={() =>
|
||||||
new OpenJosm(state.osmConnection, state.mapProperties.bounds).SetClass("w-full")}
|
new OpenJosm(state.osmConnection, state.mapProperties.bounds).SetClass("w-full")}
|
||||||
/>
|
/>
|
||||||
<MapillaryLink mapProperties={state.mapProperties} />
|
<MapillaryLink mapProperties={state.mapProperties} />
|
||||||
|
</If>
|
||||||
|
|
||||||
<ToSvelte construct={Hotkeys.generateDocumentationDynamic} />
|
<ToSvelte construct={Hotkeys.generateDocumentationDynamic} />
|
||||||
</div>
|
</div>
|
||||||
</TabbedGroup>
|
</TabbedGroup>
|
||||||
|
|
Loading…
Add table
Reference in a new issue