Merge develop

This commit is contained in:
pietervdvn 2021-10-11 23:33:09 +02:00
commit 8fdb7a6d7f
22 changed files with 536 additions and 141 deletions

View file

@ -454,7 +454,7 @@ export class InitUiElements {
return false;
}
if (z >= clustering.maxZoom) {
if (z > clustering.maxZoom) {
return true
}
@ -489,7 +489,7 @@ export class InitUiElements {
}
return true
}, [State.state.currentBounds]
}, [State.state.currentBounds, source.layer.isDisplayed]
)
new ShowDataLayer(

View file

@ -38,8 +38,7 @@ export default class OverpassFeatureSource implements FeatureSource {
readonly currentBounds: UIEventSource<BBox>
}
private readonly _isActive: UIEventSource<boolean>;
private readonly onBboxLoaded: (bbox: BBox, date: Date, layers: LayerConfig[]) => void;
private readonly onBboxLoaded: (bbox: BBox, date: Date, layers: LayerConfig[], zoomlevel: number) => void;
constructor(
state: {
readonly locationControl: UIEventSource<Loc>,
@ -49,10 +48,11 @@ export default class OverpassFeatureSource implements FeatureSource {
readonly overpassMaxZoom: UIEventSource<number>,
readonly currentBounds: UIEventSource<BBox>
},
options?: {
options: {
padToTiles: UIEventSource<number>,
isActive?: UIEventSource<boolean>,
relationTracker: RelationsTracker,
onBboxLoaded?: (bbox: BBox, date: Date, layers: LayerConfig[]) => void
onBboxLoaded?: (bbox: BBox, date: Date, layers: LayerConfig[], zoomlevel: number) => void
}) {
this.state = state
@ -61,7 +61,7 @@ export default class OverpassFeatureSource implements FeatureSource {
this.relationsTracker = options.relationTracker
const self = this;
state.currentBounds.addCallback(_ => {
self.update()
self.update(options.padToTiles.data)
})
}
@ -84,21 +84,21 @@ export default class OverpassFeatureSource implements FeatureSource {
return new Overpass(new Or(filters), extraScripts, interpreterUrl, this.state.overpassTimeout, this.relationsTracker);
}
private update() {
private update(paddedZoomLevel: number) {
if (!this._isActive.data) {
return;
}
const self = this;
this.updateAsync().then(bboxDate => {
this.updateAsync(paddedZoomLevel).then(bboxDate => {
if(bboxDate === undefined || self.onBboxLoaded === undefined){
return;
}
const [bbox, date, layers] = bboxDate
self.onBboxLoaded(bbox, date, layers)
self.onBboxLoaded(bbox, date, layers, paddedZoomLevel)
})
}
private async updateAsync(): Promise<[BBox, Date, LayerConfig[]]> {
private async updateAsync(padToZoomLevel: number): Promise<[BBox, Date, LayerConfig[]]> {
if (this.runningQuery.data) {
console.log("Still running a query, not updating");
return undefined;
@ -109,7 +109,7 @@ export default class OverpassFeatureSource implements FeatureSource {
return undefined;
}
const bounds = this.state.currentBounds.data?.pad(this.state.layoutToUse.widenFactor)?.expandToTileBounds(14);
const bounds = this.state.currentBounds.data?.pad(this.state.layoutToUse.widenFactor)?.expandToTileBounds(padToZoomLevel);
if (bounds === undefined) {
return undefined;

View file

@ -116,16 +116,15 @@ export class BBox {
return this.minLat
}
pad(factor: number): BBox {
const latDiff = this.maxLat - this.minLat
const lat = (this.maxLat + this.minLat) / 2
const lonDiff = this.maxLon - this.minLon
const lon = (this.maxLon + this.minLon) / 2
pad(factor: number, maxIncrease = 2): BBox {
const latDiff = Math.min(maxIncrease / 2, Math.abs(this.maxLat - this.minLat) * factor)
const lonDiff =Math.min(maxIncrease / 2, Math.abs(this.maxLon - this.minLon) * factor)
return new BBox([[
lon - lonDiff * factor,
lat - latDiff * factor
], [lon + lonDiff * factor,
lat + latDiff * factor]])
this.minLon - lonDiff,
this.minLat - latDiff
], [this.maxLon + lonDiff,
this.maxLat + latDiff]])
}
toLeaflet() {

View file

@ -4,11 +4,10 @@
* Technically, more an Actor then a featuresource, but it fits more neatly this ay
*/
import {FeatureSourceForLayer} from "../FeatureSource";
import SimpleMetaTagger from "../../SimpleMetaTagger";
export default class SaveTileToLocalStorageActor {
public static readonly storageKey: string = "cached-features";
public static readonly formatVersion: string = "1"
public static readonly formatVersion: string = "2"
constructor(source: FeatureSourceForLayer, tileIndex: number) {
@ -37,6 +36,5 @@ export default class SaveTileToLocalStorageActor {
}catch(e){
console.error("Could not mark tile ", key, "as visited")
}
}
}

View file

@ -58,7 +58,7 @@ export default class FeaturePipeline {
private readonly freshnesses = new Map<string, TileFreshnessCalculator>();
private readonly oldestAllowedDate: Date = new Date(new Date().getTime() - 60 * 60 * 24 * 30 * 1000);
private readonly osmSourceZoomLevel = 14
private readonly osmSourceZoomLevel = 15
constructor(
handleFeatureSource: (source: FeatureSourceForLayer & Tiled) => void,
@ -147,7 +147,7 @@ export default class FeaturePipeline {
// We split them up into tiles anyway as it is an OSM source
TiledFeatureSource.createHierarchy(src, {
layer: src.layer,
minZoomLevel: 14,
minZoomLevel: this.osmSourceZoomLevel,
dontEnforceMinZoom: true,
registerTile: (tile) => {
new RegisteringAllFromFeatureSourceActor(tile)
@ -155,7 +155,7 @@ export default class FeaturePipeline {
tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile))
}
})
}else{
} else {
new RegisteringAllFromFeatureSourceActor(src)
perLayerHierarchy.get(id).registerTile(src)
src.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(src))
@ -200,7 +200,7 @@ export default class FeaturePipeline {
new PerLayerFeatureSourceSplitter(state.filteredLayers,
(source) => TiledFeatureSource.createHierarchy(source, {
layer: source.layer,
minZoomLevel: 14,
minZoomLevel: source.layer.layerDef.minzoom,
dontEnforceMinZoom: true,
maxFeatureCount: state.layoutToUse.clustering.minNeededElements,
maxZoomLevel: state.layoutToUse.clustering.maxZoom,
@ -235,7 +235,7 @@ export default class FeaturePipeline {
// Whenever fresh data comes in, we need to update the metatagging
self.newDataLoadedSignal.stabilized(1000).addCallback(src => {
self.newDataLoadedSignal.stabilized(1000).addCallback(_ => {
self.updateAllMetaTagging()
})
@ -276,15 +276,15 @@ export default class FeaturePipeline {
const self = this
return this.state.currentBounds.map(bbox => {
if (bbox === undefined) {
return
return undefined
}
if (!isSufficientlyZoomed.data) {
return;
return undefined;
}
const osmSourceZoomLevel = self.osmSourceZoomLevel
const range = bbox.containingTileRange(osmSourceZoomLevel)
const tileIndexes = []
if (range.total > 100) {
if (range.total >= 100) {
// Too much tiles!
return []
}
@ -294,7 +294,7 @@ export default class FeaturePipeline {
if (oldestDate !== undefined && oldestDate > this.oldestAllowedDate) {
console.debug("Skipping tile", osmSourceZoomLevel, x, y, "as a decently fresh one is available")
// The cached tiles contain decently fresh data
return;
return undefined;
}
tileIndexes.push(i)
})
@ -327,28 +327,30 @@ export default class FeaturePipeline {
}
const range = bbox.containingTileRange(zoom)
if (range.total > 100) {
if (range.total >= 5000) {
return false
}
const self = this;
const allFreshnesses = Tiles.MapRange(range, (x, y) => self.freshnessForVisibleLayers(zoom, x, y))
return allFreshnesses.some(freshness => freshness === undefined || freshness < this.oldestAllowedDate)
}, [state.locationControl])
const self = this;
const updater = new OverpassFeatureSource(state,
{
padToTiles: state.locationControl.map(l => Math.min(15, l.zoom + 1)),
relationTracker: this.relationTracker,
isActive: useOsmApi.map(b => !b && overpassIsActive.data, [overpassIsActive]),
onBboxLoaded: ((bbox, date, downloadedLayers) => {
Tiles.MapRange(bbox.containingTileRange(self.osmSourceZoomLevel), (x, y) => {
onBboxLoaded: (bbox, date, downloadedLayers, paddedToZoomLevel) => {
Tiles.MapRange(bbox.containingTileRange(paddedToZoomLevel), (x, y) => {
const tileIndex = Tiles.tile_index(paddedToZoomLevel, x, y)
downloadedLayers.forEach(layer => {
SaveTileToLocalStorageActor.MarkVisited(layer.id, Tiles.tile_index(this.osmSourceZoomLevel, x, y), date)
self.freshnesses.get(layer.id).addTileLoad(tileIndex, date)
SaveTileToLocalStorageActor.MarkVisited(layer.id, tileIndex, date)
})
})
})
}
});

View file

@ -1,14 +1,16 @@
import FilteredLayer from "../../../Models/FilteredLayer";
import {FeatureSourceForLayer, Tiled} from "../FeatureSource";
import {UIEventSource} from "../../UIEventSource";
import Loc from "../../../Models/Loc";
import TileHierarchy from "./TileHierarchy";
import SaveTileToLocalStorageActor from "../Actors/SaveTileToLocalStorageActor";
import {Tiles} from "../../../Models/TileRange";
import {BBox} from "../../BBox";
export default class TiledFromLocalStorageSource implements TileHierarchy<FeatureSourceForLayer & Tiled> {
public loadedTiles: Map<number, FeatureSourceForLayer & Tiled> = new Map<number, FeatureSourceForLayer & Tiled>();
public readonly loadedTiles: Map<number, FeatureSourceForLayer & Tiled> = new Map<number, FeatureSourceForLayer & Tiled>();
private readonly layer: FilteredLayer;
private readonly handleFeatureSource: (src: FeatureSourceForLayer & Tiled, index: number) => void;
private readonly undefinedTiles: Set<number>;
public static GetFreshnesses(layerId: string): Map<number, Date> {
const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layerId + "-"
@ -29,14 +31,15 @@ export default class TiledFromLocalStorageSource implements TileHierarchy<Featur
constructor(layer: FilteredLayer,
handleFeatureSource: (src: FeatureSourceForLayer & Tiled, index: number) => void,
state: {
locationControl: UIEventSource<Loc>
leafletMap: any
currentBounds: UIEventSource<BBox>
}) {
this.layer = layer;
this.handleFeatureSource = handleFeatureSource;
const undefinedTiles = new Set<number>()
this.undefinedTiles = new Set<number>()
const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layer.layerDef.id + "-"
// @ts-ignore
const indexes: number[] = Object.keys(localStorage)
const knownTiles: number[] = Object.keys(localStorage)
.filter(key => {
return key.startsWith(prefix) && !key.endsWith("-time") && !key.endsWith("-format");
})
@ -45,8 +48,8 @@ export default class TiledFromLocalStorageSource implements TileHierarchy<Featur
})
.filter(i => !isNaN(i))
console.debug("Layer", layer.layerDef.id, "has following tiles in available in localstorage", indexes.map(i => Tiles.tile_from_index(i).join("/")).join(", "))
for (const index of indexes) {
console.debug("Layer", layer.layerDef.id, "has following tiles in available in localstorage", knownTiles.map(i => Tiles.tile_from_index(i).join("/")).join(", "))
for (const index of knownTiles) {
const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layer.layerDef.id + "-" + index;
const version = localStorage.getItem(prefix + "-format")
@ -55,78 +58,54 @@ export default class TiledFromLocalStorageSource implements TileHierarchy<Featur
localStorage.removeItem(prefix)
localStorage.removeItem(prefix+"-time")
localStorage.removeItem(prefix+"-format")
undefinedTiles.add(index)
this. undefinedTiles.add(index)
console.log("Dropped old format tile", prefix)
}
}
const zLevels = indexes.map(i => i % 100)
const indexesSet = new Set(indexes)
const maxZoom = Math.max(...zLevels)
const minZoom = Math.min(...zLevels)
const self = this;
const self = this
state.currentBounds.map(bounds => {
const neededTiles = state.locationControl.map(
location => {
if (!layer.isDisplayed.data) {
// No need to download! - the layer is disabled
return undefined;
}
if (location.zoom < layer.layerDef.minzoom) {
// No need to download! - the layer is disabled
return undefined;
}
// Yup, this is cheating to just get the bounds here
const bounds = state.leafletMap.data?.getBounds()
if (bounds === undefined) {
// We'll retry later
return undefined
}
const needed = []
for (let z = minZoom; z <= maxZoom; z++) {
const tileRange = Tiles.TileRangeBetween(z, bounds.getNorth(), bounds.getEast(), bounds.getSouth(), bounds.getWest())
const neededZ = Tiles.MapRange(tileRange, (x, y) => Tiles.tile_index(z, x, y))
.filter(i => !self.loadedTiles.has(i) && !undefinedTiles.has(i) && indexesSet.has(i))
needed.push(...neededZ)
}
if (needed.length === 0) {
return undefined
}
return needed
if(bounds === undefined){
return;
}
, [layer.isDisplayed, state.leafletMap]).stabilized(50);
neededTiles.addCallbackAndRunD(neededIndexes => {
for (const neededIndex of neededIndexes) {
// We load the features from localStorage
try {
const key = SaveTileToLocalStorageActor.storageKey + "-" + layer.layerDef.id + "-" + neededIndex
const data = localStorage.getItem(key)
const features = JSON.parse(data)
const src = {
layer: layer,
features: new UIEventSource<{ feature: any; freshness: Date }[]>(features),
name: "FromLocalStorage(" + key + ")",
tileIndex: neededIndex,
bbox: BBox.fromTileIndex(neededIndex)
}
handleFeatureSource(src, neededIndex)
self.loadedTiles.set(neededIndex, src)
} catch (e) {
console.error("Could not load data tile from local storage due to", e)
undefinedTiles.add(neededIndex)
for (const knownTile of knownTiles) {
if(this.loadedTiles.has(knownTile)){
continue;
}
if(this.undefinedTiles.has(knownTile)){
continue;
}
if(!bounds.overlapsWith(BBox.fromTileIndex(knownTile))){
continue;
}
self.loadTile(knownTile)
}
})
}
private loadTile( neededIndex: number){
try {
const key = SaveTileToLocalStorageActor.storageKey + "-" + this.layer.layerDef.id + "-" + neededIndex
const data = localStorage.getItem(key)
const features = JSON.parse(data)
const src = {
layer: this.layer,
features: new UIEventSource<{ feature: any; freshness: Date }[]>(features),
name: "FromLocalStorage(" + key + ")",
tileIndex: neededIndex,
bbox: BBox.fromTileIndex(neededIndex)
}
this.handleFeatureSource(src, neededIndex)
this.loadedTiles.set(neededIndex, src)
} catch (e) {
console.error("Could not load data tile from local storage due to", e)
this.undefinedTiles.add(neededIndex)
}
}
}

View file

@ -209,7 +209,7 @@ export default class SimpleMetaTagger {
configurable: true,
get: () => {
delete feature.properties._isOpen
feature.properties._isOpen = ""
feature.properties._isOpen = undefined
const tagsSource = State.state.allElements.getEventSourceById(feature.properties.id);
tagsSource.addCallbackAndRunD(tags => {
if (tags.opening_hours === undefined || tags._country === undefined) {
@ -230,7 +230,8 @@ export default class SimpleMetaTagger {
const oldNextChange = tags["_isOpen:nextTrigger"] ?? 0;
if (oldNextChange > (new Date()).getTime() &&
tags["_isOpen:oldvalue"] === tags["opening_hours"]) {
tags["_isOpen:oldvalue"] === tags["opening_hours"]
&& tags["_isOpen"] !== undefined) {
// Already calculated and should not yet be triggered
return false;
}
@ -267,7 +268,7 @@ export default class SimpleMetaTagger {
}
})
return feature.properties["_isOpen"]
return undefined
}
})

View file

@ -2,9 +2,7 @@ import {Utils} from "../Utils";
export default class Constants {
public static vNumber = "0.11.0-alpha-1";
public static vNumber = "0.11.0-alpha-2";
public static ImgurApiKey = '7070e7167f0a25a'
public static readonly mapillary_client_token_v3 = 'TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2'
public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85"

View file

@ -90,7 +90,7 @@ export default class LayoutConfig {
throw "Widenfactor too small"
}else{
// Unofficial themes get away with this
console.warn("Detected a very small widenfactor, bumping this above 1.")
console.warn("Detected a very small widenfactor for theme ", this.id ,", bumping this above 1.")
json.widenFactor = json.widenFactor + 1
}
}

View file

@ -15,7 +15,7 @@ export class Tiles {
public static MapRange<T>(tileRange: TileRange, f: (x: number, y: number) => T): T[] {
const result: T[] = []
const total = tileRange.total
if(total > 5000){
if(total > 100000){
throw "Tilerange too big"
}
for (let x = tileRange.xstart; x <= tileRange.xend; x++) {

View file

@ -50,13 +50,11 @@ export default class ThemeIntroductionPanel extends Combine {
)
super([
layout.description.Clone(),
"<br/><br/>",
layout.description.Clone().SetClass("blcok mb-4"),
toTheMap,
loginStatus,
layout.descriptionTail?.Clone(),
"<br/>",
languagePicker,
loginStatus.SetClass("block"),
layout.descriptionTail?.Clone().SetClass("block mt-4"),
languagePicker.SetClass("block mt-4"),
...layout.CustomCodeSnippets()
])

View file

@ -55,7 +55,11 @@ export default class SpecialVisualizations {
if (!tags.hasOwnProperty(key)) {
continue
}
parts.push([key, tags[key] ?? "<b>undefined</b>"]);
let v = tags[key]
if(v === ""){
v = "<b>empty string</b>"
}
parts.push([key, v ?? "<b>undefined</b>"]);
}
for(const key of calculatedTags){

View file

@ -0,0 +1,25 @@
[
{
"path": "post_office.svg",
"license": "CC BY-SA 4.0",
"authors": [
"https://github.com/emojione/emojione/graphs/contributors"
],
"sources": [
"https://commons.wikimedia.org/wiki/File:Emojione_BW_1F3E4.svg",
"https://github.com/emojione/emojione/blob/2.2.7/assets/svg_bw/1f3e4.svg"
]
},
{
"path": "postbox.svg",
"license": "CC BY 4.0",
"authors": [
"Vincent Le Moign",
"https://twitter.com/webalys"
],
"sources": [
"https://upload.wikimedia.org/wikipedia/commons/6/6d/726-postbox.svg",
"http://emoji.streamlineicons.com"
]
}
]

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" enable-background="new 0 0 64 64"><path d="m6.219 22.625h3.75v5.625h-3.75z"/><path d="m11.844 22.625h3.75v5.625h-3.75z"/><path d="m20.281 22.625h3.75v5.625h-3.75z"/><path d="m25.906 22.625h3.75v5.625h-3.75z"/><path d="m34.344 22.625h3.75v5.625h-3.75z"/><path d="m39.969 22.625h3.75v5.625h-3.75z"/><path d="m48.406 22.625h3.75v5.625h-3.75z"/><path d="m54.031 22.625h3.75v5.625h-3.75z"/><path d="m6.219 33.875h3.75v5.625h-3.75z"/><path d="m11.844 33.875h3.75v5.625h-3.75z"/><path d="m20.281 33.875h3.75v5.625h-3.75z"/><path d="m25.906 33.875h3.75v5.625h-3.75z"/><path d="m34.344 33.875h3.75v5.625h-3.75z"/><path d="m39.969 33.875h3.75v5.625h-3.75z"/><path d="m48.406 33.875h3.75v5.625h-3.75z"/><path d="m54.031 33.875h3.75v5.625h-3.75z"/><path d="m16.063 48.875h5.625v5.625h-5.625z"/><path d="m27.313 47h3.75v4.688h-3.75z"/><path d="m42.313 48.875h5.625v5.625h-5.625z"/><path d="m49.813 48.875h5.625v5.625h-5.625z"/><path d="m8.563 48.875h5.625v5.625h-5.625z"/><path d="m32.938 47h3.75v4.688h-3.75z"/><path d="m30.146 11.078c.163.041.365.054.642.054 1.018 0 1.646-.553 1.646-1.481 0-.835-.54-1.333-1.496-1.333-.389 0-.651.04-.791.081v2.679z"/><path d="m37.529 11.401c0 1.778.778 3.031 2.061 3.031 1.295 0 2.035-1.32 2.035-3.085 0-1.63-.728-3.03-2.048-3.03-1.295 0-2.048 1.319-2.048 3.084"/><path d="m62 17.938v-13.125c0-.516-.422-.938-.938-.938h-.938v-1.875h-56.249v1.875h-.937c-.516 0-.938.422-.938.938v13.125c0 .516.422.938.938.938v39.374c-.516 0-.938.422-.938.938v1.875c0 .515.422.937.938.937h58.125c.515 0 .937-.422.937-.937v-1.875c0-.516-.422-.938-.938-.938v-39.375c.516 0 .938-.422.938-.937m-57.187 12.187v-9.375h54.375v9.375h-54.375m54.375 1.875v9.375h-54.375v-9.375h54.375m-18.75 26.25v-11.25h-1.875v11.25h-1.875v-4.688h-3.75v4.688h-1.875v-4.688h-3.75v4.688h-1.875v-11.25h-1.875v11.25h-1.875v-1.875h-15v-9.375h15v-3.75h20.625v3.75h15v9.375h-15v1.875h-1.875m11.303-49.69v-1.725h6.509v1.725h-2.312v7.354h-1.922v-7.354h-2.275m-4.38 3.542c-1.407-.525-2.323-1.361-2.323-2.68 0-1.549 1.206-2.735 3.203-2.735.954 0 1.658.215 2.161.458l-.427 1.657c-.339-.176-.943-.431-1.771-.431-.831 0-1.231.404-1.231.875 0 .579.478.835 1.57 1.28 1.496.592 2.199 1.428 2.199 2.707 0 1.522-1.095 2.815-3.418 2.815-.967 0-1.923-.269-2.399-.554l.389-1.697c.517.284 1.308.567 2.124.567.879 0 1.344-.392 1.344-.983-.001-.566-.402-.888-1.421-1.279m-3.714-.821c0 2.976-1.683 4.782-4.158 4.782-2.513 0-3.982-2.034-3.982-4.621 0-2.721 1.621-4.754 4.12-4.754 2.602 0 4.02 2.087 4.02 4.593m-9.316-1.684c0 .889-.276 1.643-.779 2.155-.652.66-1.62.957-2.751.957-.252 0-.479-.015-.654-.041v3.246h-1.897v-8.957c.591-.107 1.419-.188 2.587-.188 1.183 0 2.023.243 2.59.728.54.457.904 1.211.904 2.1m-10.934-1.927h1.103c0 5.152-4.197 9.33-9.375 9.33s-9.375-4.178-9.375-9.33h4.413c0 .95.205 1.85.567 2.666.184-2.563 2.324-4.586 4.945-4.586 2.743 0 4.965 2.211 4.965 4.938 0 1.078-.352 2.072-.94 2.885 2.189-1.074 3.697-3.311 3.697-5.903"/><path d="m15.676 7.395c-1.823 0-3.309 1.476-3.309 3.293 0 1.819 1.485 3.293 3.309 3.293 1.827 0 3.31-1.474 3.31-3.293-.001-1.817-1.483-3.293-3.31-3.293"/></svg>

After

Width:  |  Height:  |  Size: 3 KiB

View file

@ -0,0 +1,143 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Icons" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.15;fill:#45413C;}
.st1{fill:#BF8256;}
.st2{fill:#915E3A;}
.st3{fill:none;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st4{fill:#DEA47A;}
.st5{fill:#DAEDF7;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st6{fill:#BF8256;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st7{fill:#FF6242;}
.st8{fill:#FF866E;}
.st9{fill:#DEBB7E;}
.st10{fill:#B89558;}
.st11{fill:#656769;}
.st12{fill:#525252;}
.st13{fill:#E04122;}
.st14{fill:#FFFFFF;}
.st15{fill:#F0F0F0;}
.st16{fill:#00B8F0;}
.st17{fill:#4ACFFF;}
.st18{fill:#C0DCEB;}
.st19{fill:#8CA4B8;}
.st20{fill:#FF6242;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st21{fill:#87898C;}
.st22{fill:#E0E0E0;}
.st23{fill:#E8F4FA;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st24{fill:#656769;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st25{fill:#DAEDF7;}
.st26{fill:#E8F4FA;}
.st27{fill:#ADC4D9;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st28{fill:#87898C;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st29{fill:#BDBEC0;}
.st30{fill:#FFFFFF;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st31{fill:#ADC4D9;}
.st32{fill:none;stroke:#00AED9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st33{fill:#FFFACF;}
.st34{fill:#FFE500;}
.st35{fill:#915E3A;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st36{fill:#FFAA54;}
.st37{fill:#627B8C;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st38{fill:#FFFEF2;}
.st39{fill:#FFFCE5;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st40{fill:#00F5BC;}
.st41{fill:#FFFCE5;}
.st42{fill:#FFFEF2;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st43{fill:#8CFFE4;}
.st44{fill:#FFF5E3;}
.st45{fill:#F7E5C6;}
.st46{fill:#F7E5C6;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st47{fill:#F0D5A8;}
.st48{fill:#FF87AF;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st49{fill:#45413C;}
.st50{fill:#BDBEC0;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st51{fill:#E0E0E0;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st52{fill:#DEBB7E;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st53{fill:#F5EBFF;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st54{fill:#BF8DF2;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st55{fill:#E4FFD1;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st56{fill:#F0FFE5;}
.st57{fill:#C8FFA1;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st58{fill:#6DD627;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st59{fill:#E5FEFF;}
.st60{fill:#FFAA54;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st61{fill:#9CEB60;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st62{fill:#FFF48C;}
.st63{fill:#00B8F0;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st64{fill:none;stroke:#4F4B45;stroke-linejoin:round;stroke-miterlimit:10;}
.st65{fill:#FFFFFF;stroke:#4F4B45;stroke-linejoin:round;stroke-miterlimit:10;}
.st66{fill:#FFF5E3;stroke:#4F4B45;stroke-linejoin:round;stroke-miterlimit:10;}
.st67{fill:#F7E5C6;stroke:#4F4B45;stroke-linejoin:round;stroke-miterlimit:10;}
.st68{fill:#6DD627;}
.st69{fill:#EBCB00;}
.st70{fill:#46B000;}
.st71{fill:none;stroke:#E0E0E0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st72{fill:#9CEB60;}
.st73{fill:#FFCC99;}
.st74{fill:#BF8DF2;}
.st75{fill:#9F5AE5;}
.st76{fill:#DABFF5;}
.st77{fill:#F0F0F0;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st78{fill:#E5F8FF;}
.st79{fill:#B8ECFF;}
.st80{fill:#D9FDFF;}
.st81{fill:#C0DCEB;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st82{fill:#46B000;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st83{fill:none;stroke:#FFFFFF;stroke-miterlimit:10;}
.st84{fill:none;stroke:#FFFFFF;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st85{fill:#FFE500;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st86{fill:#80DDFF;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st87{fill:#FFFFFF;stroke:#45413C;stroke-linejoin:round;stroke-miterlimit:10;}
.st88{fill:#009FD9;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st89{fill:#E04122;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st90{fill:#009FD9;}
.st91{fill:#FFFFFF;stroke:#45413C;stroke-miterlimit:10;}
.st92{fill:#009FD9;stroke:#45413C;stroke-miterlimit:10;}
.st93{fill:#FF8A14;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st94{fill:none;stroke:#009FD9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st95{fill:#525252;stroke:#45413C;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st96{fill:none;stroke:#46B000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st97{fill:none;stroke:#E04122;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st98{fill:#6DD627;stroke:#45413C;stroke-miterlimit:10;}
.st99{fill:none;stroke:#45413C;stroke-linejoin:round;stroke-miterlimit:10;}
.st100{fill:#46B000;stroke:#45413C;stroke-miterlimit:10;}
.st101{fill:#00DFEB;}
.st102{fill:#00AD85;}
.st103{fill:#E04122;stroke:#45413C;stroke-miterlimit:10;}
</style>
<g id="XMLID_15623_">
<ellipse id="XMLID_15640_" class="st0" cx="24" cy="44.4" rx="12.3" ry="1.6"/>
<path id="XMLID_15639_" class="st7" d="M34,33.9H14c-1.8,0-3.2-1.4-3.2-3.2V6.1c0-1.8,1.4-3.2,3.2-3.2h20c1.8,0,3.2,1.4,3.2,3.2
v24.6C37.2,32.5,35.8,33.9,34,33.9z"/>
<path id="XMLID_15638_" class="st8" d="M34,2.9H14c-1.8,0-3.2,1.4-3.2,3.2v2.5c0-1.8,1.4-3.2,3.2-3.2h20c1.8,0,3.2,1.4,3.2,3.2V6.1
C37.2,4.3,35.8,2.9,34,2.9z"/>
<path id="XMLID_15637_" class="st3" d="M34,33.9H14c-1.8,0-3.2-1.4-3.2-3.2V6.1c0-1.8,1.4-3.2,3.2-3.2h20c1.8,0,3.2,1.4,3.2,3.2
v24.6C37.2,32.5,35.8,33.9,34,33.9z"/>
<path id="XMLID_15636_" class="st9" d="M26.2,33.9h-4.5v9.5c0,0.6,0.5,1.1,1.1,1.1h2.3c0.6,0,1.1-0.5,1.1-1.1V33.9z"/>
<rect id="XMLID_15635_" x="21.8" y="33.9" class="st10" width="4.5" height="2.7"/>
<path id="XMLID_15634_" class="st3" d="M26.2,33.9h-4.5v9.5c0,0.6,0.5,1.1,1.1,1.1h2.3c0.6,0,1.1-0.5,1.1-1.1V33.9z"/>
<path id="XMLID_15633_" class="st11" d="M32.5,14.5h-17c-0.6,0-1.1-0.5-1.1-1.1v-1.3c0-0.6,0.5-1.1,1.1-1.1h17
c0.6,0,1.1,0.5,1.1,1.1v1.3C33.6,14.1,33.1,14.5,32.5,14.5z"/>
<path id="XMLID_15632_" class="st12" d="M32.5,11.1h-17c-0.6,0-1.1,0.5-1.1,1.1v1.3c0-0.6,0.5-1.1,1.1-1.1h17
c0.6,0,1.1,0.5,1.1,1.1v-1.3C33.6,11.5,33.1,11.1,32.5,11.1z"/>
<path id="XMLID_15631_" class="st3" d="M32.5,14.5h-17c-0.6,0-1.1-0.5-1.1-1.1v-1.3c0-0.6,0.5-1.1,1.1-1.1h17
c0.6,0,1.1,0.5,1.1,1.1v1.3C33.6,14.1,33.1,14.5,32.5,14.5z"/>
<path class="st13" d="M33.9,6.6H14.1c-0.6,0-1.1,0.5-1.1,1.1c0,0.6,0.5,1.1,1.1,1.1h19.8c0.6,0,1.1-0.5,1.1-1.1
C35,7.1,34.5,6.6,33.9,6.6z"/>
<path id="XMLID_15630_" class="st7" d="M14.1,7.7h19.8c0.4,0,0.8,0.2,1,0.6C35,8.1,35,7.9,35,7.7c0-0.6-0.5-1.1-1.1-1.1H14.1
c-0.6,0-1.1,0.5-1.1,1.1c0,0.2,0.1,0.4,0.1,0.6C13.3,7.9,13.7,7.7,14.1,7.7z"/>
<path class="st3" d="M33.9,6.6H14.1c-0.6,0-1.1,0.5-1.1,1.1c0,0.6,0.5,1.1,1.1,1.1h19.8c0.6,0,1.1-0.5,1.1-1.1
C35,7.1,34.5,6.6,33.9,6.6z"/>
<path id="XMLID_15629_" class="st14" d="M33.6,21.2h-9.8c-0.4,0-0.8-0.2-1-0.6l-4.3-9.5h11.3l4.2,9.3
C34.2,20.8,33.9,21.2,33.6,21.2z"/>
<polygon id="XMLID_15628_" class="st15" points="30.9,13.3 29.8,11.1 18.5,11.1 19.6,13.3 "/>
<path id="XMLID_15627_" class="st3" d="M33.6,21.2h-9.8c-0.4,0-0.8-0.2-1-0.6l-4.3-9.5h11.3l4.2,9.3C34.2,20.8,33.9,21.2,33.6,21.2
z"/>
<polyline id="XMLID_15626_" class="st3" points="34,20.9 26.5,14.7 27.3,11.1 "/>
<line id="XMLID_15625_" class="st3" x1="27.6" y1="15.7" x2="23.2" y2="21"/>
<line id="XMLID_15624_" class="st3" x1="26.7" y1="13.7" x2="21.5" y2="11.1"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.8 KiB

View file

@ -0,0 +1,174 @@
{
"id": "postboxes",
"title": {
"en": "Postbox and Post Office Map"
},
"shortDescription": {
"en": "A map showing postboxes and post offices"
},
"description": {
"en": "On this map you can find and add data of post offices and post boxes. You can use this map to find where you can mail your next postcard! :)<br/>Spotted an error or is a post box missing? You can edit this map with a free OpenStreetMap account. "
},
"language": [
"en"
],
"maintainer": "",
"icon": "./assets/themes/postboxes/postbox.svg",
"version": "0",
"startLat": 53.5511,
"startLon": 9.9937,
"startZoom": 13,
"widenFactor": 1.5,
"defaultBackgroundId": "CartoDB.Voyager",
"clustering": {
"maxZoom": 14,
"minNeededElements": 100
},
"layers": [
{
"id": "postboxes",
"name": {
"en": "Postboxes"
},
"minzoom": 12,
"source": {
"osmTags": "amenity=post_box"
},
"title": {
"render": {
"en": "Postbox"
}
},
"description": {
"en": "The layer showing postboxes."
},
"tagRenderings": [
"images",
{
"id": "minimap",
"render": "{minimap(18): height: 5rem; overflow: hidden; border-radius:3rem; }"
}
],
"icon": {
"render": "./assets/themes/postboxes/postbox.svg"
},
"width": {
"render": "1"
},
"iconSize": {
"render": "40,40,bottom"
},
"color": {
"render": "#DADADA"
},
"presets": [
{
"tags": [
"amenity=post_box"
],
"title": {
"en": "postbox"
}
}
],
"wayHandling": 2,
"deletion": {
"softDeletion": {
"and": [
"amenity=",
"razed:amenity=post_box"
]
}
}
},
{
"id": "postoffices",
"name": {
"en": "Post offices"
},
"minzoom": 12,
"source": {
"osmTags": "amenity=post_office"
},
"title": {
"render": {
"en": "Post Office"
}
},
"description": {
"en": "A layer showing post offices."
},
"tagRenderings": [
"images",
{
"id": "minimap",
"render": "{minimap(18): height: 5rem; overflow: hidden; border-radius:3rem; }"
},
{
"render": {
"en": "Opening Hours: {opening_hours_table()}"
},
"freeform": {
"key": "opening_hours",
"type": "opening_hours"
},
"question": {
"en": "What are the opening hours for this post office?"
},
"mappings": [
{
"if": "opening_hours=24/7",
"then": {
"en": "24/7 opened (including holidays)"
}
}
],
"id": "OH"
}
],
"icon": {
"render": "square:white;./assets/themes/postboxes/post_office.svg"
},
"iconOverlays": [
{
"if": "opening_hours~*",
"then": "isOpen",
"badge": true
}
],
"width": {
"render": "1"
},
"iconSize": {
"render": "40,40,bottom"
},
"color": {
"render": "#DADADA"
},
"presets": [
{
"tags": [
"amenity=post_office"
],
"title": {
"en": "Post Office"
}
}
],
"wayHandling": 2,
"filter": [
{
"id": "is_open",
"options": [
{
"question": {
"en": "Currently open"
},
"osmTags": "_isOpen=yes"
}
]
}
]
}
]
}

View file

@ -14,7 +14,10 @@
"nl": "Een kaart om toeristisch relevante info op aan te duiden"
},
"description": {
"nl": "Op deze kaart kan je info zien die relevant is voor toerisme, zoals:<br/><ul><li>Eetgelegenheden</li><li>Cafés en bars</li><li>(Fiets)oplaadpunten</li><li>Fietspompen, fietserverhuur en fietswinkels</li><li>Uitkijktorens</li><li>...</li></ul> Zie je fouten op de kaart? Dan kan je zelf makkelijk aanpasingen maken, die zichtbaar zijn voor iedereen. Hiervoor dien je een gratis OpenStreetMap account voor te maken.<br/><br/>Met de steun van Toerisme Vlaanderen<img src='./assets/themes/toerisme_vlaanderen/logo.png' />"
"nl": "Op deze kaart kan je info zien die relevant is voor toerisme, zoals:<br/><ul><li>Eetgelegenheden</li><li>Cafés en bars</li><li>(Fiets)oplaadpunten</li><li>Fietspompen, fietserverhuur en fietswinkels</li><li>Uitkijktorens</li><li>...</li></ul> Zie je fouten op de kaart? Dan kan je zelf makkelijk aanpasingen maken, die zichtbaar zijn voor iedereen. Hiervoor dien je een gratis OpenStreetMap account voor te maken."
},
"descriptionTail": {
"nl": "Met de steun van Toerisme Vlaanderen<img style='height:5rem; width: auto;' src='./assets/themes/toerisme_vlaanderen/logo.png' />"
},
"icon": "./assets/svg/star.svg",
"startZoom": 8,
@ -28,16 +31,38 @@
"cafe_pub"
],
"override": {
"minzoom": 16
"minzoom": 17
}
},
"charging_station",
"toilet",
"bench",
"waste_basket",
"bike_repair_station",
"binocular",
"observation_tower"
{
"builtin": [
"bench",
"picnic_table",
"waste_basket"
],
"override": {
"minzoom": 19
}
},
{
"builtin": [
"charging_station",
"toilet",
"bike_repair_station"
],
"override": {
"minzoom": 14
}
},
{
"builtin": [
"binocular",
"observation_tower"
],
"override": {
"minzoom": 10
}
}
],
"hideFromOverview": true
}

View file

@ -151,7 +151,9 @@
},
"freeform": {
"key": "addr:housenumber",
"addExtraTags": "nohousenumber="
"addExtraTags": [
"nohousenumber="
]
},
"mappings": [
{

View file

@ -20,5 +20,20 @@
"uploadingMultiple": "A enviar {count} imagens…",
"uploadingPicture": "A enviar a sua imagem…",
"addPicture": "Adicionar imagem"
},
"index": {
"#": "Estes textos são mostrados acima dos botões do tema quando nenhum tema é carregado",
"title": "Bem-vindo(a) ao MapComplete",
"intro": "O MapComplete é um visualizador e editor do OpenStreetMap, que mostra informações sobre um tema específico.",
"pickTheme": "Escolha um tema abaixo para começar."
},
"delete": {
"reasons": {
"notFound": "Não foi possível encontrar este elemento"
},
"explanations": {
"selectReason": "Por favor, selecione a razão porque este elemento deve ser eliminado",
"hardDelete": "Este ponto será eliminado no OpenStreetMap. Pode ser recuperado por um contribuidor com experiência"
}
}
}

View file

@ -8,6 +8,25 @@
},
"website": {
"question": "Apa situs web dari {name}?"
},
"payment-options": {
"mappings": {
"1": {
"then": "Disini menerima pembayaran dengan kartu"
},
"0": {
"then": "Disini menerima pembayaran tunai"
}
},
"question": "Metode pembayaran manakah yang di terima disini?"
},
"level": {
"question": "Pada tingkat apa fitur ini diletakkan?",
"mappings": {
"3": {
"then": "Berlokasi di lantai pertama"
}
}
}
}
}
}

View file

@ -61,6 +61,23 @@
}
},
"question": "Este lugar é acessível a utilizadores de cadeiras de rodas?"
},
"dog-access": {
"mappings": {
"0": {
"then": "Os cães são permitidos"
},
"1": {
"then": "Os cães <b>não</b> são permitidos"
},
"2": {
"then": "Os cães são permitidos, mas têm de ser presos pela trela"
},
"3": {
"then": "Os cães são permitidos e podem correr livremente"
}
},
"question": "Os cães são permitidos neste estabelecimento?"
}
}
}
}

View file

@ -1358,11 +1358,6 @@
"title": {
"render": "Known address"
}
},
"2": {
"title": {
"render": "{name}"
}
}
},
"shortDescription": "Help to build an open dataset of UK addresses",