diff --git a/Docs/Development_deployment.md b/Docs/Development_deployment.md index f5091fcfb4..8beafc0a09 100644 --- a/Docs/Development_deployment.md +++ b/Docs/Development_deployment.md @@ -106,6 +106,12 @@ If you want to deploy your fork: 2. Copy the entire `dist` folder to where you host your website. Visiting `index.html` gives you the landing page, visiting `yourwebsite/` should bring you to the appropriate theme. +### Getting your own API-keys + +Some services are bound to `https://mapcomplete.org`. In `package.json/config`, search for "#fork" and read the instructions. + + + Weird errors ------------ diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 50126e110a..82133b987c 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -44,7 +44,7 @@ "title": "een spandoek" }, "12": { - "title": "een totem" + "title": "eem reclamezuil" }, "13": { "description": "Gebruikt voor reclameborden, neonborden, logo's & toegangsborden voor instellingen", @@ -212,6 +212,9 @@ }, "7": { "then": "Dit is een teken" + }, + "9": { + "then": "Dit is een reclamezuil" } }, "render": "Dit is een {advertising}" @@ -238,7 +241,7 @@ "then": "Spandoek" }, "9": { - "then": "Aanplakzuil" + "then": "Reclamezuil" } } } diff --git a/langs/nl.json b/langs/nl.json index d749aaeafb..a4cd0b56e5 100644 --- a/langs/nl.json +++ b/langs/nl.json @@ -306,7 +306,12 @@ "logout": "Afmelden", "menu": { "aboutMapComplete": "Over MapComplete", - "filter": "Filter data" + "filter": "Filter data", + "aboutCurrentThemeTitle": "Over deze kaart", + "moreUtilsTitle": "Ontdek meer", + "openHereDifferentApp": "Open de huidige locatie in andere toepassingen", + "showIntroduction": "Toon introductie", + "title": "Menu" }, "morescreen": { "createYourOwnTheme": "Maak je eigen MapComplete-kaart", @@ -402,7 +407,9 @@ "recents": "Recent bekeken plaatsen", "search": "Zoek naar een locatie, filter of kaart", "searchShort": "Zoek…", - "searching": "Aan het zoeken…" + "searching": "Aan het zoeken…", + "nMoreFilters": "{n} meer", + "nothingFor": "Geen resultaten gevonden voor {term}" }, "share": "Deel deze locatie", "sharescreen": { @@ -412,7 +419,15 @@ "fsWelcomeMessage": "Toon het welkomstbericht en de bijhorende tabbladen", "intro": "Kopieer onderstaande link om deze kaart naar vrienden en familie door te sturen:", "thanksForSharing": "Bedankt om te delen!", - "title": "Deel deze kaart" + "title": "Deel deze kaart", + "fsBackground": "Wisselende achtergronden inschakelen", + "fsFilter": "De mogelijkheid inschakelen om te wisselen tussen lagen en filters", + "fsGeolocation": "Geolocatie inschakelen", + "openInOtherApplications": "De huidige locatie openen met een andere kaarttoepassing", + "openLayers": "Open het menu met lagen en filters", + "options": "Opties voor delen", + "stateIsIncluded": "De huidige status van de lagen en filters is opgenomen in de gedeelde link en iframe.", + "documentation": "Voor meer informatie over beschikbare URL-parameters, raadpleeg de documentatie" }, "skip": "Sla deze vraag over", "testing": "Testmode - wijzigingen worden niet opgeslaan", @@ -479,7 +494,9 @@ "unlocked": "Bewegen ontgrendeld", "viewportCenterCloseToGps": "De kaart is gecentreerd op je huidige GPS-locatie.", "viewportCenterDetails": "Het kaartbeeldcentrum is {distance} {bearing} vanaf je huidige locatie.", - "west": "Naar het westen" + "west": "Naar het westen", + "fromGps": "{distance} {direction} van uw locatie", + "fromMapCenter": "{distance} {direction} van het midden van de kaart" }, "waitingForLocation": "Je locatie wordt gezocht…", "weekdays": { @@ -522,7 +539,17 @@ "searchToShort": "Je zoekopdracht is te kort, vul een langere tekst in", "searchWikidata": "Zoek op Wikidata", "wikipediaboxTitle": "Wikipedia" - } + }, + "retry": "Opnieuw proberen", + "searchAnswer": "Zoek een optie…", + "seeIndex": "Zie het overzich van alle thematische kaarten", + "uploadError": "Fout tijdens het uploaden van wijzigingen: {error}", + "mappingsAreHidden": "Sommige opties zijn verborgen. Gebruik zoeken om meer opties te tonen.", + "poweredByMapComplete": "Powered by MapComplete - crowdsourced, thematische kaarten met OpenStreetMap", + "uploadPending": "{count} wijzigingen in behandeling", + "uploadPendingSingle": "Eén wijziging in behandeling", + "uploadingChanges": "Wijzigingen aan het uploaden…", + "waitingForGeopermission": "Aan het wachten op toestemming om geolocatie te gebruiken…" }, "hotkeyDocumentation": { "action": "Actie", @@ -536,7 +563,15 @@ "selectMapnik": "Gebruik OpenStreetMap-carto als achtergrondlaag", "selectOsmbasedmap": "Gebruik een OpenStreetMap-gebaseerde achtergrondkaart (of schakel de achtergrond-rasterlaag uit)", "selectSearch": "Selecteer de zoekbalk om locaties te zoeken", - "title": "Sneltoetsen" + "title": "Sneltoetsen", + "queryCurrentLocation": "Toon het adres dichtst bij het midden van de kaart", + "selectFavourites": "Open de pagina met favorieten", + "selectItem2": "Selecteer het POI het op één na dichtst bij het midden van de kaart (crosshair). Alleen wanneer toetsenbordnavigatie wordt gebruikt", + "shakePhone": "Schudden met je telefoon", + "translationMode": "Vertaalmodus in- of uitschakelen", + "openFilterPanel": "Opent het POI-lagen- en filterpaneel", + "selectItem": "Selecteer het POI het dichtst bij het midden van de kaart (crosshair). Alleen wanneer toetsenbordnavigatie wordt gebruikt", + "selectItem3": "Selecteer het POI het op twee na dichtst bij het midden van de kaart (crosshair). Alleen wanneer toetsenbordnavigatie wordt gebruikt" }, "image": { "addPicture": "Voeg foto toe", @@ -547,7 +582,10 @@ "nearby": { "link": "Deze afbeelding toont het object", "seeNearby": "Bekijk foto's in de buurt", - "title": "Straatafbeeldingen uit de buurt" + "title": "Straatafbeeldingen uit de buurt", + "failed": "Afbeeldingen ophalen van {service} mislukt", + "close": "Paneel met nabije afbeeldingen samenvouwen", + "noNearbyImages": "Geen afbeeldingen in de buurt gevonden" }, "pleaseLogin": "Gelieve je aan te melden om een foto toe te voegen", "respectPrivacy": "Voeg geen Google Maps, Google Streetview of foto's met auteursrechten toe.", @@ -556,7 +594,34 @@ "uploadFailed": "Afbeelding uploaden mislukt. Heb je internet? Gebruik je Brave of UMatrix? Dan moet je derde partijen toelaten.", "uploadMultipleDone": "{count} afbeeldingen zijn toegevoegd. Bedankt voor je bijdrage!", "uploadingMultiple": "Bezig met {count} foto's te uploaden…", - "uploadingPicture": "Bezig met een foto te uploaden…" + "uploadingPicture": "Bezig met een foto te uploaden…", + "panoramax": { + "deletionRequested": "Het rapport is verzonden. Een moderator zal er binnenkort naar kijken", + "freeform": "Is er andere relevante informatie?", + "otherFreeform": "Waarom moet deze afbeelding worden verwijderd:", + "report": { + "blur_missing": "Op deze foto is een gezicht of nummerplaat niet geblurd", + "blur_excess": "Er is te geblurd, waardoor de foto onbruikbaar is", + "copyright": "De foto bevat auteursrechtelijk beschermde inhoud", + "mislocated": "De foto is van een andere locatie", + "other": "Een andere reden, specificeer", + "picture_low_quality": "De foto is van lage kwaliteit", + "privacy": "De foto toont een privé-eigendom", + "inappropriate": "Deze foto is ongepast (bevat naakt, roept op tot haat of is geen straatbeeld)" + }, + "requestDeletion": "Verwijdering van foto aanvragen", + "title": "Waarom zou dit beeld permanent verwijderd moeten worden?", + "placeholder": "Leg uit waarom de foto verwijderd moet worden" + }, + "processing": "De server is je beeld aan het verwerken", + "unlink": { + "button": "Link naar foto verwijderen", + "title": "Link naar deze afbeelding verwijderen?", + "explanation": "Door de link naar deze afbeelding te verwijderen, wordt deze niet meer getoond bij dit object. De afbeelding zal nog steeds verschijnen bij nabije afbeeldingen en mogelijk ook andere objecten." + }, + "upload": { + "failReasons": "Mogelijk heb je geen verbinding meer met het internet" + } }, "importInspector": { "title": "Inspecteer en beheer importeer-notas" @@ -781,4 +846,4 @@ "description": "Een Wikidata-code" } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 7cbcff1a0f..64efcc3fdb 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ ], "oauth_credentials": { "#": "This client-id is registered by 'MapComplete' on OpenStreetMap.org", + "#fork": "For a fork, you will need to register your own key at https://www.openstreetmap.org/oauth2/applications ; redirect url is `https:///land.html`, permissions: `read_user_preferences`, `modify preferences`, `modify the map`. For GPS-traces: both, `modify-notes` if you want the notes-theme to work.", "oauth_client_id": "K93H1d8ve7p-tVLE1ZwsQ4lAFLQk8INx5vfTLMu5DWk", "oauth_secret": "NBWGhWDrD3QDB35xtVuxv4aExnmIt4FA_WgeLtwxasg", "url": "https://www.openstreetmap.org", @@ -37,10 +38,12 @@ "error_server": "https://report.mapcomplete.org/report", "api_keys": { "#": "Various API-keys for various services. Feel free to reuse those in another MapComplete-hosted version", + "#fork": "Not bound to a domain; can be reused", "imgur": "7070e7167f0a25a", "mapillary_v4": "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" }, "panoramax": { + "#fork": "Not bound to a domain; can be reused", "url": "https://panoramax.mapcomplete.org", "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnZW92aXNpbyIsInN1YiI6IjU5ZjgzOGI0LTM4ZjAtNDdjYi04OWYyLTM3NDQ3MWMxNTUxOCJ9.0rBioZS_48NTjnkIyN9497c3fQdTqtGgH1HDqlz1bWs", "sequence": "6e702976-580b-419c-8fb3-cf7bd364e6f8", @@ -61,6 +64,7 @@ "photonEndpoint": "https://photon.komoot.io/", "jsonld-proxy": "https://lod.mapcomplete.org/extractgraph?url={url}", "protomaps": { + "#fork": "Bound to https://mapcomplete.org; get your own at https://protomaps.com/", "api-key": "2af8b969a9e8b692", "endpoint": "https://api.protomaps.com/tiles/", "styles": [ @@ -73,7 +77,7 @@ } }, "scripts": { - "init": "npm ci && npm run prep:layeroverview && npm run generate && npm run download:editor-layer-index && npm run generate:layouts && npm run clean && npm run weblate:add-upstream", + "init": "npm ci && npm run prep:layeroverview && npm run generate && npm run download:editor-layer-index && npm run generate:layouts && npm run clean", "start": "npm run generate:layeroverview && npm run strt", "strt": "vite --host | sed 's/localhost:/127.0.0.1:/g'", "build": "./scripts/build.sh", @@ -114,6 +118,8 @@ "download:editor-layer-index": "vite-node scripts/downloadEli.ts", "download:stats": "vite-node scripts/GenerateSeries.ts", "download:images": "vite-node scripts/generateImageAnalysis.ts -- ~/data/imgur-image-backup/", + "weblate:add-upstream": "git remote add weblate https://translate.mapcomplete.org/git/mapcomplete/core/ ; git remote update weblate", + "weblate:fix": "npm run weblate:add-upstream && git merge weblate/master && git rebase origin/master && git push origin master", "lint": "npm run lint:prettier && npm run lint:eslint && npm run lint:themes", "lint:eslint": "eslint ./src", "lint:prettier": "prettier --check '**/*.ts' '**/*.svelte'", diff --git a/src/Logic/FeatureSource/Sources/FilteringFeatureSource.ts b/src/Logic/FeatureSource/Sources/FilteringFeatureSource.ts index e38c811d49..843513a38c 100644 --- a/src/Logic/FeatureSource/Sources/FilteringFeatureSource.ts +++ b/src/Logic/FeatureSource/Sources/FilteringFeatureSource.ts @@ -13,60 +13,74 @@ export default class FilteringFeatureSource implements FeatureSource { private readonly _is_dirty = new UIEventSource(false) private readonly _layer: FilteredLayer private previousFeatureSet: Set = undefined + private readonly _zoomlevel: Store + private readonly _selectedElement: Store constructor( layer: FilteredLayer, upstream: FeatureSource, fetchStore?: (id: string) => Store>, globalFilters?: Store, - metataggingUpdated?: Store + metataggingUpdated?: Store, + zoomlevel?: Store, + selectedElement?: Store ) { this.upstream = upstream this._fetchStore = fetchStore this._layer = layer this._globalFilters = globalFilters + this._zoomlevel = zoomlevel + this._selectedElement = selectedElement - const self = this upstream.features.addCallback(() => { - self.update() + this.update() }) layer.isDisplayed.addCallback(() => { - self.update() + this.update() }) layer.appliedFilters.forEach((value) => value.addCallback((_) => { - self.update() + this.update() }) ) this._is_dirty.stabilized(1000).addCallbackAndRunD((dirty) => { if (dirty) { - self.update() + this.update() } }) - metataggingUpdated?.addCallback((_) => { - self._is_dirty.setData(true) + metataggingUpdated?.addCallback(() => { + this._is_dirty.setData(true) }) - globalFilters?.addCallback((_) => { - self.update() + globalFilters?.addCallback(() => { + this.update() }) + selectedElement?.addCallback(() => this.update()) + + zoomlevel?.mapD(z => Math.floor(z)).addCallback(() => this.update()) + this.update() } private update() { - const self = this const layer = this._layer const features: Feature[] = this.upstream.features.data ?? [] const includedFeatureIds = new Set() - const globalFilters = self._globalFilters?.data?.map((f) => f) + const globalFilters = this._globalFilters?.data?.map((f) => f) + const zoomlevel = this._zoomlevel?.data + const selectedElement = this._selectedElement?.data?.properties?.id const newFeatures = (features ?? []).filter((f) => { - self.registerCallback(f.properties.id) + this.registerCallback(f.properties.id) - if (!layer.isShown(f.properties, globalFilters)) { + if(selectedElement === f.properties.id){ + return true + } + + if (!layer.isShown(f.properties, globalFilters, zoomlevel)) { return false } diff --git a/src/Models/FilteredLayer.ts b/src/Models/FilteredLayer.ts index a3d5364ec2..724e441213 100644 --- a/src/Models/FilteredLayer.ts +++ b/src/Models/FilteredLayer.ts @@ -210,7 +210,7 @@ export default class FilteredLayer { * - the specified 'global filters' * - the 'isShown'-filter set by the layer */ - public isShown(properties: Record, globalFilters?: GlobalFilter[]): boolean { + public isShown(properties: Record, globalFilters?: GlobalFilter[], zoomlevel?: number): boolean { if (properties._deleted === "yes") { return false } @@ -219,9 +219,10 @@ export default class FilteredLayer { if (neededTags !== undefined) { const doesMatch = neededTags.matchesProperties(properties) if (globalFilter.forceShowOnMatch) { - return doesMatch || this.isDisplayed.data - } - if (!doesMatch) { + if(doesMatch){ + return true + } + } else if (!doesMatch) { return false } } @@ -240,6 +241,10 @@ export default class FilteredLayer { } } + if(zoomlevel !== undefined && (this.layerDef.minzoom > zoomlevel || this.layerDef.minzoomVisible < zoomlevel)){ + return false + } + return true } diff --git a/src/Models/ThemeConfig/LayerConfig.ts b/src/Models/ThemeConfig/LayerConfig.ts index 2f7824cb20..60652478f2 100644 --- a/src/Models/ThemeConfig/LayerConfig.ts +++ b/src/Models/ThemeConfig/LayerConfig.ts @@ -159,7 +159,7 @@ export default class LayerConfig extends WithContextLoader { if (json["minZoom"] !== undefined) { throw "At " + context + ": minzoom is written all lowercase" } - this.minzoomVisible = json.minzoomVisible ?? this.minzoom + this.minzoomVisible = json.minzoomVisible ?? 100 this.shownByDefault = json.shownByDefault ?? true this.doCount = json.isCounted ?? this.shownByDefault ?? true this.forceLoad = json.forceLoad ?? false diff --git a/src/Models/ThemeViewState.ts b/src/Models/ThemeViewState.ts index d663b4fb6e..4da8a8e425 100644 --- a/src/Models/ThemeViewState.ts +++ b/src/Models/ThemeViewState.ts @@ -473,7 +473,10 @@ export default class ThemeViewState implements SpecialVisualizationState { fs.layer, fs, (id) => this.featureProperties.getStore(id), - this.layerState.globalFilters + this.layerState.globalFilters, + undefined, + this.mapProperties.zoom, + this.selectedElement ) filteringFeatureSource.set(layerName, filtered)