Merge branch 'master' into HEAD
17
.github/pull_request_template.md
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
|
||||
Opening a pull request on MapComplete
|
||||
=====================================
|
||||
|
||||
Hey! Thanks for opening a pull request on Mapcomplete! This probably means you want to add a new theme - if so, please follow the checklist below.
|
||||
If this pull request is for some other issue, please ignore the template.
|
||||
|
||||
Adding your new theme
|
||||
---------------------
|
||||
|
||||
Thanks for taking the time to create your theme and to add it to the main repository!
|
||||
|
||||
To making merging smooth, please make sure that each of the following conditions is met:
|
||||
|
||||
- [ ] The codebase is GPL-licensed. By opening a pull request, the new theme will be GPL too
|
||||
- [ ] All images are included in the pull request and no images are loaded from an external service (e.g. Wikipedia)
|
||||
- [ ] The [guidelines on how to make your own theme](https://github.com/pietervdvn/MapComplete/blob/master/Docs/Making_Your_Own_Theme.md) are read and followed
|
65
.github/workflows/pull_request_check.yml
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
name: Pull request check
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, edited, synchronize, ready_for_review, review_requested]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v1.2.0
|
||||
env:
|
||||
ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true'
|
||||
|
||||
- name: install deps
|
||||
run: npm install
|
||||
|
||||
- name: create generated dir
|
||||
run: mkdir ./assets/generated
|
||||
|
||||
- name: create stub themes
|
||||
run: "echo '{\"layers\": [], \"themes\": []}' > ./assets/generated/known_layers_and_themes.json"
|
||||
|
||||
- name: Compile license info
|
||||
run: npm run generate:licenses
|
||||
|
||||
- name: Compile and validate themes and layers
|
||||
run: npm run validate:layeroverview
|
||||
|
||||
- name: Validate license info
|
||||
run: npm run validate:licenses
|
||||
|
||||
- name: Set failure key
|
||||
run: |
|
||||
ls
|
||||
if [[ -f "layer_report.txt" || -f "missing_licenses.txt" ]]; then
|
||||
echo "Found a report..."
|
||||
echo "VALIDATION_FAILED=true" >> $GITHUB_ENV
|
||||
else
|
||||
echo "VALIDATION_FAILED=false" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Test variable
|
||||
run: echo "${{ env.VALIDATION_FAILED }}"
|
||||
|
||||
- name: Archive reports
|
||||
uses: actions/upload-artifact@v2
|
||||
if: >-
|
||||
env.VALIDATION_FAILED == 'true'
|
||||
with:
|
||||
name: reports
|
||||
path: |
|
||||
layer_report.txt
|
||||
missing_licenses.txt
|
||||
|
||||
- name: Comment PR
|
||||
uses: allthatjazzleo/actions-pull-request-add-comment@master
|
||||
if: >-
|
||||
env.VALIDATION_FAILED == 'true'
|
||||
with:
|
||||
message: "cat layer_report.txt missing_licenses.txt"
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
32
.github/workflows/theme_validation.yml
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
name: Theme Validation
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v1.2.0
|
||||
env:
|
||||
ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true'
|
||||
|
||||
- name: install deps
|
||||
run: npm install
|
||||
|
||||
- name: create generated dir
|
||||
run: mkdir ./assets/generated
|
||||
|
||||
- name: create stub themes
|
||||
run: "echo '{\"layers\": [], \"themes\": []}' > ./assets/generated/known_layers_and_themes.json"
|
||||
|
||||
|
||||
- name: Compile license info
|
||||
run: npm run validate:licenses
|
||||
|
||||
- name: Compile themes and layers
|
||||
run: npm run validate:layeroverview
|
|
@ -1,90 +1,35 @@
|
|||
import * as drinkingWater from "../assets/layers/drinking_water/drinking_water.json";
|
||||
import * as ghostbikes from "../assets/layers/ghost_bike/ghost_bike.json"
|
||||
import * as viewpoint from "../assets/layers/viewpoint/viewpoint.json"
|
||||
import * as bike_parking from "../assets/layers/bike_parking/bike_parking.json"
|
||||
import * as bike_repair_station from "../assets/layers/bike_repair_station/bike_repair_station.json"
|
||||
import * as birdhides from "../assets/layers/bird_hide/birdhides.json"
|
||||
import * as nature_reserve from "../assets/layers/nature_reserve/nature_reserve.json"
|
||||
import * as bike_cafes from "../assets/layers/bike_cafe/bike_cafes.json"
|
||||
import * as bike_monitoring_station from "../assets/layers/bike_monitoring_station/bike_monitoring_station.json"
|
||||
import * as cycling_themed_objects from "../assets/layers/cycling_themed_object/cycling_themed_objects.json"
|
||||
import * as bike_shops from "../assets/layers/bike_shop/bike_shop.json"
|
||||
import * as bike_cleaning from "../assets/layers/bike_cleaning/bike_cleaning.json"
|
||||
import * as bicycle_library from "../assets/layers/bicycle_library/bicycle_library.json"
|
||||
import * as bicycle_tube_vending_machine from "../assets/layers/bicycle_tube_vending_machine/bicycle_tube_vending_machine.json"
|
||||
import * as maps from "../assets/layers/maps/maps.json"
|
||||
import * as information_boards from "../assets/layers/information_board/information_board.json"
|
||||
import * as direction from "../assets/layers/direction/direction.json"
|
||||
import * as surveillance_camera from "../assets/layers/surveillance_cameras/surveillance_cameras.json"
|
||||
import * as toilets from "../assets/layers/toilets/toilets.json"
|
||||
import * as bookcases from "../assets/layers/public_bookcases/public_bookcases.json"
|
||||
import * as tree_nodes from "../assets/layers/trees/tree_nodes.json"
|
||||
import * as benches from "../assets/layers/benches/benches.json"
|
||||
import * as benches_at_pt from "../assets/layers/benches/benches_at_pt.json"
|
||||
import * as picnic_tables from "../assets/layers/benches/picnic_tables.json"
|
||||
import * as play_forest from "../assets/layers/play_forest/play_forest.json"
|
||||
import * as playground from "../assets/layers/playground/playground.json"
|
||||
import * as sport_pitch from "../assets/layers/sport_pitch/sport_pitch.json"
|
||||
import * as slow_roads from "../assets/layers/slow_roads/slow_roads.json"
|
||||
import LayerConfig from "./JSON/LayerConfig";
|
||||
import {LayerConfigJson} from "./JSON/LayerConfigJson";
|
||||
import * as grass_in_parks from "../assets/layers/village_green/grass_in_parks.json"
|
||||
import * as village_green from "../assets/layers/village_green/village_green.json"
|
||||
import * as known_layers from "../assets/generated/known_layers_and_themes.json"
|
||||
import {Utils} from "../Utils";
|
||||
|
||||
export default class AllKnownLayers {
|
||||
|
||||
|
||||
private static sharedLayersListRaw : LayerConfigJson[] = [
|
||||
drinkingWater,
|
||||
ghostbikes,
|
||||
viewpoint,
|
||||
bike_parking,
|
||||
bike_repair_station,
|
||||
bike_monitoring_station,
|
||||
birdhides,
|
||||
nature_reserve,
|
||||
bike_cafes,
|
||||
bicycle_library,
|
||||
cycling_themed_objects,
|
||||
bike_shops,
|
||||
bike_cleaning,
|
||||
bicycle_tube_vending_machine,
|
||||
maps,
|
||||
direction,
|
||||
information_boards,
|
||||
toilets,
|
||||
bookcases,
|
||||
surveillance_camera,
|
||||
tree_nodes,
|
||||
benches,
|
||||
benches_at_pt,
|
||||
picnic_tables,
|
||||
play_forest,
|
||||
playground,
|
||||
sport_pitch,
|
||||
slow_roads,
|
||||
grass_in_parks,
|
||||
village_green
|
||||
];
|
||||
|
||||
// Must be below the list...
|
||||
public static sharedLayers: Map<string, LayerConfig> = AllKnownLayers.getSharedLayers();
|
||||
public static sharedLayersJson: Map<string, any> = AllKnownLayers.getSharedLayersJson();
|
||||
|
||||
private static sharedLayersListRaw: LayerConfigJson[] = known_layers.layers;
|
||||
|
||||
private static getSharedLayers(): Map<string, LayerConfig> {
|
||||
const sharedLayers = new Map<string, LayerConfig>();
|
||||
for (const layer of AllKnownLayers.sharedLayersListRaw) {
|
||||
for (const layer of known_layers.layers) {
|
||||
try {
|
||||
const parsed = new LayerConfig(layer, "shared_layers")
|
||||
sharedLayers.set(layer.id, parsed);
|
||||
sharedLayers[layer.id] = parsed;
|
||||
} catch (e) {
|
||||
if (!Utils.runningFromConsole) {
|
||||
console.error("CRITICAL: Could not parse a layer configuration!", layer.id, " due to", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
return sharedLayers;
|
||||
}
|
||||
|
||||
private static getSharedLayersJson(): Map<string, any> {
|
||||
const sharedLayers = new Map<string, any>();
|
||||
for (const layer of AllKnownLayers.sharedLayersListRaw) {
|
||||
for (const layer of known_layers.layers) {
|
||||
sharedLayers.set(layer.id, layer);
|
||||
sharedLayers[layer.id] = layer;
|
||||
}
|
||||
|
|
|
@ -1,74 +1,29 @@
|
|||
import * as bookcases from "../assets/themes/bookcases/Bookcases.json";
|
||||
import * as aed from "../assets/themes/aed/aed.json";
|
||||
import * as toilets from "../assets/themes/toilets/toilets.json";
|
||||
import * as artworks from "../assets/themes/artwork/artwork.json";
|
||||
import * as cyclestreets from "../assets/themes/cyclestreets/cyclestreets.json";
|
||||
import * as ghostbikes from "../assets/themes/ghostbikes/ghostbikes.json"
|
||||
import * as cyclofix from "../assets/themes/cyclofix/cyclofix.json"
|
||||
import * as buurtnatuur from "../assets/themes/buurtnatuur/buurtnatuur.json"
|
||||
import * as nature from "../assets/themes/nature/nature.json"
|
||||
import * as maps from "../assets/themes/maps/maps.json"
|
||||
import * as shops from "../assets/themes/shops/shops.json"
|
||||
import * as bike_monitoring_stations from "../assets/themes/bike_monitoring_station/bike_monitoring_stations.json"
|
||||
import * as fritures from "../assets/themes/fritures/fritures.json"
|
||||
import * as benches from "../assets/themes/benches/benches.json";
|
||||
import * as charging_stations from "../assets/themes/charging_stations/charging_stations.json"
|
||||
import * as widths from "../assets/themes/widths/width.json"
|
||||
import * as drinking_water from "../assets/themes/drinking_water/drinking_water.json"
|
||||
import * as climbing from "../assets/themes/climbing/climbing.json"
|
||||
import * as surveillance_cameras from "../assets/themes/surveillance_cameras/surveillance_cameras.json"
|
||||
import * as trees from "../assets/themes/trees/trees.json"
|
||||
import * as personal from "../assets/themes/personalLayout/personalLayout.json"
|
||||
import * as playgrounds from "../assets/themes/playgrounds/playgrounds.json"
|
||||
import * as bicycle_lib from "../assets/themes/bicycle_library/bicycle_library.json"
|
||||
import * as campersites from "../assets/themes/campersites/campersites.json"
|
||||
import * as play_forests from "../assets/themes/play_forests/play_forests.json"
|
||||
import * as speelplekken from "../assets/themes/speelplekken/speelplekken.json"
|
||||
import * as sport_pitches from "../assets/themes/sport_pitches/sport_pitches.json"
|
||||
import * as grb from "../assets/themes/grb.json"
|
||||
import * as facadegardens from "../assets/themes/facadegardens/facadegardens.json"
|
||||
import LayerConfig from "./JSON/LayerConfig";
|
||||
import LayoutConfig from "./JSON/LayoutConfig";
|
||||
import AllKnownLayers from "./AllKnownLayers";
|
||||
|
||||
import * as known_themes from "../assets/generated/known_layers_and_themes.json"
|
||||
import {LayoutConfigJson} from "./JSON/LayoutConfigJson";
|
||||
import * as all_layouts from "../assets/generated/known_layers_and_themes.json"
|
||||
export class AllKnownLayouts {
|
||||
|
||||
public static allLayers: Map<string, LayerConfig> = undefined;
|
||||
public static layoutsList: LayoutConfig[] = [
|
||||
new LayoutConfig(personal),
|
||||
AllKnownLayouts.GenerateCycloFix(),
|
||||
new LayoutConfig(aed),
|
||||
new LayoutConfig(bookcases),
|
||||
new LayoutConfig(toilets),
|
||||
new LayoutConfig(artworks),
|
||||
new LayoutConfig(ghostbikes),
|
||||
new LayoutConfig(shops),
|
||||
new LayoutConfig(drinking_water),
|
||||
new LayoutConfig(nature),
|
||||
new LayoutConfig(cyclestreets),
|
||||
new LayoutConfig(bicycle_lib),
|
||||
new LayoutConfig(maps),
|
||||
new LayoutConfig(fritures),
|
||||
new LayoutConfig(benches),
|
||||
new LayoutConfig(charging_stations),
|
||||
new LayoutConfig(widths),
|
||||
new LayoutConfig(buurtnatuur),
|
||||
new LayoutConfig(bike_monitoring_stations),
|
||||
new LayoutConfig(surveillance_cameras),
|
||||
new LayoutConfig(climbing),
|
||||
new LayoutConfig(playgrounds),
|
||||
new LayoutConfig(trees),
|
||||
new LayoutConfig(campersites),
|
||||
new LayoutConfig(play_forests),
|
||||
new LayoutConfig(speelplekken),
|
||||
new LayoutConfig(sport_pitches),
|
||||
new LayoutConfig(grb),
|
||||
new LayoutConfig(facadegardens)
|
||||
];
|
||||
public static allSets: Map<string, LayoutConfig> = AllKnownLayouts.AllLayouts();
|
||||
|
||||
private static GenerateCycloFix(): LayoutConfig {
|
||||
const layout = new LayoutConfig(cyclofix)
|
||||
public static allKnownLayouts: Map<string, LayoutConfig> = AllKnownLayouts.AllLayouts();
|
||||
public static layoutsList: LayoutConfig[] = AllKnownLayouts.GenerateOrderedList(AllKnownLayouts.allKnownLayouts);
|
||||
|
||||
private static GenerateOrderedList(allKnownLayouts: Map<string, LayoutConfig>): LayoutConfig[] {
|
||||
const keys = ["personal", "cyclofix", "bookcases", "toilets", "aed"]
|
||||
const list = []
|
||||
for (const key of keys) {
|
||||
list.push(allKnownLayouts.get(key))
|
||||
}
|
||||
allKnownLayouts.forEach((layout, key) => {
|
||||
if (keys.indexOf(key) < 0) {
|
||||
list.push(layout)
|
||||
}
|
||||
})
|
||||
return list;
|
||||
}
|
||||
|
||||
private static AddGhostBikes(layout: LayoutConfig): LayoutConfig {
|
||||
const now = new Date();
|
||||
const m = now.getMonth() + 1;
|
||||
const day = new Date().getDate() + 1;
|
||||
|
@ -86,8 +41,15 @@ export class AllKnownLayouts {
|
|||
}
|
||||
|
||||
private static AllLayouts(): Map<string, LayoutConfig> {
|
||||
this.allLayers = new Map<string, LayerConfig>();
|
||||
for (const layout of this.layoutsList) {
|
||||
const dict: Map<string, LayoutConfig> = new Map();
|
||||
for (const layoutConfigJson of known_themes.themes) {
|
||||
const layout = new LayoutConfig(layoutConfigJson, true)
|
||||
|
||||
if (layout.id === "cyclofix") {
|
||||
AllKnownLayouts.AddGhostBikes(layout)
|
||||
}
|
||||
dict.set(layout.id, layout)
|
||||
|
||||
for (let i = 0; i < layout.layers.length; i++) {
|
||||
let layer = layout.layers[i];
|
||||
if (typeof (layer) === "string") {
|
||||
|
@ -98,20 +60,9 @@ export class AllKnownLayouts {
|
|||
}
|
||||
}
|
||||
|
||||
if (this.allLayers[layer.id] !== undefined) {
|
||||
continue;
|
||||
}
|
||||
this.allLayers[layer.id] = layer;
|
||||
this.allLayers[layer.id.toLowerCase()] = layer;
|
||||
}
|
||||
}
|
||||
|
||||
const allSets: Map<string, LayoutConfig> = new Map();
|
||||
for (const layout of this.layoutsList) {
|
||||
allSets[layout.id] = layout;
|
||||
allSets[layout.id.toLowerCase()] = layout;
|
||||
}
|
||||
return allSets;
|
||||
return dict;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import {TagRenderingConfigJson} from "./TagRenderingConfigJson";
|
|||
import {Translation} from "../../UI/i18n/Translation";
|
||||
import Img from "../../UI/Base/Img";
|
||||
import Svg from "../../Svg";
|
||||
|
||||
import {Utils} from "../../Utils";
|
||||
import Combine from "../../UI/Base/Combine";
|
||||
import {VariableUiElement} from "../../UI/Base/VariableUIElement";
|
||||
|
@ -17,6 +18,7 @@ import {SubstitutedTranslation} from "../../UI/SubstitutedTranslation";
|
|||
import SourceConfig from "./SourceConfig";
|
||||
import {TagsFilter} from "../../Logic/Tags/TagsFilter";
|
||||
import {Tag} from "../../Logic/Tags/Tag";
|
||||
import SubstitutingTag from "../../Logic/Tags/SubstitutingTag";
|
||||
|
||||
export default class LayerConfig {
|
||||
|
||||
|
@ -40,6 +42,7 @@ export default class LayerConfig {
|
|||
icon: TagRenderingConfig;
|
||||
iconOverlays: { if: TagsFilter, then: TagRenderingConfig, badge: boolean }[]
|
||||
iconSize: TagRenderingConfig;
|
||||
label: TagRenderingConfig;
|
||||
rotation: TagRenderingConfig;
|
||||
color: TagRenderingConfig;
|
||||
width: TagRenderingConfig;
|
||||
|
@ -212,6 +215,7 @@ export default class LayerConfig {
|
|||
}
|
||||
this.isShown = tr("isShown", "yes");
|
||||
this.iconSize = tr("iconSize", "40,40,center");
|
||||
this.label = tr("label", "")
|
||||
this.color = tr("color", "#0000ff");
|
||||
this.width = tr("width", "7");
|
||||
this.rotation = tr("rotation", "0");
|
||||
|
@ -307,7 +311,7 @@ export default class LayerConfig {
|
|||
|
||||
function render(tr: TagRenderingConfig, deflt?: string) {
|
||||
const str = (tr?.GetRenderValue(tags.data)?.txt ?? deflt);
|
||||
return SubstitutedTranslation.SubstituteKeys(str, tags.data);
|
||||
return SubstitutedTranslation.SubstituteKeys(str, tags.data).replace(/{.*}/g, "");
|
||||
}
|
||||
|
||||
const iconSize = render(this.iconSize, "40,40,center").split(",");
|
||||
|
@ -321,7 +325,7 @@ export default class LayerConfig {
|
|||
const weight = rendernum(this.width, 5);
|
||||
|
||||
const iconW = num(iconSize[0]);
|
||||
const iconH = num(iconSize[1]);
|
||||
let iconH = num(iconSize[1]);
|
||||
const mode = iconSize[2] ?? "center"
|
||||
|
||||
let anchorW = iconW / 2;
|
||||
|
@ -343,15 +347,15 @@ export default class LayerConfig {
|
|||
const iconUrlStatic = render(this.icon);
|
||||
const self = this;
|
||||
const mappedHtml = tags.map(tgs => {
|
||||
// What do you mean, 'tgs' is never read?
|
||||
// It is read implicitly in the 'render' method
|
||||
const iconUrl = render(self.icon);
|
||||
const rotation = render(self.rotation, "0deg");
|
||||
|
||||
let htmlParts: UIElement[] = [];
|
||||
let sourceParts = iconUrl.split(";");
|
||||
|
||||
function genHtmlFromString(sourcePart: string): UIElement {
|
||||
if (sourcePart.indexOf("html:") == 0) {
|
||||
// We use § as a replacement for ;
|
||||
const html = sourcePart.substring("html:".length)
|
||||
const inner = new FixedUiElement(SubstitutingTag.substituteString(html, tgs)).SetClass("block w-min text-center")
|
||||
const outer = new Combine([inner]).SetClass("flex flex-col items-center")
|
||||
return outer;
|
||||
}
|
||||
|
||||
const style = `width:100%;height:100%;transform: rotate( ${rotation} );display:block;position: absolute; top: 0; left: 0`;
|
||||
let html: UIElement = new FixedUiElement(`<img src="${sourcePart}" style="${style}" />`);
|
||||
const match = sourcePart.match(/([a-zA-Z0-9_]*):([^;]*)/)
|
||||
|
@ -365,11 +369,17 @@ export default class LayerConfig {
|
|||
}
|
||||
|
||||
|
||||
// What do you mean, 'tgs' is never read?
|
||||
// It is read implicitly in the 'render' method
|
||||
const iconUrl = render(self.icon);
|
||||
const rotation = render(self.rotation, "0deg");
|
||||
|
||||
let htmlParts: UIElement[] = [];
|
||||
let sourceParts = Utils.NoNull(iconUrl.split(";").filter(prt => prt != ""));
|
||||
for (const sourcePart of sourceParts) {
|
||||
htmlParts.push(genHtmlFromString(sourcePart))
|
||||
}
|
||||
|
||||
|
||||
let badges = [];
|
||||
for (const iconOverlay of self.iconOverlays) {
|
||||
if (!iconOverlay.if.matchesProperties(tgs)) {
|
||||
|
@ -377,7 +387,7 @@ export default class LayerConfig {
|
|||
}
|
||||
if (iconOverlay.badge) {
|
||||
const badgeParts: UIElement[] = [];
|
||||
const partDefs = iconOverlay.then.GetRenderValue(tgs).txt.split(";");
|
||||
const partDefs = iconOverlay.then.GetRenderValue(tgs).txt.split(";").filter(prt => prt != "");
|
||||
|
||||
for (const badgePartStr of partDefs) {
|
||||
badgeParts.push(genHtmlFromString(badgePartStr))
|
||||
|
@ -399,6 +409,16 @@ export default class LayerConfig {
|
|||
.SetStyle("display:flex;height:50%;width:100%;position:absolute;top:50%;left:50%;");
|
||||
htmlParts.push(badgesComponent)
|
||||
}
|
||||
|
||||
if(sourceParts.length ==0){iconH = 0}
|
||||
|
||||
const label = self.label.GetRenderValue(tgs)?.Subs(tgs)
|
||||
.SetClass("block w-min text-center")
|
||||
.SetStyle("margin-top: "+(iconH + 2) +"px")
|
||||
console.log("Generating label gave ", label, " source: ", self.label, "tags: ", tgs)
|
||||
if (label !== undefined) {
|
||||
htmlParts.push(new Combine([label]).SetClass("flex flex-col items-center"))
|
||||
}
|
||||
return new Combine(htmlParts).Render();
|
||||
})
|
||||
|
||||
|
@ -419,5 +439,23 @@ export default class LayerConfig {
|
|||
};
|
||||
}
|
||||
|
||||
public ExtractImages(): Set<string> {
|
||||
const parts: Set<string>[] = []
|
||||
parts.push(...this.tagRenderings?.map(tr => tr.ExtractImages(false)))
|
||||
parts.push(...this.titleIcons?.map(tr => tr.ExtractImages(true)))
|
||||
parts.push(this.icon?.ExtractImages(true))
|
||||
parts.push(...this.iconOverlays?.map(overlay => overlay.then.ExtractImages(true)))
|
||||
for (const preset of this.presets) {
|
||||
parts.push(new Set<string>(preset.description?.ExtractImages(false)))
|
||||
}
|
||||
|
||||
const allIcons = new Set<string>();
|
||||
for (const part of parts) {
|
||||
part?.forEach(allIcons.add, allIcons)
|
||||
}
|
||||
|
||||
return allIcons;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -105,6 +105,7 @@ export interface LayerConfigJson {
|
|||
* As a result, on could use a generic pin, then overlay it with a specific icon.
|
||||
* To make things even more practical, one can use all svgs from the folder "assets/svg" and _substitute the color_ in it.
|
||||
* E.g. to draw a red pin, use "pin:#f00", to have a green circle with your icon on top, use `circle:#0f0;<path to my icon.svg>`
|
||||
*
|
||||
*/
|
||||
icon?: string | TagRenderingConfigJson;
|
||||
|
||||
|
@ -128,6 +129,11 @@ export interface LayerConfigJson {
|
|||
* Usage: as if it were a css property for 'rotate', thus has to end with 'deg', e.g. `90deg`, `{direction}deg`, `calc(90deg - {camera:direction}deg)``
|
||||
*/
|
||||
rotation?: string | TagRenderingConfigJson;
|
||||
/**
|
||||
* A HTML-fragment that is shown at the center of the icon, for example:
|
||||
* <div style="background: white; display: block">{name}</div>
|
||||
*/
|
||||
label?: string | TagRenderingConfigJson ;
|
||||
|
||||
/**
|
||||
* The color for way-elements and SVG-elements.
|
||||
|
|
|
@ -9,6 +9,7 @@ import {Utils} from "../../Utils";
|
|||
export default class LayoutConfig {
|
||||
public readonly id: string;
|
||||
public readonly maintainer: string;
|
||||
public readonly credits?: string;
|
||||
public readonly changesetmessage?: string;
|
||||
public readonly version: string;
|
||||
public readonly language: string[];
|
||||
|
@ -48,6 +49,7 @@ export default class LayoutConfig {
|
|||
this.id = json.id;
|
||||
context = (context ?? "") + "." + this.id;
|
||||
this.maintainer = json.maintainer;
|
||||
this.credits = json.credits;
|
||||
this.changesetmessage = json.changesetmessage;
|
||||
this.version = json.version;
|
||||
this.language = [];
|
||||
|
@ -182,4 +184,14 @@ export default class LayoutConfig {
|
|||
custom.splice(0, 0, msg);
|
||||
return custom;
|
||||
}
|
||||
|
||||
public ExtractImages() : Set<string>{
|
||||
const icons = new Set<string>()
|
||||
for (const layer of this.layers) {
|
||||
layer.ExtractImages().forEach(icons.add, icons)
|
||||
}
|
||||
icons.add(this.icon)
|
||||
icons.add(this.socialImage)
|
||||
return icons
|
||||
}
|
||||
}
|
|
@ -24,6 +24,12 @@ export interface LayoutConfigJson {
|
|||
* 'cyclestreets' which become 'cyclestreets.html'
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* Who helped to create this theme and should be attributed?
|
||||
*/
|
||||
credits?: string;
|
||||
|
||||
/**
|
||||
* Who does maintian this preset?
|
||||
*/
|
||||
|
|
|
@ -90,6 +90,9 @@ export default class TagRenderingConfig {
|
|||
this.multiAnswer = json.multiAnswer ?? false
|
||||
if (json.mappings) {
|
||||
|
||||
if(!Array.isArray(json.mappings)){
|
||||
throw "Tagrendering has a 'mappings'-object, but expected a list ("+context+")"
|
||||
}
|
||||
|
||||
this.mappings = json.mappings.map((mapping, i) => {
|
||||
|
||||
|
@ -254,5 +257,16 @@ export default class TagRenderingConfig {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
public ExtractImages(isIcon: boolean): Set<string> {
|
||||
|
||||
const usedIcons = new Set<string>()
|
||||
this.render?.ExtractImages(isIcon)?.forEach(usedIcons.add, usedIcons)
|
||||
|
||||
for (const mapping of this.mappings ?? []) {
|
||||
mapping.then.ExtractImages(isIcon).forEach(usedIcons.add, usedIcons)
|
||||
}
|
||||
|
||||
return usedIcons;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,11 +1,15 @@
|
|||
import {AndOrTagConfigJson} from "./TagConfigJson";
|
||||
|
||||
/**
|
||||
* A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.
|
||||
* If the desired tags are missing and a question is defined, a question will be shown instead.
|
||||
*/
|
||||
export interface TagRenderingConfigJson {
|
||||
/**
|
||||
* Renders this value. Note that "{key}"-parts are substituted by the corresponding values of the element.
|
||||
* If neither 'textFieldQuestion' nor 'mappings' are defined, this text is simply shown as default value.
|
||||
*
|
||||
* Note that this is a HTML-interpreted value, so you can add links as e.g. <a href='{website}'>{website}</a>
|
||||
* Note that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`
|
||||
*/
|
||||
render?: string | any,
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ Adds the time that the data got loaded - pretty much the time of downloading fro
|
|||
Calculating tags with Javascript
|
||||
--------------------------------
|
||||
|
||||
In some cases, it is useful to have some tags calculated based on other properties. Some useful tags are available by default (e.g. **\_lat**, **lon**, **\_country**), as detailed above.
|
||||
In some cases, it is useful to have some tags calculated based on other properties. Some useful tags are available by default (e.g. **lat**, **lon**, **\_country**), as detailed above.
|
||||
|
||||
It is also possible to calculate your own tags - but this requires some javascript knowledge.
|
||||
|
||||
|
@ -46,7 +46,13 @@ Before proceeding, some warnings:
|
|||
|
||||
In the layer object, add a field **calculatedTags**, e.g.:
|
||||
|
||||
"calculatedTags": { "\_someKey": "javascript-expression", "name": "feat.properties.name ?? feat.properties.ref ?? feat.properties.operator", "\_distanceCloserThen3Km": "feat.distanceTo( some\_lon, some\_lat) < 3 ? 'yes' : 'no'" }
|
||||
"calculatedTags": \[ "\_someKey=javascript-expression", "name=feat.properties.name ?? feat.properties.ref ?? feat.properties.operator", "\_distanceCloserThen3Km=feat.distanceTo( some\_lon, some\_lat) < 3 ? 'yes' : 'no'" \]
|
||||
|
||||
The above code will be executed for every feature in the layer. The feature is accessible as **feat** and is an amended geojson object: - **area** contains the surface area (in square meters) of the object - **lat** and **lon** contain the latitude and longitude Some advanced functions are available on **feat** as well:
|
||||
|
||||
* distanceTo
|
||||
* overlapWith
|
||||
* closest
|
||||
|
||||
### distanceTo
|
||||
|
||||
|
@ -54,3 +60,15 @@ Calculates the distance between the feature and a specified point
|
|||
|
||||
* longitude
|
||||
* latitude
|
||||
|
||||
### overlapWith
|
||||
|
||||
Gives a list of features from the specified layer which this feature overlaps with, the amount of overlap in m². The returned value is **{ feat: GeoJSONFeature, overlap: number}**
|
||||
|
||||
* ...layerIds - one or more layer ids of the layer from which every feature is checked for overlap)
|
||||
|
||||
### closest
|
||||
|
||||
Given either a list of geojson features or a single layer name, gives the single object which is nearest to the feature. In the case of ways/polygons, only the centerpoint is considered.
|
||||
|
||||
* list of features
|
|
@ -50,17 +50,19 @@ The preferred way to add your theme is via a Pull Request. A Pull Request is les
|
|||
1) Fork this repository
|
||||
2) Go to `assets/themes` and create a new directory `yourtheme`
|
||||
3) Create a new file `yourtheme.json`, paste the theme configuration in there. You can find your theme configuration in the customThemeBuilder (the tab with the *Floppy disk* icon)
|
||||
4) Copy all the images into this new directory: external assets can suddenly break and leak privacy
|
||||
- Make sure the license is suitable, preferable a Creative Commons license. Attribution can be added at the bottom of this document
|
||||
4) Copy all the images into this new directory. **No external sources are allowed!** External image sources leak privacy or can break.
|
||||
- Make sure the license is suitable, preferable a Creative Commons license or CC0-license.
|
||||
- If an SVG version is available, use the SVG version
|
||||
- Make sure all the links in `yourtheme.json` are updated. You can use `./assets/themes/yourtheme/yourimage.svg` instead of the HTML link
|
||||
- Create a file `license_info.json` in the theme directory, which contains metadata on every artwork source
|
||||
5) Add your theme to the code base:
|
||||
- Open [AllKnownLayouts.ts](https://github.com/pietervdvn/MapComplete/blob/master/Customizations/AllKnownLayouts.ts)
|
||||
- Add an import statement, e.g. `import * as yourtheme from "../assets/themes/yourtheme/yourthemes.json";`
|
||||
- Add your theme to the `LayoutsList`, by adding a line `new LayoutConfig(yourtheme)`
|
||||
6) Test your theme: run the project as described [above](../README.md#Dev)
|
||||
7) Happy with your theme? Time to open a Pull Request!
|
||||
8) Thanks a lot for improving MapComplete!
|
||||
6) Add some finishing touches, such as a social image. See [this blog post](https://www.h3xed.com/web-and-internet/how-to-use-og-image-meta-tag-facebook-reddit) for some hints
|
||||
7) Test your theme: run the project as described [above](../README.md#Dev)
|
||||
8) Happy with your theme? Time to open a Pull Request!
|
||||
9) Thanks a lot for improving MapComplete!
|
||||
|
||||
|
||||
The .JSON-format
|
||||
|
@ -84,6 +86,17 @@ Every field is documented in the source code itself - you can find them here:
|
|||
|
||||
There are few tags available that are calculated for convenience - e.g. the country an object is located at. [An overview of all these metatags is available here](Docs/CalculatedTags.md)
|
||||
|
||||
Some hints
|
||||
------------
|
||||
|
||||
### Everything is HTML
|
||||
|
||||
All the texts are actually *HTML*-snippets, so you can use `<b>` to add bold, or `<img src=...>` to add images to mappings or tagrenderings.
|
||||
|
||||
Some remarks:
|
||||
|
||||
- links are disabled when answering a question (e.g. a link in a mapping) as it should trigger the answer - not trigger to open the link.
|
||||
- If you include images, e.g. to clarify a type, make sure these are _icons_ or _diagrams_ - not actual pictures! If users see a picture, they think it is a picture of _that actual object_, not a type to clarify the type. An icon is however perceived as something more abstract.
|
||||
|
||||
Some pitfalls
|
||||
---------------
|
||||
|
@ -128,4 +141,6 @@ For example, in the [cyclofix-theme](https://mapcomplete.osm.org/cyclofix), ther
|
|||
|
||||
If all the layers are deselected except the bike wash layer, a shop having this tag will still match and will still show up as shop.
|
||||
|
||||
### Not reading the .JSON-specs
|
||||
|
||||
There are a few advanced features to do fancy stuff available, which are documented only in the spec above - for example, reusing background images and substituting the colours or HTML-rendering. If you need advanced stuff, read it through!
|
||||
|
|
Before Width: | Height: | Size: 656 KiB After Width: | Height: | Size: 695 KiB |
Before Width: | Height: | Size: 285 KiB After Width: | Height: | Size: 290 KiB |
Before Width: | Height: | Size: 420 KiB After Width: | Height: | Size: 438 KiB |
Before Width: | Height: | Size: 210 KiB After Width: | Height: | Size: 210 KiB |
Before Width: | Height: | Size: 204 KiB After Width: | Height: | Size: 204 KiB |
Before Width: | Height: | Size: 230 KiB After Width: | Height: | Size: 238 KiB |
Before Width: | Height: | Size: 189 KiB After Width: | Height: | Size: 184 KiB |
Before Width: | Height: | Size: 265 KiB After Width: | Height: | Size: 280 KiB |
Before Width: | Height: | Size: 264 KiB After Width: | Height: | Size: 264 KiB |
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 174 KiB |
After Width: | Height: | Size: 122 KiB |
Before Width: | Height: | Size: 660 KiB After Width: | Height: | Size: 665 KiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 100 KiB |
After Width: | Height: | Size: 139 KiB |
Before Width: | Height: | Size: 200 KiB After Width: | Height: | Size: 210 KiB |
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 123 KiB |
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 135 KiB |
Before Width: | Height: | Size: 186 KiB After Width: | Height: | Size: 190 KiB |
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 180 KiB |
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 85 KiB |
Before Width: | Height: | Size: 222 KiB After Width: | Height: | Size: 250 KiB |
Before Width: | Height: | Size: 546 KiB After Width: | Height: | Size: 568 KiB |
Before Width: | Height: | Size: 356 KiB After Width: | Height: | Size: 360 KiB |
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 126 KiB |
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 120 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 117 KiB |
Before Width: | Height: | Size: 323 KiB After Width: | Height: | Size: 353 KiB |
Before Width: | Height: | Size: 349 KiB After Width: | Height: | Size: 356 KiB |
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 182 KiB |
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 223 KiB |
Before Width: | Height: | Size: 264 KiB After Width: | Height: | Size: 258 KiB |
Before Width: | Height: | Size: 261 KiB After Width: | Height: | Size: 261 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 65 KiB |
Before Width: | Height: | Size: 560 KiB After Width: | Height: | Size: 610 KiB |
Before Width: | Height: | Size: 742 KiB After Width: | Height: | Size: 795 KiB |
|
@ -3239,3 +3239,81 @@
|
|||
"2021-04-02", "Thierry1030", "nl", "cyclofix", "MapComplete 0.6.4a", 1, 2, "Adding data with #MapComplete for theme #cyclofix", "pietervdvn.github.io"
|
||||
"2021-04-02", "WandelenMetKinderen", "en", "campersite", "MapComplete 0.6.4b", 0, 5, "Adding data with #MapComplete for theme #campersite", "mapcomplete.osm.be"
|
||||
"2021-04-02", "WandelenMetKinderen", "nl", "playgrounds", "MapComplete 0.6.4b", 0, 2, "Adding data with #MapComplete for theme #playgrounds", "mapcomplete.osm.be"
|
||||
"2021-04-03", "Awo", "en", "trees", "MapComplete 0.6.4a", 0, 2, "Adding data with #MapComplete for theme #trees", "pietervdvn.github.io"
|
||||
"2021-04-03", "dkf2010", "en", "benches", "MapComplete 0.6.4b", 0, 6, "Adding data with #MapComplete for theme #benches", "mapcomplete.osm.be"
|
||||
"2021-04-03", "dkf2010", "en", "charging_stations", "MapComplete 0.6.4b", 0, 2, "Adding data with #MapComplete for theme #charging_stations", "mapcomplete.osm.be"
|
||||
"2021-04-03", "GOwin", "en", "HailHydrant", "MapComplete 0.6.4a", 0, 1, "Adding data with #MapComplete for theme #HailHydrant", "pietervdvn.github.io"
|
||||
"2021-04-03", "GOwin", "en", "HailHydrant", "MapComplete 0.6.4a", 0, 4, "Adding data with #MapComplete for theme #HailHydrant", "pietervdvn.github.io"
|
||||
"2021-04-03", "GOwin", "en", "HailHydrant", "MapComplete 0.6.4a", 1, 3, "Adding data with #MapComplete for theme #HailHydrant", "pietervdvn.github.io"
|
||||
"2021-04-03", "hke2912", "en", "benches", "MapComplete 0.6.4a", 4, 9, "Adding data with #MapComplete for theme #benches", "pietervdvn.github.io"
|
||||
"2021-04-03", "jhuanjho", "en", "drinking_water", "MapComplete 0.6.4b", 0, 2, "Adding data with #MapComplete for theme #drinking_water", "mapcomplete.osm.be"
|
||||
"2021-04-03", "Kinjkajh", "nl", "surveillance", "MapComplete 0.6.4a", 4, 17, "Adding data with #MapComplete for theme #surveillance", "pietervdvn.github.io"
|
||||
"2021-04-03", "Koen Rijnsent", "en", "personal", "MapComplete 0.6.4b", 8, 28, "Adding data with #MapComplete for theme #personal", "mapcomplete.osm.be"
|
||||
"2021-04-03", "MAGONA", "en", "trees", "MapComplete 0.6.4a", 0, 1, "Adding data with #MapComplete for theme #trees", "pietervdvn.github.io"
|
||||
"2021-04-03", "MAGONA", "en", "trees", "MapComplete 0.6.4a", 0, 1, "Adding data with #MapComplete for theme #trees", "pietervdvn.github.io"
|
||||
"2021-04-03", "Peter Elderson", "en", "stolpersteine", "MapComplete 0.6.4b", 2, 20, "Adding data with #MapComplete for theme #stolpersteine", "mapcomplete.osm.be"
|
||||
"2021-04-03", "Peter Elderson", "en", "stolpersteine", "MapComplete 0.6.4b", 37, 142, "Adding data with #MapComplete for theme #stolpersteine", "mapcomplete.osm.be"
|
||||
"2021-04-03", "Peter Elderson", "en", "stolpersteine", "MapComplete 0.6.4b", 6, 61, "Adding data with #MapComplete for theme #stolpersteine", "mapcomplete.osm.be"
|
||||
"2021-04-03", "Pieter Nuytinck", "en", "bookcases", "MapComplete 0.6.4a", 1, 5, "Adding data with #MapComplete for theme #bookcases", "pietervdvn.github.io"
|
||||
"2021-04-03", "WinstonSmith", "en", "drinking_water", "MapComplete 0.6.4b", 1, 3, "Adding data with #MapComplete for theme #drinking_water", "mapcomplete.osm.be"
|
||||
"2021-04-04", "dkf2010", "en", "benches", "MapComplete 0.6.4b", 0, 1, "Adding data with #MapComplete for theme #benches", "mapcomplete.osm.be"
|
||||
"2021-04-04", "Fauranië", "en", "benches", "MapComplete 0.6.4b", 2, 2, "Adding data with #MapComplete for theme #benches", "mapcomplete.osm.be"
|
||||
"2021-04-04", "Fauranië", "en", "ghostbikes", "MapComplete 0.6.4b", 1, 0, "Adding data with #MapComplete for theme #ghostbikes", "mapcomplete.osm.be"
|
||||
"2021-04-04", "GOwin", "en", "HailHydrant", "MapComplete 0.6.4a", 0, 2, "Adding data with #MapComplete for theme #HailHydrant", "pietervdvn.github.io"
|
||||
"2021-04-04", "JuanjoMC", "en", "cyclofix", "MapComplete 0.6.4b", 0, 4, "Adding data with #MapComplete for theme #cyclofix", "mapcomplete.osm.be"
|
||||
"2021-04-04", "M!dgard", "nl", "fire", "MapComplete 0.6.4b", 1, 2, "Adding data with #MapComplete for theme #fire", "mapcomplete.braindeaddev.com"
|
||||
"2021-04-04", "M!dgard", "nl", "fire", "MapComplete 0.6.4b", 2, 4, "Adding data with #MapComplete for theme #fire", "mapcomplete.braindeaddev.com"
|
||||
"2021-04-04", "Peter Elderson", "en", "wandelknooppunten", "MapComplete 0.6.4b", 0, 1, "Adding data with #MapComplete for theme #wandelknooppunten", "mapcomplete.osm.be"
|
||||
"2021-04-04", "Pieter Nuytinck", "en", "bookcases", "MapComplete 0.6.5", 1, 1, "Adding data with #MapComplete for theme #bookcases", "pietervdvn.github.io"
|
||||
"2021-04-04", "Pieter Vander Vennet", "nl", "fritures", "MapComplete 0.6.4b", 1, 2, "Adding data with #MapComplete for theme #fritures", "mapcomplete.osm.be"
|
||||
"2021-04-04", "Thierry1030", "nl", "cyclofix", "MapComplete 0.6.5", 1, 6, "Adding data with #MapComplete for theme #cyclofix", "pietervdvn.github.io"
|
||||
"2021-04-04", "WinstonSmith", "en", "drinking_water", "MapComplete 0.6.4b", 1, 1, "Adding data with #MapComplete for theme #drinking_water", "mapcomplete.osm.be"
|
||||
"2021-04-05", "antonbundle", "en", "bookcases", "MapComplete 0.6.4b", 1, 4, "Adding data with #MapComplete for theme #bookcases", "mapcomplete.osm.be"
|
||||
"2021-04-05", "Awo", "en", "artworks", "MapComplete 0.6.5c", 0, 2, "Adding data with #MapComplete for theme #artworks", "pietervdvn.github.io"
|
||||
"2021-04-05", "dentonny", "nl", "cyclofix", "MapComplete 0.6.5", 1, 2, "Adding data with #MapComplete for theme #cyclofix", "pietervdvn.github.io"
|
||||
"2021-04-05", "dmlu", "en", "https://raw.githubusercontent.com/osmbe/play/master/mapcomplete/urban_fossils/urban_fossils.json", "MapComplete 0.6.4c", 1, 1, "Adding data with #MapComplete for theme #https://raw.githubusercontent.com/osmbe/play/master/mapcomplete/urban_fossils/urban_fossils.json", "mapcomplete.osm.be"
|
||||
"2021-04-05", "Fauranië", "en", "benches", "MapComplete 0.6.4b", 3, 3, "Adding data with #MapComplete for theme #benches", "mapcomplete.osm.be"
|
||||
"2021-04-05", "Fauranië", "en", "charging_stations", "MapComplete 0.6.4b", 2, 2, "Adding data with #MapComplete for theme #charging_stations", "mapcomplete.osm.be"
|
||||
"2021-04-05", "Fauranië", "en", "cyclofix", "MapComplete 0.6.4b", 2, 2, "Adding data with #MapComplete for theme #cyclofix", "mapcomplete.osm.be"
|
||||
"2021-04-05", "Fauranië", "en", "playgrounds", "MapComplete 0.6.4b", 2, 2, "Adding data with #MapComplete for theme #playgrounds", "mapcomplete.osm.be"
|
||||
"2021-04-05", "Fauranië", "nl", "basketswings", "MapComplete 0.6.4b", 1, 0, "Adding data with #MapComplete for theme #basketswings", "mapcomplete.osm.be"
|
||||
"2021-04-05", "lunaticstraydog", "en", "climbing", "MapComplete 0.6.4b", 1, 9, "Adding data with #MapComplete for theme #climbing", "mapcomplete.osm.be"
|
||||
"2021-04-05", "M!dgard", "nl", "aed", "MapComplete 0.6.4b", 0, 3, "Adding data with #MapComplete for theme #aed", "mapcomplete.braindeaddev.com"
|
||||
"2021-04-05", "Michel Stuyts", "en", "benches", "MapComplete 0.6.4b", 0, 2, "Adding data with #MapComplete for theme #benches", "mapcomplete.osm.be"
|
||||
"2021-04-05", "Michel Stuyts", "en", "benches", "MapComplete 0.6.4b", 1, 2, "Adding data with #MapComplete for theme #benches", "mapcomplete.osm.be"
|
||||
"2021-04-05", "Peter Elderson", "en", "stolpersteine", "MapComplete 0.6.4b", 0, 4, "Adding data with #MapComplete for theme #stolpersteine", "mapcomplete.osm.be"
|
||||
"2021-04-05", "Pieter Vander Vennet", "nl", "grb", "MapComplete 0.6.4b", 0, 8, "Adding data with #MapComplete for theme #grb", "mapcomplete.osm.be"
|
||||
"2021-04-05", "SuSanne Grittner", "de", "ghostbikes", "MapComplete 0.6.5c", 0, 1, "Adding data with #MapComplete for theme #ghostbikes", "pietervdvn.github.io"
|
||||
"2021-04-05", "Technopolice_newBiE", "en", "surveillance", "MapComplete 0.6.4-unlocked", 92, 139, "Adding data with #MapComplete for theme #surveillance", "pietervdvn.github.io"
|
||||
"2021-04-05", "Thierry1030", "nl", "cyclofix", "MapComplete 0.6.5", 1, 2, "Adding data with #MapComplete for theme #cyclofix", "pietervdvn.github.io"
|
||||
"2021-04-05", "Tim Couwelier", "nl", "aed", "MapComplete 0.6.5", 1, 2, "Adding data with #MapComplete for theme #aed", "pietervdvn.github.io"
|
||||
"2021-04-05", "WinstonSmith", "en", "drinking_water", "MapComplete 0.6.4b", 0, 1, "Adding data with #MapComplete for theme #drinking_water", "mapcomplete.osm.be"
|
||||
"2021-04-06", "BS97n", "de", "cyclofix", "MapComplete 0.6.4d", 2, 2, "Adding data with #MapComplete for theme #cyclofix", "mapcomplete.osm.be"
|
||||
"2021-04-06", "Pieter Vander Vennet", "en", "https://raw.githubusercontent.com/osmbe/play/master/mapcomplete/urban_fossils/urban_fossils.json", "MapComplete 0.6.4c", 1, 1, "Adding data with #MapComplete for theme #https://raw.githubusercontent.com/osmbe/play/master/mapcomplete/urban_fossils/urban_fossils.json", "mapcomplete.osm.be"
|
||||
"2021-04-07", "bvrslypp", "en", "surveillance", "MapComplete 0.6.6a", 1, 4, "Adding data with #MapComplete for theme #surveillance", "mapcomplete.osm.be"
|
||||
"2021-04-07", "dkf2010", "en", "benches", "MapComplete 0.6.6a", 1, 2, "Adding data with #MapComplete for theme #benches", "mapcomplete.osm.be"
|
||||
"2021-04-07", "joost schouppe", "nl", "cyclofix", "MapComplete 0.6.6a", 0, 1, "Adding data with #MapComplete for theme #cyclofix", "mapcomplete.osm.be"
|
||||
"2021-04-07", "joost schouppe", "nl", "personal", "MapComplete 0.6.6a", 0, 1, "Adding data with #MapComplete for theme #personal", "mapcomplete.osm.be"
|
||||
"2021-04-07", "L'imaginaire", "en", "benches", "MapComplete 0.6.6a", 0, 2, "Adding data with #MapComplete for theme #benches", "mapcomplete.osm.be"
|
||||
"2021-04-07", "L'imaginaire", "en", "playgrounds", "MapComplete 0.6.6a", 0, 2, "Adding data with #MapComplete for theme #playgrounds", "mapcomplete.osm.be"
|
||||
"2021-04-07", "L'imaginaire", "nl", "cyclofix", "MapComplete 0.6.6a", 0, 2, "Adding data with #MapComplete for theme #cyclofix", "mapcomplete.osm.be"
|
||||
"2021-04-07", "Pieter Vander Vennet", "en", "artworks", "MapComplete 0.6.6a", 1, 1, "Adding data with #MapComplete for theme #artworks", "mapcomplete.osm.be"
|
||||
"2021-04-07", "Pieter Vander Vennet", "en", "benches", "MapComplete 0.6.6a", 1, 1, "Adding data with #MapComplete for theme #benches", "mapcomplete.osm.be"
|
||||
"2021-04-07", "Pieter Vander Vennet", "en", "cyclofix", "MapComplete 0.6.6a", 1, 2, "Adding data with #MapComplete for theme #cyclofix", "mapcomplete.osm.be"
|
||||
"2021-04-07", "ttt1234", "en", "surveillance", "MapComplete 0.6.6", 10, 31, "Adding data with #MapComplete for theme #surveillance", "pietervdvn.github.io"
|
||||
"2021-04-07", "ttt1234", "nl", "fritures", "MapComplete 0.6.6", 1, 1, "Adding data with #MapComplete for theme #fritures", "pietervdvn.github.io"
|
||||
"2021-04-07", "Wim L", "en", "bookcases", "MapComplete 0.6.6b", 1, 1, "Adding data with #MapComplete for theme #bookcases", "mapcomplete.osm.be"
|
||||
"2021-04-08", "boute002", "nl", "drinking_water", "MapComplete 0.6.6b", 1, 0, "Adding data with #MapComplete for theme #drinking_water", "mapcomplete.osm.be"
|
||||
"2021-04-08", "bvrslypp", "en", "surveillance", "MapComplete 0.6.6", 1, 1, "Adding data with #MapComplete for theme #surveillance", "pietervdvn.github.io"
|
||||
"2021-04-08", "bvrslypp", "en", "surveillance", "MapComplete 0.6.6", 2, 3, "Adding data with #MapComplete for theme #surveillance", "pietervdvn.github.io"
|
||||
"2021-04-08", "dkf2010", "en", "benches", "MapComplete 0.6.6b", 0, 2, "Adding data with #MapComplete for theme #benches", "mapcomplete.osm.be"
|
||||
"2021-04-08", "Edlocks", "en", "cyclofix", "MapComplete 0.6.6", 1, 9, "Adding data with #MapComplete for theme #cyclofix", "pietervdvn.github.io"
|
||||
"2021-04-08", "Erin76", "nl", "playgrounds", "MapComplete 0.6.4b", 0, 13, "Adding data with #MapComplete for theme #playgrounds", "mapcomplete.braindeaddev.com"
|
||||
"2021-04-08", "joost schouppe", "nl", "personal", "MapComplete 0.6.6b", 1, 2, "Adding data with #MapComplete for theme #personal", "mapcomplete.osm.be"
|
||||
"2021-04-08", "Koen Rijnsent", "en", "personal", "MapComplete 0.6.6b", 0, 4, "Adding data with #MapComplete for theme #personal", "mapcomplete.osm.be"
|
||||
"2021-04-08", "Koen Rijnsent", "en", "personal", "MapComplete 0.6.6b", 7, 12, "Adding data with #MapComplete for theme #personal", "mapcomplete.osm.be"
|
||||
"2021-04-08", "Pieter Vander Vennet", "nl", "nature", "MapComplete 0.6.6b", 1, 4, "Adding data with #MapComplete for theme #nature", "mapcomplete.osm.be"
|
||||
"2021-04-08", "Pieter Vander Vennet", "nl", "nature", "MapComplete 0.6.6b", 2, 5, "Adding data with #MapComplete for theme #nature", "mapcomplete.osm.be"
|
||||
"2021-04-08", "sjokomoeske", "en", "benches", "MapComplete 0.6.6b", 3, 4, "Adding data with #MapComplete for theme #benches", "mapcomplete.osm.be"
|
||||
"2021-04-08", "sjokomoeske", "en", "playgrounds", "MapComplete 0.6.6b", 1, 2, "Adding data with #MapComplete for theme #playgrounds", "mapcomplete.osm.be"
|
||||
"2021-04-08", "ttt1234", "en", "surveillance", "MapComplete 0.6.6", 3, 6, "Adding data with #MapComplete for theme #surveillance", "pietervdvn.github.io"
|
||||
|
|
|
108
Docs/URL_Parameters.md
Normal file
|
@ -0,0 +1,108 @@
|
|||
custom-css
|
||||
------------
|
||||
If specified, the custom css from the given link will be loaded additionaly
|
||||
|
||||
test
|
||||
------
|
||||
If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org
|
||||
The default value is _false_
|
||||
|
||||
layout
|
||||
--------
|
||||
The layout to load into MapComplete
|
||||
|
||||
userlayout
|
||||
------------
|
||||
|
||||
The default value is _false_
|
||||
|
||||
layer-control-toggle
|
||||
----------------------
|
||||
Whether or not the layer control is shown
|
||||
The default value is _false_
|
||||
|
||||
tab
|
||||
-----
|
||||
The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >50 changesets)
|
||||
The default value is _0_
|
||||
|
||||
z
|
||||
---
|
||||
The initial/current zoom level
|
||||
The default value is set by the loaded theme
|
||||
|
||||
lat
|
||||
-----
|
||||
The initial/current latitude
|
||||
The default value is set by the loaded theme
|
||||
|
||||
lon
|
||||
-----
|
||||
The initial/current longitude of the app
|
||||
The default value is set by the loaded theme
|
||||
|
||||
fs-userbadge
|
||||
--------------
|
||||
Disables/Enables the user information pill (userbadge) at the top left. Disabling this disables logging in and thus disables editing all together, effectively putting MapComplete into read-only mode.
|
||||
The default value is _true_
|
||||
|
||||
fs-search
|
||||
-----------
|
||||
Disables/Enables the search bar
|
||||
The default value is _true_
|
||||
|
||||
fs-layers
|
||||
-----------
|
||||
Disables/Enables the layer control
|
||||
The default value is _true_
|
||||
|
||||
fs-add-new
|
||||
------------
|
||||
Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place)
|
||||
The default value is _true_
|
||||
|
||||
fs-welcome-message
|
||||
--------------------
|
||||
Disables/enables the help menu or welcome message
|
||||
The default value is _true_
|
||||
|
||||
fs-iframe
|
||||
-----------
|
||||
Disables/Enables the iframe-popup
|
||||
The default value is _false_
|
||||
|
||||
fs-more-quests
|
||||
----------------
|
||||
Disables/Enables the 'More Quests'-tab in the welcome message
|
||||
The default value is _true_
|
||||
|
||||
fs-share-screen
|
||||
-----------------
|
||||
Disables/Enables the 'Share-screen'-tab in the welcome message
|
||||
The default value is _true_
|
||||
|
||||
fs-geolocation
|
||||
----------------
|
||||
Disables/Enables the geolocation button
|
||||
The default value is _true_
|
||||
|
||||
debug
|
||||
-------
|
||||
If true, shows some extra debugging help such as all the available tags on every object
|
||||
The default value is _false_
|
||||
|
||||
oauth_token
|
||||
-------------
|
||||
Used to complete the login
|
||||
No default value set
|
||||
|
||||
background
|
||||
------------
|
||||
The id of the background layer to start with
|
||||
The default value is set by the loaded theme
|
||||
|
||||
layer-<layer-id>
|
||||
-----------------
|
||||
Wether or not layer with _<layer-id>_ is shown
|
||||
The default value is _true_
|
||||
|
|
@ -36,6 +36,9 @@ import Translations from "./UI/i18n/Translations";
|
|||
import MapControlButton from "./UI/MapControlButton";
|
||||
import Combine from "./UI/Base/Combine";
|
||||
import SelectedFeatureHandler from "./Logic/Actors/SelectedFeatureHandler";
|
||||
import LZString from "lz-string";
|
||||
import {LayoutConfigJson} from "./Customizations/JSON/LayoutConfigJson";
|
||||
import AttributionPanel from "./UI/BigComponents/AttributionPanel";
|
||||
|
||||
export class InitUiElements {
|
||||
|
||||
|
@ -209,7 +212,17 @@ export class InitUiElements {
|
|||
hashFromLocalStorage.setData(hash);
|
||||
dedicatedHashFromLocalStorage.setData(hash);
|
||||
}
|
||||
const layoutToUse = new LayoutConfig(JSON.parse(atob(hash)), false);
|
||||
|
||||
let json = {}
|
||||
try{
|
||||
json = JSON.parse(atob(hash));
|
||||
} catch (e) {
|
||||
// We try to decode with lz-string
|
||||
json = JSON.parse( Utils.UnMinify(LZString.decompressFromBase64(hash)))
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const layoutToUse = new LayoutConfig(json, false);
|
||||
userLayoutParam.setData(layoutToUse.id);
|
||||
return layoutToUse;
|
||||
} catch (e) {
|
||||
|
@ -275,11 +288,7 @@ export class InitUiElements {
|
|||
const copyrightNotice =
|
||||
new ScrollableFullScreen(
|
||||
() => Translations.t.general.attribution.attributionTitle.Clone(),
|
||||
() => new Combine([
|
||||
Translations.t.general.attribution.attributionContent,
|
||||
"<br/>",
|
||||
new Attribution(undefined, undefined, State.state.layoutToUse, undefined)
|
||||
]),
|
||||
() => new AttributionPanel(State.state.layoutToUse),
|
||||
"copyright"
|
||||
)
|
||||
|
||||
|
@ -288,7 +297,7 @@ export class InitUiElements {
|
|||
copyrightNotice,
|
||||
new MapControlButton(Svg.osm_copyright_svg()),
|
||||
copyrightNotice.isShown
|
||||
).SetClass("p-0.5 md:hidden")
|
||||
).SetClass("p-0.5")
|
||||
|
||||
new Combine([copyrightButton, checkbox])
|
||||
.AttachTo("bottom-left");
|
||||
|
@ -416,11 +425,12 @@ export class InitUiElements {
|
|||
}
|
||||
|
||||
|
||||
const newPointDialogIsShown = new UIEventSource<boolean>(false);
|
||||
const addNewPoint = new ScrollableFullScreen(
|
||||
() => Translations.t.general.add.title.Clone(),
|
||||
() => new SimpleAddUI(),
|
||||
"new");
|
||||
|
||||
() => new SimpleAddUI(newPointDialogIsShown),
|
||||
"new",
|
||||
newPointDialogIsShown)
|
||||
addNewPoint.isShown.addCallback(isShown => {
|
||||
if (!isShown) {
|
||||
State.state.LastClickLocation.setData(undefined)
|
||||
|
@ -436,5 +446,6 @@ export class InitUiElements {
|
|||
);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -81,7 +81,7 @@ export class ImageSearcher extends UIEventSource<{ key: string, url: string }[]>
|
|||
let mapillary = tags.mapillary;
|
||||
const prefix = "https://www.mapillary.com/map/im/";
|
||||
|
||||
let regex = /https?:\/\/www.mapillary.com\/app\/.*&pKey=([^&]*)/
|
||||
let regex = /https?:\/\/www.mapillary.com\/app\/.*pKey=([^&]*).*/
|
||||
let match = mapillary.match(regex);
|
||||
if (match) {
|
||||
mapillary = match[1];
|
||||
|
|
|
@ -27,6 +27,10 @@ export default class SelectedFeatureHandler {
|
|||
featureSource.features.addCallback(_ => self.selectFeature());
|
||||
|
||||
selectedFeature.addCallback(feature => {
|
||||
if(feature === undefined){
|
||||
hash.setData("")
|
||||
}
|
||||
|
||||
const h = feature?.properties?.id;
|
||||
if(h !== undefined){
|
||||
hash.setData(h)
|
||||
|
|
|
@ -18,11 +18,11 @@ export default class SubstitutingTag implements TagsFilter {
|
|||
this._value = value;
|
||||
}
|
||||
|
||||
private static substituteString(template: string, dict: any): string {
|
||||
public static substituteString(template: string, dict: any): string {
|
||||
for (const k in dict) {
|
||||
template = template.replace(new RegExp("\\{" + k + "\\}", 'g'), dict[k])
|
||||
}
|
||||
return template;
|
||||
return template.replace(/{.*}/g, "");
|
||||
}
|
||||
|
||||
asHumanString(linkToWiki: boolean, shorten: boolean, properties) {
|
||||
|
|
|
@ -38,6 +38,10 @@ export class QueryParameters {
|
|||
QueryParameters.knownSources[key] = source;
|
||||
}
|
||||
}
|
||||
|
||||
window["mapcomplete_query_parameter_overview"] = () => {
|
||||
console.log(QueryParameters.GenerateQueryParameterDocs())
|
||||
}
|
||||
}
|
||||
|
||||
private static Serialize() {
|
||||
|
@ -84,7 +88,13 @@ export class QueryParameters {
|
|||
public static GenerateQueryParameterDocs(): string {
|
||||
const docs = [];
|
||||
for (const key in QueryParameters.documentation) {
|
||||
docs.push("**" + key + "**: " + QueryParameters.documentation[key] + " (default value: _" + QueryParameters.defaults[key] + "_)")
|
||||
docs.push([
|
||||
" "+key+" ",
|
||||
"-".repeat(key.length + 2),
|
||||
QueryParameters.documentation[key],
|
||||
QueryParameters.defaults[key] === undefined ? "No default value set" : `The default value is _${QueryParameters.defaults[key]}_`
|
||||
|
||||
].join("\n"))
|
||||
}
|
||||
return docs.join("\n\n");
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Utils } from "../Utils";
|
|||
|
||||
export default class Constants {
|
||||
|
||||
public static vNumber = "0.6.4b";
|
||||
public static vNumber = "0.6.8";
|
||||
|
||||
// The user journey states thresholds when a new feature gets unlocked
|
||||
public static userJourney = {
|
||||
|
|
6
Models/smallLicense.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
export default interface SmallLicense {
|
||||
path: string,
|
||||
authors: string[],
|
||||
license: string,
|
||||
sources: string[]
|
||||
}
|
89
README.md
|
@ -142,49 +142,9 @@ Whenever a change is made -even adding a single tag- the change is uploaded into
|
|||
|
||||
Note that changesets are closed automatically after one hour of inactivity, so we don't have to worry about closing them.
|
||||
|
||||
### Query parameters
|
||||
# Documentation
|
||||
|
||||
By adding extra query parameters, more options are available to influence:
|
||||
|
||||
**test**: If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org (default value: _false_)
|
||||
|
||||
**layout**: The layout to load into MapComplete (default value: _bookcases_)
|
||||
|
||||
**userlayout**: undefined (default value: _false_)
|
||||
|
||||
**layer-control-toggle**: Wether or not the layer control is shown (default value: _false_)
|
||||
|
||||
**tab**: The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >200 changesets) (default value: _0_)
|
||||
|
||||
**z**: The initial/current zoom level (default value: _1_)
|
||||
|
||||
**lat**: The initial/current latitude (default value: _0_)
|
||||
|
||||
**lon**: The initial/current longitude of the app (default value: _0_)
|
||||
|
||||
**fs-userbadge**: Disables/Enables the userbadge (and thus disables login capabilities) (default value: _true_)
|
||||
|
||||
**fs-search**: Disables/Enables the search bar (default value: _true_)
|
||||
|
||||
**fs-layers**: Disables/Enables the layer control (default value: _true_)
|
||||
|
||||
**fs-add-new**: Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place) (default value: _true_)
|
||||
|
||||
**fs-welcome-message**: undefined (default value: _true_)
|
||||
|
||||
**fs-iframe**: Disables/Enables the iframe-popup (default value: _false_)
|
||||
|
||||
**fs-more-quests**: Disables/Enables the 'More Quests'-tab in the welcome message (default value: _true_)
|
||||
|
||||
**fs-share-screen**: Disables/Enables the 'Share-screen'-tab in the welcome message (default value: _true_)
|
||||
|
||||
**fs-geolocation**: Disables/Enables the geolocation button (default value: _true_)
|
||||
|
||||
**oauth_token**: Used to complete the login (default value: _undefined_)
|
||||
|
||||
**background**: The id of the background layer to start with (default value: _undefined_)
|
||||
|
||||
**layer-bookcases**: Wehter or not layer bookcases is shown (default value: _true_) index.ts:104:8
|
||||
All documentation can be found in [here](Docs/)
|
||||
|
||||
# Privacy
|
||||
|
||||
|
@ -195,53 +155,12 @@ Geolocation is available on mobile only throught hte device's GPS location (so n
|
|||
TODO: erase cookies of third party websites and API's
|
||||
|
||||
|
||||
# Attributions
|
||||
# Attribution
|
||||
|
||||
Data from OpenStreetMap
|
||||
|
||||
Background layer selection: curated by https://github.com/osmlab/editor-layer-index
|
||||
|
||||
Images from Wikipedia/Wikimedia
|
||||
|
||||
https://commons.wikimedia.org/wiki/File:Camera_font_awesome.svg
|
||||
Camera Icon, Dave Gandy, CC-BY-SA 3.0
|
||||
|
||||
https://commons.wikimedia.org/wiki/File:OOjs_UI_indicator_search-rtl.svg
|
||||
Search Icon, MIT
|
||||
|
||||
https://commons.wikimedia.org/wiki/File:Trash_font_awesome.svg
|
||||
Trash icon by Dave Gandy, CC-BY-SA
|
||||
|
||||
https://commons.wikimedia.org/wiki/File:Home-icon.svg
|
||||
Home icon by Timothy Miller, CC-BY-SA 3.0
|
||||
|
||||
https://commons.wikimedia.org/wiki/File:Map_icons_by_Scott_de_Jonge_-_bicycle-store.svg
|
||||
Bicycle logo, Scott de Jonge
|
||||
|
||||
Nature Reserve icon via http://www.onlinewebfonts.com/icon/389579, CC BY 3.0 (@ Эдуард Черных)
|
||||
|
||||
Park icon via http://www.onlinewebfonts.com/icon/425974, CC BY 3.0 (@sterankofrank)
|
||||
|
||||
Forest icon via https://www.onlinewebfonts.com/icon/498112, CC BY
|
||||
|
||||
Statistics icon via https://www.onlinewebfonts.com/icon/197818
|
||||
|
||||
Chronometer (on monitoring_station.svg): ANTU chronometer
|
||||
https://commons.wikimedia.org/w/index.php?title=Antu_chronometer
|
||||
|
||||
Fries icon:
|
||||
https://www.flaticon.com/free-icon/french-fries_1144288
|
||||
|
||||
Shower icon (used in 'bike_cleaning.svg'):
|
||||
https://commons.wikimedia.org/wiki/File:Shower_symbol.svg
|
||||
|
||||
Bench icons from StreetComplete: https://github.com/westnordost/StreetComplete/tree/v25.0-beta1/res/graphics/quest%20icons, GPLv3.0
|
||||
Icons are attributed in various 'license_info.json'-files and can be found in the app.
|
||||
|
||||
|
||||
Urinal icon: https://thenounproject.com/term/urinal/1307984/
|
||||
|
||||
24/7 icon: https://www.vecteezy.com/vector-art/1394992-24-7-service-and-support-icon-set
|
||||
|
||||
Translation-icon: https://commons.wikimedia.org/wiki/File:OOjs_UI_icon_language-ltr.svg
|
||||
|
||||
PingPong-table icon: Font Awesome Free 5.2.0 by @fontawesome - https://fontawesome.com
|
7
Svg.ts
|
@ -10,6 +10,9 @@ import Loc from "../../Models/Loc";
|
|||
import LeafletMap from "../../Models/LeafletMap";
|
||||
import * as L from "leaflet"
|
||||
|
||||
/**
|
||||
* The bottom right attribution panel in the leaflet map
|
||||
*/
|
||||
export default class Attribution extends UIElement {
|
||||
|
||||
private readonly _location: UIEventSource<Loc>;
|
||||
|
|
69
UI/BigComponents/AttributionPanel.ts
Normal file
|
@ -0,0 +1,69 @@
|
|||
import {UIElement} from "../UIElement";
|
||||
import Combine from "../Base/Combine";
|
||||
import Translations from "../i18n/Translations";
|
||||
import Attribution from "./Attribution";
|
||||
import State from "../../State";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
|
||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
import * as licenses from "../../assets/generated/license_info.json"
|
||||
import SmallLicense from "../../Models/smallLicense";
|
||||
import {Icon} from "leaflet";
|
||||
import Img from "../Base/Img";
|
||||
import {Utils} from "../../Utils";
|
||||
|
||||
/**
|
||||
* The attribution panel shown on mobile
|
||||
*/
|
||||
export default class AttributionPanel extends Combine {
|
||||
|
||||
private static LicenseObject = AttributionPanel.GenerateLicenses();
|
||||
|
||||
constructor(layoutToUse: UIEventSource<LayoutConfig>) {
|
||||
super([
|
||||
Translations.t.general.attribution.attributionContent,
|
||||
|
||||
((layoutToUse.data.maintainer ?? "") == "") ? "" : Translations.t.general.attribution.themeBy.Subs({author: layoutToUse.data.maintainer}),
|
||||
layoutToUse.data.credits ,
|
||||
"<br/>",
|
||||
new Attribution(undefined, undefined, State.state.layoutToUse, undefined),
|
||||
"<br/>",
|
||||
Translations.t.general.attribution.iconAttribution.title.Clone().SetClass("font-bold pt-12 pb-3"),
|
||||
...Utils.NoNull(Array.from(layoutToUse.data.ExtractImages()))
|
||||
.map(AttributionPanel.IconAttribution)
|
||||
]);
|
||||
this.SetClass("flex flex-col")
|
||||
}
|
||||
|
||||
private static IconAttribution(iconPath: string) {
|
||||
if (iconPath.startsWith("http")) {
|
||||
iconPath = "." + new URL(iconPath).pathname;
|
||||
}
|
||||
|
||||
const license: SmallLicense = AttributionPanel.LicenseObject[iconPath]
|
||||
if (license == undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if(license.license.indexOf("trivial")>=0){
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return new Combine([
|
||||
`<img src='${iconPath}' style="width: 50px; height: 50px; margin-right: 0.5em;">`,
|
||||
new Combine([
|
||||
new FixedUiElement(license.authors.join("; ")).SetClass("font-bold"),
|
||||
new Combine([license.license, license.sources.length > 0 ? " - " : "",
|
||||
...license.sources.map(link => `<a href='${link}' target="_blank">${new URL(link).hostname}</a> `)]).SetClass("block")
|
||||
]).SetClass("flex flex-col")
|
||||
]).SetClass("flex")
|
||||
}
|
||||
|
||||
private static GenerateLicenses() {
|
||||
const allLicenses = {}
|
||||
for (const key in licenses) {
|
||||
const license: SmallLicense = licenses[key];
|
||||
allLicenses[license.path] = license
|
||||
}
|
||||
return allLicenses;
|
||||
}
|
||||
}
|
|
@ -85,16 +85,12 @@ export default class MoreScreen extends UIElement {
|
|||
|
||||
const linkButton: UIElement[] = []
|
||||
|
||||
for (const k in AllKnownLayouts.allSets) {
|
||||
const layout: LayoutConfig = AllKnownLayouts.allSets[k];
|
||||
if (k === personal.id) {
|
||||
for (const layout of AllKnownLayouts.layoutsList) {
|
||||
if (layout.id === personal.id) {
|
||||
if (State.state.osmConnection.userDetails.data.csCount < Constants.userJourney.personalLayoutUnlock) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (layout.id !== k) {
|
||||
continue; // This layout was added multiple time due to an uppercase
|
||||
}
|
||||
linkButton.push(this.createLinkButton(layout));
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ export default class SimpleAddUI extends UIElement {
|
|||
private readonly goToInboxButton: UIElement = new SubtleButton(Svg.envelope_ui(),
|
||||
Translations.t.general.goToInbox, {url: "https://www.openstreetmap.org/messages/inbox", newTab: false});
|
||||
|
||||
constructor() {
|
||||
constructor(isShown: UIEventSource<boolean>) {
|
||||
super(State.state.locationControl.map(loc => loc.zoom));
|
||||
const self = this;
|
||||
this.ListenTo(Locale.language);
|
||||
|
@ -64,6 +64,18 @@ export default class SimpleAddUI extends UIElement {
|
|||
State.state.layerControlIsOpened.setData(true);
|
||||
})
|
||||
|
||||
// IS shown is the state of the dialog - we reset the choice if the dialog dissappears
|
||||
isShown.addCallback(isShown =>
|
||||
{
|
||||
if(!isShown){
|
||||
self._confirmPreset.setData(undefined)
|
||||
}
|
||||
})
|
||||
// If the click location changes, we reset the dialog as well
|
||||
State.state.LastClickLocation.addCallback(() => {
|
||||
self._confirmPreset.setData(undefined)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
InnerRender(): string {
|
||||
|
@ -143,7 +155,9 @@ export default class SimpleAddUI extends UIElement {
|
|||
"<b>",
|
||||
Translations.t.general.add.confirmButton.Subs({category: preset.name}),
|
||||
"</b>"])).SetClass("break-words");
|
||||
confirmButton.onClick(this.CreatePoint(preset.tags));
|
||||
confirmButton.onClick(
|
||||
this.CreatePoint(preset.tags)
|
||||
);
|
||||
|
||||
if (!this._confirmPreset.data.layerToAddTo.isDisplayed.data) {
|
||||
return new Combine([
|
||||
|
@ -158,7 +172,7 @@ export default class SimpleAddUI extends UIElement {
|
|||
let tagInfo = "";
|
||||
const csCount = State.state.osmConnection.userDetails.data.csCount;
|
||||
if (csCount > Constants.userJourney.tagsVisibleAt) {
|
||||
tagInfo = this._confirmPreset.data.tags.map(t => t.asHumanString(csCount > Constants.userJourney.tagsVisibleAndWikiLinked, true, {})).join("&");
|
||||
tagInfo = this._confirmPreset.data.tags.map(t => t.asHumanString(csCount > Constants.userJourney.tagsVisibleAndWikiLinked, true)).join("&");
|
||||
tagInfo = `<br/>More information about the preset: ${tagInfo}`
|
||||
}
|
||||
|
||||
|
@ -186,7 +200,7 @@ export default class SimpleAddUI extends UIElement {
|
|||
const csCount = State.state.osmConnection.userDetails.data.csCount;
|
||||
let tagInfo = undefined;
|
||||
if (csCount > Constants.userJourney.tagsVisibleAt) {
|
||||
const presets = preset.tags.map(t => new Combine ([t.asHumanString(false, true, {}), " "]).SetClass("subtle break-words") )
|
||||
const presets = preset.tags.map(t => new Combine([t.asHumanString(false, true), " "]).SetClass("subtle break-words"))
|
||||
tagInfo = new Combine(presets)
|
||||
}
|
||||
const button: UIElement =
|
||||
|
@ -220,11 +234,17 @@ export default class SimpleAddUI extends UIElement {
|
|||
|
||||
private CreatePoint(tags: Tag[]) {
|
||||
return () => {
|
||||
console.log("Create Point Triggered")
|
||||
const loc = State.state.LastClickLocation.data;
|
||||
let feature = State.state.changes.createElement(tags, loc.lat, loc.lon);
|
||||
State.state.selectedElement.setData(feature);
|
||||
this._confirmPreset.setData(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
public OnClose(){
|
||||
console.log("On close triggered")
|
||||
this._confirmPreset.setData(undefined)
|
||||
}
|
||||
|
||||
}
|
|
@ -17,7 +17,8 @@ import {LocalStorageSource} from "../../Logic/Web/LocalStorageSource";
|
|||
import HelpText from "./HelpText";
|
||||
import Svg from "../../Svg";
|
||||
import Constants from "../../Models/Constants";
|
||||
|
||||
import LZString from "lz-string";
|
||||
import {Utils} from "../../Utils";
|
||||
|
||||
export default class CustomGeneratorPanel extends UIElement {
|
||||
private mainPanel: UIElement;
|
||||
|
@ -40,7 +41,7 @@ export default class CustomGeneratorPanel extends UIElement {
|
|||
|
||||
private InitMainPanel(layout: LayoutConfigJson, userDetails: UserDetails, connection: OsmConnection) {
|
||||
const es = new UIEventSource(layout);
|
||||
const encoded = es.map(config => btoa(JSON.stringify(config)));
|
||||
const encoded = es.map(config => LZString.compressToBase64(Utils.MinifyJSON(JSON.stringify(config, null, 0))));
|
||||
encoded.addCallback(encoded => LocalStorageSource.Get("last-custom-theme"))
|
||||
const liveUrl = encoded.map(encoded => `./index.html?userlayout=${es.data.id}#${encoded}`)
|
||||
const testUrl = encoded.map(encoded => `./index.html?test=true&userlayout=${es.data.id}#${encoded}`)
|
||||
|
|
|
@ -21,14 +21,13 @@ import Constants from "../../Models/Constants";
|
|||
|
||||
export default class TagRenderingPanel extends InputElement<TagRenderingConfigJson> {
|
||||
|
||||
public IsImage = false;
|
||||
public options: { title?: string; description?: string; disableQuestions?: boolean; isImage?: boolean; };
|
||||
public readonly validText: UIElement;
|
||||
IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
||||
private intro: UIElement;
|
||||
private settingsTable: UIElement;
|
||||
|
||||
public IsImage = false;
|
||||
private readonly _value: UIEventSource<TagRenderingConfigJson>;
|
||||
public options: { title?: string; description?: string; disableQuestions?: boolean; isImage?: boolean; };
|
||||
|
||||
public readonly validText : UIElement;
|
||||
|
||||
constructor(languages: UIEventSource<string[]>,
|
||||
currentlySelected: UIEventSource<SingleSetting<any>>,
|
||||
|
@ -63,8 +62,18 @@ export default class TagRenderingPanel extends InputElement<TagRenderingConfigJs
|
|||
}
|
||||
|
||||
this._value.addCallback(value => {
|
||||
let doPing = false;
|
||||
if (value?.freeform?.key == "") {
|
||||
value.freeform = undefined;
|
||||
doPing = true;
|
||||
}
|
||||
|
||||
if (value?.render == "") {
|
||||
value.render = undefined;
|
||||
doPing = true;
|
||||
}
|
||||
|
||||
if (doPing) {
|
||||
this._value.ping();
|
||||
}
|
||||
})
|
||||
|
@ -138,8 +147,6 @@ export default class TagRenderingPanel extends InputElement<TagRenderingConfigJs
|
|||
return this._value;
|
||||
}
|
||||
|
||||
IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
||||
|
||||
IsValid(t: TagRenderingConfigJson): boolean {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -34,13 +34,6 @@ export class SubstitutedTranslation extends UIElement {
|
|||
this.SetClass("w-full")
|
||||
}
|
||||
|
||||
private static GenerateMap(){
|
||||
return new Map<UIEventSource<any>, SubstitutedTranslation>()
|
||||
}
|
||||
private static GenerateSubCache(){
|
||||
return new Map<Translation, Map<UIEventSource<any>, SubstitutedTranslation>>();
|
||||
}
|
||||
|
||||
public static construct(
|
||||
translation: Translation,
|
||||
tags: UIEventSource<any>): SubstitutedTranslation {
|
||||
|
@ -59,12 +52,19 @@ export class SubstitutedTranslation extends UIElement {
|
|||
|
||||
public static SubstituteKeys(txt: string, tags: any) {
|
||||
for (const key in tags) {
|
||||
// Poor mans replace all
|
||||
txt = txt.split("{" + key + "}").join(tags[key]);
|
||||
txt = txt.replace(new RegExp("{" + key + "}", "g"), tags[key])
|
||||
}
|
||||
return txt;
|
||||
}
|
||||
|
||||
private static GenerateMap() {
|
||||
return new Map<UIEventSource<any>, SubstitutedTranslation>()
|
||||
}
|
||||
|
||||
private static GenerateSubCache() {
|
||||
return new Map<Translation, Map<UIEventSource<any>, SubstitutedTranslation>>();
|
||||
}
|
||||
|
||||
InnerRender(): string {
|
||||
if (this.content.length == 1) {
|
||||
return this.content[0].Render();
|
||||
|
@ -117,8 +117,8 @@ export class SubstitutedTranslation extends UIElement {
|
|||
}
|
||||
}
|
||||
|
||||
// IF we end up here, no changes have to be made
|
||||
return [new FixedUiElement(template)];
|
||||
// IF we end up here, no changes have to be made - except to remove any resting {}
|
||||
return [new FixedUiElement(template.replace(/{.*}/g, ""))];
|
||||
}
|
||||
|
||||
}
|
|
@ -8,6 +8,8 @@ export class Translation extends UIElement {
|
|||
public static forcedLanguage = undefined;
|
||||
|
||||
public readonly translations: object
|
||||
return
|
||||
allIcons;
|
||||
|
||||
constructor(translations: object, context?: string) {
|
||||
super(Locale.language)
|
||||
|
@ -17,6 +19,9 @@ export class Translation extends UIElement {
|
|||
let count = 0;
|
||||
for (const translationsKey in translations) {
|
||||
count++;
|
||||
if(typeof(translations[translationsKey]) != "string"){
|
||||
throw "Error in an object depicting a translation: a non-string object was found. ("+context+")\n You probably put some other section accidentally in the translation"
|
||||
}
|
||||
}
|
||||
this.translations = translations;
|
||||
if (count === 0) {
|
||||
|
@ -102,7 +107,6 @@ export class Translation extends UIElement {
|
|||
return new Translation(this.translations)
|
||||
}
|
||||
|
||||
|
||||
FirstSentence() {
|
||||
|
||||
const tr = {};
|
||||
|
@ -115,4 +119,38 @@ export class Translation extends UIElement {
|
|||
|
||||
return new Translation(tr);
|
||||
}
|
||||
|
||||
public ExtractImages(isIcon = false): string[] {
|
||||
const allIcons: string[] = []
|
||||
for (const key in this.translations) {
|
||||
const render = this.translations[key]
|
||||
|
||||
if (isIcon) {
|
||||
const icons = render.split(";").filter(part => part.match(/(\.svg|\.png|\.jpg)$/) != null)
|
||||
allIcons.push(...icons)
|
||||
} else if (!Utils.runningFromConsole) {
|
||||
// This might be a tagrendering containing some img as html
|
||||
const htmlElement = document.createElement("div")
|
||||
htmlElement.innerHTML = render
|
||||
const images = Array.from(htmlElement.getElementsByTagName("img")).map(img => img.src)
|
||||
allIcons.push(...images)
|
||||
} else {
|
||||
// We are running this in ts-node (~= nodejs), and can not access document
|
||||
// So, we fallback to simple regex
|
||||
try {
|
||||
const matches = render.match(/<img[^>]+>/g)
|
||||
if (matches != null) {
|
||||
const sources = matches.map(img => img.match(/src=("[^"]+"|'[^']+'|[^/ ]+)/))
|
||||
.filter(match => match != null)
|
||||
.map(match => match[1].trim().replace(/^['"]/, '').replace(/['"]$/, ''));
|
||||
allIcons.push(...sources)
|
||||
}
|
||||
}catch(e){
|
||||
console.error("Could not search for images: ", render, this.txt)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
return allIcons.filter(icon => icon != undefined)
|
||||
}
|
||||
}
|
38
Utils.ts
|
@ -10,6 +10,8 @@ export class Utils {
|
|||
public static runningFromConsole = false;
|
||||
|
||||
public static readonly assets_path = "./assets/svg/";
|
||||
private static knownKeys = ["addExtraTags", "and", "calculatedTags", "changesetmessage", "clustering", "color", "condition", "customCss", "dashArray", "defaultBackgroundId", "description", "descriptionTail", "doNotDownload", "enableAddNewPoints", "enableBackgroundLayerSelection", "enableGeolocation", "enableLayers", "enableMoreQuests", "enableSearch", "enableShareScreen", "enableUserBadge", "freeform", "hideFromOverview", "hideInAnswer", "icon", "iconOverlays", "iconSize", "id", "if", "ifnot", "isShown", "key", "language", "layers", "lockLocation", "maintainer", "mappings", "maxzoom", "maxZoom", "minNeededElements", "minzoom", "multiAnswer", "name", "or", "osmTags", "passAllFeatures", "presets", "question", "render", "roaming", "roamingRenderings", "rotation", "shortDescription", "socialImage", "source", "startLat", "startLon", "startZoom", "tagRenderings", "tags", "then", "title", "titleIcons", "type", "version", "wayHandling", "widenFactor", "width"]
|
||||
private static extraKeys = ["nl", "en", "fr", "de", "pt", "es", "name", "phone", "email", "amenity", "leisure", "highway", "building", "yes", "no", "true", "false"]
|
||||
|
||||
static EncodeXmlValue(str) {
|
||||
return str.replace(/&/g, '&')
|
||||
|
@ -202,6 +204,42 @@ export class Utils {
|
|||
return {x: Utils.lon2tile(lon, z), y: Utils.lat2tile(lat, z), z: z}
|
||||
}
|
||||
|
||||
public static MinifyJSON(stringified: string): string {
|
||||
stringified = stringified.replace(/\|/g, "||");
|
||||
|
||||
const keys = Utils.knownKeys.concat(Utils.extraKeys);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const knownKey = keys[i];
|
||||
let code = i;
|
||||
if (i >= 124) {
|
||||
code += 1; // Character 127 is our 'escape' character |
|
||||
}
|
||||
let replacement = "|" + String.fromCharCode(code)
|
||||
stringified = stringified.replace(new RegExp(`\"${knownKey}\":`, "g"), replacement);
|
||||
}
|
||||
|
||||
return stringified;
|
||||
}
|
||||
|
||||
public static UnMinify(minified: string): string {
|
||||
|
||||
const parts = minified.split("|");
|
||||
let result = parts.shift();
|
||||
const keys = Utils.knownKeys.concat(Utils.extraKeys);
|
||||
|
||||
for (const part of parts) {
|
||||
if (part == "") {
|
||||
// Empty string => this was a || originally
|
||||
result += "|"
|
||||
continue
|
||||
}
|
||||
const i = part.charCodeAt(0);
|
||||
result += "\"" + keys[i] + "\":" + part.substring(1)
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static tile2long(x, z) {
|
||||
return (x / Math.pow(2, z) * 360 - 180);
|
||||
}
|
||||
|
|
BIN
assets/SocialImage.png
Normal file
After Width: | Height: | Size: 1.6 MiB |
BIN
assets/generic_osm_background.png
Normal file
After Width: | Height: | Size: 2.2 MiB |
10
assets/layers/benches/license_info.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
[
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "picnic_table.svg",
|
||||
"license": "CC0",
|
||||
"sources": []
|
||||
}
|
||||
]
|
10
assets/layers/bicycle_library/license_info.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
[
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "bicycle_library.svg",
|
||||
"license": "CC0",
|
||||
"sources": []
|
||||
}
|
||||
]
|
18
assets/layers/bicycle_tube_vending_machine/license_info.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
[
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "pinIcon.svg",
|
||||
"license": "CC0",
|
||||
"sources": []
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "tube.svg",
|
||||
"license": "CC0",
|
||||
"sources": []
|
||||
}
|
||||
]
|
16
assets/layers/bike_cafe/license_info.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
[
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Fiers",
|
||||
"Thibault Declercq",
|
||||
"Pierre Barban",
|
||||
"Joost Schouppe",
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "bike_cafe.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/cyclofix"
|
||||
]
|
||||
}
|
||||
]
|
18
assets/layers/bike_cleaning/license_info.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
[
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "bike_cleaning.svg",
|
||||
"license": "CC0",
|
||||
"sources": []
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "bike_cleaning_icon.svg",
|
||||
"license": "CC0",
|
||||
"sources": []
|
||||
}
|
||||
]
|
12
assets/layers/bike_monitoring_station/license_info.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
[
|
||||
{
|
||||
"authors": [
|
||||
"Fabián Alexis"
|
||||
],
|
||||
"path": "monitoring_station.svg",
|
||||
"license": "CC-BY-SA 3.0",
|
||||
"sources": [
|
||||
"https://commons.wikimedia.org/wiki/File:Antu_chronometer-reset.svg"
|
||||
]
|
||||
}
|
||||
]
|
|
@ -293,7 +293,6 @@
|
|||
"question": {
|
||||
"en": "Does this bicycle parking have spots for cargo bikes?",
|
||||
"nl": "Heeft deze fietsparking plaats voor bakfietsen?",
|
||||
"fr": "TODO: fr",
|
||||
"gl": "Este aparcadoiro de bicicletas ten espazo para bicicletas de carga?",
|
||||
"de": "Gibt es auf diesem Fahrrad-Parkplatz Plätze für Lastenfahrräder?"
|
||||
},
|
||||
|
@ -303,7 +302,6 @@
|
|||
"then": {
|
||||
"en": "This parking has room for cargo bikes",
|
||||
"nl": "Deze parking heeft plaats voor bakfietsen",
|
||||
"fr": "TODO: fr",
|
||||
"gl": "Este aparcadoiro ten espazo para bicicletas de carga.",
|
||||
"de": "Dieser Parkplatz bietet Platz für Lastenfahrräder"
|
||||
}
|
||||
|
@ -313,7 +311,6 @@
|
|||
"then": {
|
||||
"en": "This parking has designated (official) spots for cargo bikes.",
|
||||
"nl": "Er zijn speciale plaatsen voorzien voor bakfietsen",
|
||||
"fr": "TODO: fr",
|
||||
"gl": "Este aparcadoiro ten espazos designados (oficiais) para bicicletas de carga.",
|
||||
"de": "Dieser Parkplatz verfügt über ausgewiesene (offizielle) Plätze für Lastenfahrräder."
|
||||
}
|
||||
|
@ -323,7 +320,6 @@
|
|||
"then": {
|
||||
"en": "You're not allowed to park cargo bikes",
|
||||
"nl": "Je mag hier geen bakfietsen parkeren",
|
||||
"fr": "TODO: fr",
|
||||
"gl": "Non está permitido aparcar bicicletas de carga",
|
||||
"de": "Es ist nicht erlaubt, Lastenfahrräder zu parken"
|
||||
}
|
||||
|
|
100
assets/layers/bike_parking/license_info.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
[
|
||||
{
|
||||
"authors": [
|
||||
"Gitte Vande Graveele"
|
||||
],
|
||||
"path": "bollard.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/walk-by-brussels"
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Gitte Vande Graveele"
|
||||
],
|
||||
"path": "handlebar_holder.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/walk-by-brussels"
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Fiers",
|
||||
"Thibault Declercq",
|
||||
"Pierre Barban",
|
||||
"Joost Schouppe",
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "parking.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/cyclofix"
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Fiers",
|
||||
"Thibault Declercq",
|
||||
"Pierre Barban",
|
||||
"Joost Schouppe",
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "parking_old.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/cyclofix"
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Gitte Vande Graveele"
|
||||
],
|
||||
"path": "rack.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/walk-by-brussels"
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Gitte Vande Graveele"
|
||||
],
|
||||
"path": "shed.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/walk-by-brussels"
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Gitte Vande Graveele"
|
||||
],
|
||||
"path": "staple.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/walk-by-brussels"
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Gitte Vande Graveele"
|
||||
],
|
||||
"path": "two_tier.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/walk-by-brussels"
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Gitte Vande Graveele"
|
||||
],
|
||||
"path": "wall_loops.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/walk-by-brussels"
|
||||
]
|
||||
}
|
||||
]
|
|
@ -533,8 +533,8 @@
|
|||
"service:bicycle:pump=yes"
|
||||
],
|
||||
"description": {
|
||||
"en": "A device to inflate your tires on a fixed location in the public space.<h3>Examples of bicycle pumps</h3><img src='./assets/layers/bike_repair_station/pump_example_manual.jpg' height='200'/><img src='./assets/layers/bike_repair_station/pump_example.png' height='200'/><img src='./assets/layers/bike_repair_station/pump_example_round.jpg' height='200'/>",
|
||||
"nl": "Een apparaat waar je je fietsbanden kan oppompen, beschikbaar in de publieke ruimte. De fietspomp in je kelder telt dus niet.<h3>Voorbeelden</h3><img src='./assets/layers/bike_repair_station/pump_example_manual.jpg' height='200'/><img src='./assets/layers/bike_repair_station/pump_example.png' height='200'/><img src='./assets/layers/bike_repair_station/pump_example_round.jpg' height='200'/>"
|
||||
"en": "A device to inflate your tires on a fixed location in the public space.<h3>Examples of bicycle pumps</h3><div style='width: 100%; display: flex; align-items: stretch;'><img src='./assets/layers/bike_repair_station/pump_example_manual.jpg' style='height: 200px; width: auto;'/><img src='./assets/layers/bike_repair_station/pump_example.png' style='height: 200px; width: auto;'/><img src='./assets/layers/bike_repair_station/pump_example_round.jpg' style='height: 200px; width: auto;'/></div>",
|
||||
"nl": "Een apparaat waar je je fietsbanden kan oppompen, beschikbaar in de publieke ruimte. De fietspomp in je kelder telt dus niet.<h3>Voorbeelden</h3><h3>Examples of bicycle pumps</h3><div style='width: 100%; display: flex; align-items: stretch;'><img src='./assets/layers/bike_repair_station/pump_example_manual.jpg' style='height: 200px; width: auto;'/><img src='./assets/layers/bike_repair_station/pump_example.png' style='height: 200px; width: auto;'/><img src='./assets/layers/bike_repair_station/pump_example_round.jpg' style='height: 200px; width: auto;'/></div>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
126
assets/layers/bike_repair_station/license_info.json
Normal file
|
@ -0,0 +1,126 @@
|
|||
[
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Fiers",
|
||||
"Thibault Declercq",
|
||||
"Pierre Barban",
|
||||
"Joost Schouppe",
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "bike_pump.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/cyclofix"
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Fiers",
|
||||
"Thibault Declercq",
|
||||
"Pierre Barban",
|
||||
"Joost Schouppe",
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "broken_pump.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/cyclofix"
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Fiers",
|
||||
"Thibault Declercq",
|
||||
"Pierre Barban",
|
||||
"Joost Schouppe",
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "broken_pump_2.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/cyclofix"
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Fiers",
|
||||
"Thibault Declercq",
|
||||
"Pierre Barban",
|
||||
"Joost Schouppe",
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "pump.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/cyclofix"
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Turvec Solutions"
|
||||
],
|
||||
"path": "pump_example.png",
|
||||
"license": "Used with permission; all rights reserved",
|
||||
"note": "Used with permission after email conversation, can be assumed to be CC-BY",
|
||||
"sources": [
|
||||
"https://turvec.com/product/public-bike-pump/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "pump_example_manual.jpg",
|
||||
"license": "CC0",
|
||||
"sources": []
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"©Altinnova"
|
||||
],
|
||||
"path": "pump_example_round.jpg",
|
||||
"license": "Used with permission; all rights reserved",
|
||||
"sources": [
|
||||
"https://www.altinnova.com",
|
||||
"https://www.teeken.de/produkte/stadtmobiliar/green-air1/12?lang=3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Fiers",
|
||||
"Thibault Declercq",
|
||||
"Pierre Barban",
|
||||
"Joost Schouppe",
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "repair_station.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/cyclofix"
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Polarbear24"
|
||||
],
|
||||
"path": "repair_station_example.jpg",
|
||||
"license": "CC-BY-SA 4.0",
|
||||
"sources": [
|
||||
"https://wiki.openstreetmap.org/wiki/File:Public_Bike_Repair_Station.jpg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Fiers",
|
||||
"Thibault Declercq",
|
||||
"Pierre Barban",
|
||||
"Joost Schouppe",
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "repair_station_pump.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/cyclofix"
|
||||
]
|
||||
}
|
||||
]
|
|
@ -58,8 +58,7 @@
|
|||
{
|
||||
"if": {
|
||||
"and": [
|
||||
"shop=sports",
|
||||
"name~*"
|
||||
"shop=sports"
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
|
@ -68,18 +67,10 @@
|
|||
"fr": "Magasin de sport <i>{name}</i>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "shop=sports",
|
||||
"then": {
|
||||
"en": "Sport gear shop",
|
||||
"nl": "Sportwinkel",
|
||||
"fr": "Magasin de sport"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"and": [
|
||||
"shop!~bicycle",
|
||||
"shop!~.*bicycle.*",
|
||||
"shop~*"
|
||||
]
|
||||
},
|
||||
|
@ -88,9 +79,24 @@
|
|||
{
|
||||
"if": {
|
||||
"and": [
|
||||
"name~*",
|
||||
{
|
||||
"or": [
|
||||
"service:bicycle:rental=yes",
|
||||
"amenity=bicycle_rental"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"nl": "Fietsverhuur <i>{name}</i>",
|
||||
"en": "Bicycle rental <i>{name}</i>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"and": [
|
||||
"service:bicycle:retail!~yes",
|
||||
"service:bicycle:repair!~no"
|
||||
"service:bicycle:repair=yes"
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
|
@ -104,22 +110,6 @@
|
|||
{
|
||||
"if": {
|
||||
"and": [
|
||||
"service:bicycle:retail!~yes",
|
||||
"service:bicycle:repair!~no"
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"en": "Bike repair",
|
||||
"nl": "Fietsenmaker",
|
||||
"fr": "Réparateur de vélo",
|
||||
"gl": "Arranxo de bicicletas",
|
||||
"de": "Fahrradwerkstatt"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"and": [
|
||||
"name~*",
|
||||
"service:bicycle:repair!~yes"
|
||||
]
|
||||
},
|
||||
|
@ -131,33 +121,6 @@
|
|||
"de": "Fahrradgeschäft <i>{name}</i>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "service:bicycle:repair!~yes",
|
||||
"then": {
|
||||
"en": "Bike shop",
|
||||
"nl": "Fietswinkel",
|
||||
"fr": "Magasin de vélo",
|
||||
"gl": "Tenda de bicicletas",
|
||||
"de": "Fahrradgeschäft"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"and": [
|
||||
"name~*",
|
||||
{
|
||||
"or": [
|
||||
"service:bicycle:rental=yes",
|
||||
"amenity=bicycle_rental"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"nl": "Fietsverhuur <i>{name}</i>",
|
||||
"en": "Bicycle rental <i>{name}</i>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "name~*",
|
||||
"then": {
|
||||
|
|
58
assets/layers/bike_shop/license_info.json
Normal file
|
@ -0,0 +1,58 @@
|
|||
[
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Fiers",
|
||||
"Thibault Declercq",
|
||||
"Pierre Barban",
|
||||
"Joost Schouppe",
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "pump.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/cyclofix"
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Fiers",
|
||||
"Thibault Declercq",
|
||||
"Pierre Barban",
|
||||
"Joost Schouppe",
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "repair_shop.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/cyclofix"
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Fiers",
|
||||
"Thibault Declercq",
|
||||
"Pierre Barban",
|
||||
"Joost Schouppe",
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "shop.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/cyclofix"
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Fiers",
|
||||
"Thibault Declercq",
|
||||
"Pierre Barban",
|
||||
"Joost Schouppe",
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "tools.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/cyclofix"
|
||||
]
|
||||
}
|
||||
]
|
22
assets/layers/bird_hide/license_info.json
Normal file
|
@ -0,0 +1,22 @@
|
|||
[
|
||||
{
|
||||
"authors": [
|
||||
"Font Awesome"
|
||||
],
|
||||
"path": "birdhide.svg",
|
||||
"license": "CC-BY 4.0",
|
||||
"sources": [
|
||||
"https://fontawesome.com"
|
||||
]
|
||||
},
|
||||
{
|
||||
"authors": [
|
||||
"Font Awesome Free 5.2.0 by @fontawesome"
|
||||
],
|
||||
"path": "birdshelter.svg",
|
||||
"license": "CC-BY-SA 4.0",
|
||||
"sources": [
|
||||
"https://fontawesome.com\r"
|
||||
]
|
||||
}
|
||||
]
|
10
assets/layers/cycling_themed_object/license_info.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
[
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "other_services.svg",
|
||||
"license": "CC0",
|
||||
"sources": []
|
||||
}
|
||||
]
|
|
@ -82,21 +82,21 @@
|
|||
"if": "operational_status=",
|
||||
"then": {
|
||||
"en": "This drinking water works",
|
||||
"nl": "Deze drinkwaterfonteint werkt"
|
||||
"nl": "Deze drinkwaterfontein werkt"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "operational_status=broken",
|
||||
"then": {
|
||||
"en": "This drinking water is broken",
|
||||
"nl": "Deze drinkwaterfonteint is kapot"
|
||||
"nl": "Deze drinkwaterfontein is kapot"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "operational_status=closed",
|
||||
"then": {
|
||||
"en": "This drinking water is closed",
|
||||
"nl": "Deze drinkwaterfonteint is afgesloten"
|
||||
"nl": "Deze drinkwaterfontein is afgesloten"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
16
assets/layers/drinking_water/license_info.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
[
|
||||
{
|
||||
"authors": [
|
||||
"Pieter Fiers",
|
||||
"Thibault Declercq",
|
||||
"Pierre Barban",
|
||||
"Joost Schouppe",
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"path": "drips.svg",
|
||||
"license": "CC-BY-SA",
|
||||
"sources": [
|
||||
"https://osoc.be/editions/2020/cyclofix"
|
||||
]
|
||||
}
|
||||
]
|