diff --git a/.github/workflows/pull_request_check.yml b/.github/workflows/pull_request_check.yml new file mode 100644 index 000000000..c40d52b8a --- /dev/null +++ b/.github/workflows/pull_request_check.yml @@ -0,0 +1,31 @@ +name: Pull request check +on: + pull_request: + 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; creates missing_licenses.txt if any missing + run: npm run validate:licenses + + - name: Compile and validate themes and layers; creates layer_report.txt if any errors are found + run: npm run validate:layeroverview diff --git a/.github/workflows/theme_validation.yml b/.github/workflows/theme_validation.yml new file mode 100644 index 000000000..944ef73ba --- /dev/null +++ b/.github/workflows/theme_validation.yml @@ -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 generate:licenses + + - name: Compile themes and layers + run: npm run generate:layeroverview diff --git a/AllTranslationAssets.ts b/AllTranslationAssets.ts index 6552d52a3..abd1d07c6 100644 --- a/AllTranslationAssets.ts +++ b/AllTranslationAssets.ts @@ -67,7 +67,10 @@ export default class AllTranslationAssets { }, openStreetMapIntro: new Translation( {"en":"

An Open Map

Wouldn't it be cool if there was a single map, which everyone could freely use and edit? A single place to store all geo-information? Then, all those websites with different, small and incompatible maps (which are always outdated) wouldn't be needed anymore.

OpenStreetMap is this map. The map data can be used for free (with attribution and publication of changes to that data). On top of that, everyone can freely add new data and fix errors. This website uses OpenStreetMap as well. All the data is from there, and your answers and corrections are added there as well.

A ton of people and application already use OpenStreetMap: Maps.me, OsmAnd, but also the maps at Facebook, Instagram, Apple-maps and Bing-maps are (partly) powered by OpenStreetMap.If you change something here, it'll be reflected in those applications too - after their next update!

","es":"

Un mapa abierto

¿No sería genial si hubiera un solo mapa, que todos pudieran usar y editar libremente?¿Un solo lugar para almacenar toda la información geográfica? Entonces, todos esos sitios web con mapas diferentes, pequeños e incompatibles (que siempre están desactualizados) ya no serían necesarios.

OpenStreetMap es ese mapa. Los datos del mapa se pueden utilizar de forma gratuita (con atribución y publicación de cambios en esos datos).Además de eso, todos pueden agregar libremente nuevos datos y corregir errores. Este sitio web también usa OpenStreetMap. Todos los datos provienen de allí, y tus respuestas y correcciones también se añadirán allí.

Muchas personas y aplicaciones ya usan OpenStreetMap: Maps.me, OsmAnd, pero también los mapas de Facebook, Instagram, Apple y Bing son (en parte) impulsados ​​por OpenStreetMap .Si cambias algo aquí, también se reflejará en esas aplicaciones, en su próxima actualización

","ca":"

Un mapa obert

No seria genial si hagués un únic mapa, que tothom pogués utilitzar i editar lliurement?Un sol lloc on emmagatzemar tota la informació geogràfica? Llavors tots aquests llocs web amb mapes diferents petits i incompatibles (que sempre estaran desactulitzats) ja no serien necessaris.

OpenStreetMap és aquest mapa. Les dades del mapa es poden utilitzar de franc (amb atribució i publicació de canvis en aquestes dades).A més a més, tothom pot agregar lliurement noves dades i corregir errors. De fet, aquest lloc web també fa servir OpenStreetMap. Totes les dades provenen d'allà i les teves respostes i correccions també s'afegiran allà.

Moltes persones i aplicacions ja utilitzen OpenStreetMap: Maps.me, OsmAnd, però també els mapes de Facebook, Instagram, Apple i Bing són (en part) impulsats ​​per OpenStreetMap.Si canvies alguna cosa aquí també es reflectirà en aquestes aplicacions en la seva propera actualització.

","nl":"

Een open kaart

Zou het niet fantastisch zijn als er een open kaart zou zijn die door iedereen aangepast én gebruikt kan worden? Een kaart waar iedereen zijn interesses aan zou kunnen toevoegen? Dan zouden er geen duizend-en-één verschillende kleine kaartjes, websites, ... meer nodig zijn

OpenStreetMap is deze open kaart. Je mag de kaartdata gratis gebruiken (mits bronvermelding en herpublicatie van aanpassingen). Daarenboven mag je de kaart ook gratis aanpassen als je een account maakt. Ook deze website is gebaseerd op OpenStreetMap. Als je hier een vraag beantwoord, gaat het antwoord daar ook naartoe

Tenslotte zijn er reeds vele gebruikers van OpenStreetMap. Denk maar Maps.me, OsmAnd, verschillende gespecialiseerde routeplanners, de achtergrondkaarten op Facebook, Instagram,...
Zelfs Apple Maps en Bing-Maps gebruiken OpenStreetMap in hun kaarten!

Kortom, als je hier een punt toevoegd of een vraag beantwoord, zal dat na een tijdje ook in al dié applicaties te zien zijn.

","fr":"

Une carte ouverte

Ne serait-il pas génial d'avoir sur une carte que tout le monde pourrait éditer ouvertement? Une seule et unique plateforme regroupant toutes les informations geographiques? Ainsi nous n'aurons plus besoin de toutes ces cartes petites et incompatibles cartes (souvent non mises à jour).

OpenStreetMap est la carte qu'il vous faut!. Toutes les données de cette carte peuvent être utilisé gratuitement (avec d'attribution et de publication des changements de données). De plus tout le monde est libre d'ajouter de nouvelles données et de corriger les erreurs. Ce site internet utilise également OpenStreetMap. Toutes les données en proviennent et tous les ajouts et modifications y seront également ajoutés.

De nombreux individus et d'applications utilisent déjà OpenStreetMap: Maps.me, OsmAnd, mais aussi les cartes de Facebook, Instagram, Apple-maps et Bing-maps sont (en partie) supporté par OpenStreetMap. Si vous modifié quelque chose ici, ces changements seront incorporer dans ces applications dès leurs mises à jour!

","gl":"

Un mapa aberto

Non sería xenial se houbera un só mapa, que todos puideran empregar e editar de xeito libre?Un só lugar para almacenar toda a información xeográfica? Entón, todos eses sitios web con mapas diferentes, pequenos e incompatíbeis (que sempre están desactualizados) xa non serían necesarios.

OpenStreetMap é ese mapa. Os datos do mapa pódense empregar de balde (con atribución e publicación de modificacións neses datos).Ademais diso, todos poden engadir de xeito ceibe novos datos e corrixir erros. Este sitio web tamén emprega o OpenStreetMap. Todos os datos proveñen de alí, e as túas respostas e correccións tamén serán engadidas alí.

Moitas persoas e aplicacións xa empregan o OpenStreetMap: Maps.me, OsmAnd, pero tamén os mapas do Facebook, Instagram, Apple e Bing son (en parte) impulsados ​​polo OpenStreetMap.Se mudas algo aquí, tamén será reflexado nesas aplicacións, na súa seguinte actualización!

","de":"

Eine offene Karte

Wäre es nicht toll, wenn es eine offene Karte gäbe, die von jedem angepasst und benutzt werden könnte? Eine Karte, zu der jeder seine Interessen hinzufügen kann? Dann bräuchte man all diese Websites mit unterschiedlichen, kleinen und inkompatiblen Karten (die immer veraltet sind) nicht mehr.

OpenStreetMap ist diese offene Karte. Die Kartendaten können kostenlos verwendet werden (mit Attribution und Veröffentlichung von Änderungen an diesen Daten). Darüber hinaus können Sie die Karte kostenlos ändern und Fehler beheben, wenn Sie ein Konto erstellen. Diese Website basiert ebenfalls auf OpenStreetMap. Wenn Sie eine Frage hier beantworten, geht die Antwort auch dorthin.

Viele Menschen und Anwendungen nutzen OpenStreetMap bereits: Maps.me, OsmAnd, verschiedene spezialisierte Routenplaner, die Hintergrundkarten auf Facebook, Instagram,...
Sogar Apple Maps und Bing Maps verwenden OpenStreetMap in ihren Karten!

Wenn Sie hier einen Punkt hinzufügen oder eine Frage beantworten, wird er nach einer Weile in all diesen Anwendungen sichtbar sein.

"} ), attribution: { attributionTitle: new Translation( {"en":"Attribution notice","nl":"Met dank aan"} ), - attributionContent: new Translation( {"en":"

All data is provided by OpenStreetMap, freely reusable under the Open DataBase License.

Some images are provided by Wikimedia

"} ), + attributionContent: new Translation( {"en":"

All data is provided by OpenStreetMap, freely reusable under the Open DataBase License.

"} ), + themeBy: new Translation( {"en":"Theme maintained by {author}"} ), + iconAttribution: { title: new Translation( {"en":"Used icons"} ), +}, }, sharescreen: { intro: new Translation( {"en":"

Share this map

Share this map by copying the link below and sending it to friends and family:","ca":"

Comparteix aquest mapa

Comparteix aquest mapa copiant l'enllaç de sota i enviant-lo a amics i família:","es":"

Comparte este mapa

Comparte este mapa copiando el enlace de debajo y enviándolo a amigos y familia:","fr":"

Partager cette carte

Partagez cette carte en copiant le lien suivant et envoyez-le à vos amis:","nl":"

Deel deze kaart

Kopieer onderstaande link om deze kaart naar vrienden en familie door te sturen:","gl":"

Comparte este mapa

Comparte este mapa copiando a ligazón de embaixo e enviándoa ás amizades e familia:","de":"

Diese Karte teilen

Sie können diese Karte teilen, indem Sie den untenstehenden Link kopieren und an Freunde und Familie schick"} ), addToHomeScreen: new Translation( {"en":"

Add to your home screen

You can easily add this website to your smartphone home screen for a native feel. Click the 'add to home screen button' in the URL bar to do this.","ca":"

Afegir-lo a la pantalla d'inici

Pots afegir aquesta web a la pantalla d'inici del teu smartphone per a que es vegi més nadiu. Apreta al botó 'afegir a l'inici' a la barra d'adreces URL per fer-ho.","es":"

Añadir a la pantalla de inicio

Puedes añadir esta web en la pantalla de inicio de tu smartphone para que se vea más nativo. Aprieta el botón 'añadir a inicio' en la barra de direcciones URL para hacerlo.","fr":"

Ajouter à votre page d'accueil

Vous pouvez facilement ajouter la carte à votre écran d'accueil de téléphone. Cliquer sur le boutton 'ajouter à l'ecran d'accueil' dans la barre d'adresse pour éffectuer cette tâche.","gl":"

Engadir á pantalla de inicio

Podes engadir esta web na pantalla de inicio do teu smartphone para que se vexa máis nativo. Preme o botón 'engadir ó inicio' na barra de enderezos URL para facelo.","nl":"

Voeg toe aan je thuis-scherm

Je kan deze website aan je thuisscherm van je smartphone toevoegen voor een native feel","de":"

Zum Startbildschirm hinzufügen

Sie können diese Website einfach zum Startbildschirm Ihres Smartphones hinzufügen, um ein natives Gefühl zu erhalten. Klicken Sie dazu in der URL-Leiste auf die Schaltfläche 'Zum Startbildschirm hinzufügen'."} ), diff --git a/Customizations/AllKnownLayers.ts b/Customizations/AllKnownLayers.ts index ba5795dfa..ae059e502 100644 --- a/Customizations/AllKnownLayers.ts +++ b/Customizations/AllKnownLayers.ts @@ -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 = AllKnownLayers.getSharedLayers(); public static sharedLayersJson: Map = AllKnownLayers.getSharedLayersJson(); - + private static sharedLayersListRaw: LayerConfigJson[] = known_layers.layers; private static getSharedLayers(): Map { const sharedLayers = new Map(); - for (const layer of AllKnownLayers.sharedLayersListRaw) { - const parsed = new LayerConfig(layer, "shared_layers") - sharedLayers.set(layer.id, parsed); - sharedLayers[layer.id] = parsed; + 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 { const sharedLayers = new Map(); - for (const layer of AllKnownLayers.sharedLayersListRaw) { + for (const layer of known_layers.layers) { sharedLayers.set(layer.id, layer); sharedLayers[layer.id] = layer; } diff --git a/Customizations/AllKnownLayouts.ts b/Customizations/AllKnownLayouts.ts index 488c21c48..87d42f10f 100644 --- a/Customizations/AllKnownLayouts.ts +++ b/Customizations/AllKnownLayouts.ts @@ -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 = 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 = AllKnownLayouts.AllLayouts(); - private static GenerateCycloFix(): LayoutConfig { - const layout = new LayoutConfig(cyclofix) + public static allKnownLayouts: Map = AllKnownLayouts.AllLayouts(); + public static layoutsList: LayoutConfig[] = AllKnownLayouts.GenerateOrderedList(AllKnownLayouts.allKnownLayouts); + + private static GenerateOrderedList(allKnownLayouts: Map): 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 { - this.allLayers = new Map(); - for (const layout of this.layoutsList) { + const dict: Map = 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 = new Map(); - for (const layout of this.layoutsList) { - allSets[layout.id] = layout; - allSets[layout.id.toLowerCase()] = layout; - } - return allSets; + return dict; } } diff --git a/Customizations/JSON/LayerConfig.ts b/Customizations/JSON/LayerConfig.ts index ba6bd4f64..0577c6dca 100644 --- a/Customizations/JSON/LayerConfig.ts +++ b/Customizations/JSON/LayerConfig.ts @@ -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"; @@ -18,7 +19,6 @@ 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 { @@ -452,5 +452,23 @@ export default class LayerConfig { }; } + public ExtractImages(): Set { + const parts: Set[] = [] + 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(preset.description?.ExtractImages(false))) + } + + const allIcons = new Set(); + for (const part of parts) { + part?.forEach(allIcons.add, allIcons) + } + + return allIcons; + } + } \ No newline at end of file diff --git a/Customizations/JSON/LayoutConfig.ts b/Customizations/JSON/LayoutConfig.ts index 7e587ae44..64cfc84c6 100644 --- a/Customizations/JSON/LayoutConfig.ts +++ b/Customizations/JSON/LayoutConfig.ts @@ -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{ + const icons = new Set() + for (const layer of this.layers) { + layer.ExtractImages().forEach(icons.add, icons) + } + icons.add(this.icon) + icons.add(this.socialImage) + return icons + } } \ No newline at end of file diff --git a/Customizations/JSON/LayoutConfigJson.ts b/Customizations/JSON/LayoutConfigJson.ts index 65f415086..0b63c727b 100644 --- a/Customizations/JSON/LayoutConfigJson.ts +++ b/Customizations/JSON/LayoutConfigJson.ts @@ -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? */ diff --git a/Customizations/JSON/TagRenderingConfig.ts b/Customizations/JSON/TagRenderingConfig.ts index c550a303b..05661b737 100644 --- a/Customizations/JSON/TagRenderingConfig.ts +++ b/Customizations/JSON/TagRenderingConfig.ts @@ -70,7 +70,7 @@ export default class TagRenderingConfig { addExtraTags: json.freeform.addExtraTags?.map((tg, i) => FromJSON.Tag(tg, `${context}.extratag[${i}]`)) ?? [] } - if(json.freeform["extraTags"] !== undefined){ + if (json.freeform["extraTags"] !== undefined) { throw `Freeform.extraTags is defined. This should probably be 'freeform.addExtraTag' (at ${context})` } if (this.freeform.key === undefined || this.freeform.key === "") { @@ -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 { + + const usedIcons = new Set() + this.render?.ExtractImages(isIcon)?.forEach(usedIcons.add, usedIcons) + + for (const mapping of this.mappings ?? []) { + mapping.then.ExtractImages(isIcon).forEach(usedIcons.add, usedIcons) + } + + return usedIcons; + } } \ No newline at end of file diff --git a/Docs/Making_Your_Own_Theme.md b/Docs/Making_Your_Own_Theme.md index bb5c8a86d..148b17eb8 100644 --- a/Docs/Making_Your_Own_Theme.md +++ b/Docs/Making_Your_Own_Theme.md @@ -51,9 +51,10 @@ The preferred way to add your theme is via a Pull Request. A Pull Request is les 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. **No external sources are allowed!** External image sources leak privacy or can break. - - Make sure the license is suitable, preferable a Creative Commons license. Attribution can be added at the bottom of this document - - 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 + - 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";` diff --git a/InitUiElements.ts b/InitUiElements.ts index 1b5c41a8e..5503057c0 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -38,6 +38,7 @@ 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 { @@ -287,11 +288,7 @@ export class InitUiElements { const copyrightNotice = new ScrollableFullScreen( () => Translations.t.general.attribution.attributionTitle.Clone(), - () => new Combine([ - Translations.t.general.attribution.attributionContent, - "
", - new Attribution(undefined, undefined, State.state.layoutToUse, undefined) - ]), + () => new AttributionPanel(State.state.layoutToUse), "copyright" ) @@ -300,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"); diff --git a/Models/Constants.ts b/Models/Constants.ts index c51b8ac30..b211bef9d 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -2,7 +2,7 @@ import { Utils } from "../Utils"; export default class Constants { - public static vNumber = "0.6.6c"; + public static vNumber = "0.6.7"; // The user journey states thresholds when a new feature gets unlocked public static userJourney = { diff --git a/Models/smallLicense.ts b/Models/smallLicense.ts new file mode 100644 index 000000000..e87473a0e --- /dev/null +++ b/Models/smallLicense.ts @@ -0,0 +1,6 @@ +export default interface SmallLicense { + path: string, + authors: string[], + license: string, + sources: string[] +} \ No newline at end of file diff --git a/README.md b/README.md index 223d61c89..cce053ce3 100644 --- a/README.md +++ b/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 \ No newline at end of file diff --git a/UI/BigComponents/Attribution.ts b/UI/BigComponents/Attribution.ts index 46934b4da..dac7a0eba 100644 --- a/UI/BigComponents/Attribution.ts +++ b/UI/BigComponents/Attribution.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; diff --git a/UI/BigComponents/AttributionPanel.ts b/UI/BigComponents/AttributionPanel.ts new file mode 100644 index 000000000..343425e1b --- /dev/null +++ b/UI/BigComponents/AttributionPanel.ts @@ -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) { + super([ + Translations.t.general.attribution.attributionContent, + + ((layoutToUse.data.maintainer ?? "") == "") ? "" : Translations.t.general.attribution.themeBy.Subs({author: layoutToUse.data.maintainer}), + layoutToUse.data.credits , + "
", + new Attribution(undefined, undefined, State.state.layoutToUse, undefined), + "
", + 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([ + ``, + new Combine([ + new FixedUiElement(license.authors.join("; ")).SetClass("font-bold"), + new Combine([license.license, license.sources.length > 0 ? " - " : "", + ...license.sources.map(link => `${new URL(link).hostname} `)]).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; + } +} \ No newline at end of file diff --git a/UI/BigComponents/MoreScreen.ts b/UI/BigComponents/MoreScreen.ts index 6fe0524f4..e5b036172 100644 --- a/UI/BigComponents/MoreScreen.ts +++ b/UI/BigComponents/MoreScreen.ts @@ -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)); } diff --git a/UI/i18n/Translation.ts b/UI/i18n/Translation.ts index 380875771..279b0a977 100644 --- a/UI/i18n/Translation.ts +++ b/UI/i18n/Translation.ts @@ -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) { @@ -46,7 +51,7 @@ export class Translation extends UIElement { public SupportedLanguages(): string[] { const langs = [] for (const translationsKey in this.translations) { - if(translationsKey === "#"){ + if (translationsKey === "#") { continue; } langs.push(translationsKey) @@ -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(/]+>/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) + } } \ No newline at end of file diff --git a/assets/layers/benches/license_info.json b/assets/layers/benches/license_info.json new file mode 100644 index 000000000..0b125cebe --- /dev/null +++ b/assets/layers/benches/license_info.json @@ -0,0 +1,10 @@ +[ + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "picnic_table.svg", + "license": "CC0", + "sources": [] + } +] \ No newline at end of file diff --git a/assets/layers/bicycle_library/license_info.json b/assets/layers/bicycle_library/license_info.json new file mode 100644 index 000000000..8ebd0f345 --- /dev/null +++ b/assets/layers/bicycle_library/license_info.json @@ -0,0 +1,10 @@ +[ + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "bicycle_library.svg", + "license": "CC0", + "sources": [] + } +] \ No newline at end of file diff --git a/assets/layers/bicycle_tube_vending_machine/license_info.json b/assets/layers/bicycle_tube_vending_machine/license_info.json new file mode 100644 index 000000000..fe85c0b8a --- /dev/null +++ b/assets/layers/bicycle_tube_vending_machine/license_info.json @@ -0,0 +1,18 @@ +[ + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "pinIcon.svg", + "license": "CC0", + "sources": [] + }, + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "tube.svg", + "license": "CC0", + "sources": [] + } +] \ No newline at end of file diff --git a/assets/layers/bike_cafe/license_info.json b/assets/layers/bike_cafe/license_info.json new file mode 100644 index 000000000..4dd843eae --- /dev/null +++ b/assets/layers/bike_cafe/license_info.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/assets/layers/bike_cleaning/license_info.json b/assets/layers/bike_cleaning/license_info.json new file mode 100644 index 000000000..6462adde5 --- /dev/null +++ b/assets/layers/bike_cleaning/license_info.json @@ -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": [] + } +] \ No newline at end of file diff --git a/assets/layers/bike_monitoring_station/license_info.json b/assets/layers/bike_monitoring_station/license_info.json new file mode 100644 index 000000000..5cf3c9f39 --- /dev/null +++ b/assets/layers/bike_monitoring_station/license_info.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/assets/layers/bike_parking/bike_parking.json b/assets/layers/bike_parking/bike_parking.json index af9a49b25..4c86ac891 100644 --- a/assets/layers/bike_parking/bike_parking.json +++ b/assets/layers/bike_parking/bike_parking.json @@ -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" } diff --git a/assets/layers/bike_parking/license_info.json b/assets/layers/bike_parking/license_info.json new file mode 100644 index 000000000..4e53f86f8 --- /dev/null +++ b/assets/layers/bike_parking/license_info.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/assets/layers/bike_repair_station/license_info.json b/assets/layers/bike_repair_station/license_info.json new file mode 100644 index 000000000..91cf7a733 --- /dev/null +++ b/assets/layers/bike_repair_station/license_info.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/assets/layers/bike_shop/license_info.json b/assets/layers/bike_shop/license_info.json new file mode 100644 index 000000000..af036ada8 --- /dev/null +++ b/assets/layers/bike_shop/license_info.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/assets/layers/bird_hide/license_info.json b/assets/layers/bird_hide/license_info.json new file mode 100644 index 000000000..73f3ddbe8 --- /dev/null +++ b/assets/layers/bird_hide/license_info.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/assets/layers/cycling_themed_object/license_info.json b/assets/layers/cycling_themed_object/license_info.json new file mode 100644 index 000000000..30582c595 --- /dev/null +++ b/assets/layers/cycling_themed_object/license_info.json @@ -0,0 +1,10 @@ +[ + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "other_services.svg", + "license": "CC0", + "sources": [] + } +] \ No newline at end of file diff --git a/assets/layers/drinking_water/license_info.json b/assets/layers/drinking_water/license_info.json new file mode 100644 index 000000000..9a07669a3 --- /dev/null +++ b/assets/layers/drinking_water/license_info.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/assets/layers/ghost_bike/license_info.json b/assets/layers/ghost_bike/license_info.json new file mode 100644 index 000000000..d46c19dca --- /dev/null +++ b/assets/layers/ghost_bike/license_info.json @@ -0,0 +1,10 @@ +[ + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "ghost_bike.svg", + "license": "CC0", + "sources": [] + } +] \ No newline at end of file diff --git a/assets/layers/information_board/license_info.json b/assets/layers/information_board/license_info.json new file mode 100644 index 000000000..3dc32b2a8 --- /dev/null +++ b/assets/layers/information_board/license_info.json @@ -0,0 +1,10 @@ +[ + { + "authors": [], + "path": "board.svg", + "license": "CC0", + "sources": [ + "https://wiki.openstreetmap.org/wiki/File:Board-14.svg" + ] + } +] \ No newline at end of file diff --git a/assets/layers/maps/license_info.json b/assets/layers/maps/license_info.json new file mode 100644 index 000000000..3777e540f --- /dev/null +++ b/assets/layers/maps/license_info.json @@ -0,0 +1,42 @@ +[ + { + "authors": [ + "OpenStreetMap", + " Pieter Vander Vennet", + " M!dgard" + ], + "path": "map-stickered.svg", + "license": "CC-BY-SA", + "sources": [ + "https://OpenStreetMap.org", + " https://wiki.openstreetmap.org/wiki/Logos" + ] + }, + { + "authors": [], + "path": "map.svg", + "license": "CC-BY-SA", + "sources": [ + "https://www.OpenStreetMap.org" + ], + "note": "Modified OpenStreetMap logo" + }, + { + "authors": [], + "path": "osm-logo-buggy-attr.svg", + "license": "CC-BY-SA", + "sources": [ + "https://www.OpenStreetMap.org" + ], + "note": "Modified OpenStreetMap logo" + }, + { + "authors": [], + "path": "osm-logo-white-bg.svg", + "license": "CC-BY-SA", + "sources": [ + "https://www.OpenStreetMap.org" + ], + "note": "Modified OpenStreetMap logo" + } +] \ No newline at end of file diff --git a/assets/layers/nature_reserve/license_info.json b/assets/layers/nature_reserve/license_info.json new file mode 100644 index 000000000..c7e23107f --- /dev/null +++ b/assets/layers/nature_reserve/license_info.json @@ -0,0 +1,42 @@ +[ + { + "authors": [ + "Agentschap Natuur en Bos" + ], + "path": "ANB.jpg", + "license": "Logo; All rights reserved", + "sources": [ + "https://www.natuurenbos.be/" + ] + }, + { + "authors": [ + "Natuurpunt" + ], + "path": "Natuurpunt.jpg", + "license": "Logo; All rights reserved", + "sources": [ + "https://www.natuurpunt.be/" + ] + }, + { + "authors": [ + "Groen" + ], + "path": "groen_logo.svg", + "license": "All rights reserved", + "sources": [ + "https://www.groen.be/" + ] + }, + { + "authors": [ + "@ Эдуард Черных" + ], + "path": "nature_reserve.svg", + "license": "CC-BY 3.0", + "sources": [ + "https://www.onlinewebfonts.com/icon/389579" + ] + } +] \ No newline at end of file diff --git a/assets/layers/nature_reserve/nature_reserve.json b/assets/layers/nature_reserve/nature_reserve.json index b7a332445..4de9ca24e 100644 --- a/assets/layers/nature_reserve/nature_reserve.json +++ b/assets/layers/nature_reserve/nature_reserve.json @@ -333,14 +333,14 @@ } }, { - "#": "Surface are", + "#": "Surface area", "render": { - "en": "Surface area: {_surface:ha}Ha", - "mappings": { + "en": "Surface area: {_surface:ha}Ha" + }, + "mappings":[ { "if": "_surface:ha=0", - "then": "" - } - } + "then": {"*":""} + }] } ], "hideUnderlayingFeaturesMinPercentage": 10, @@ -372,4 +372,4 @@ } ] } - \ No newline at end of file + diff --git a/assets/layers/play_forest/icon.jpg b/assets/layers/play_forest/icon.jpg deleted file mode 100644 index 5a0b42bf7..000000000 Binary files a/assets/layers/play_forest/icon.jpg and /dev/null differ diff --git a/assets/layers/play_forest/license_info.json b/assets/layers/play_forest/license_info.json new file mode 100644 index 000000000..696a885ca --- /dev/null +++ b/assets/layers/play_forest/license_info.json @@ -0,0 +1,12 @@ +[ + { + "authors": [ + "Agentschap Natuur en Bos" + ], + "path": "icon.svg", + "license": "?", + "sources": [ + "https://www.natuurenbos.be/spelen" + ] + } +] \ No newline at end of file diff --git a/assets/layers/playground/playground.json b/assets/layers/playground/playground.json index df78ce6c5..416934ce0 100644 --- a/assets/layers/playground/playground.json +++ b/assets/layers/playground/playground.json @@ -313,7 +313,7 @@ ], "hideUnderlayingFeaturesMinPercentage": 0, "icon": { - "render": "https://upload.wikimedia.org/wikipedia/commons/0/00/Map_icons_by_Scott_de_Jonge_-_playground.svg" + "render": "./assets/themes/playgrounds/playground.svg" }, "iconOverlays": [ { @@ -347,4 +347,4 @@ } ], "wayHandling": 2 -} \ No newline at end of file +} diff --git a/assets/layers/sport_pitch/license_info.json b/assets/layers/sport_pitch/license_info.json new file mode 100644 index 000000000..c0b16967e --- /dev/null +++ b/assets/layers/sport_pitch/license_info.json @@ -0,0 +1,13 @@ +[ + { + "authors": [ + "@fontawesome" + ], + "path": "tabletennis.svg", + "license": "CC-BY 4.0", + "sources": [ + "https://commons.wikimedia.org/wiki/File:Font_Awesome_5_solid_table-tennis.svg", + " https://fontawesome.com" + ] + } +] \ No newline at end of file diff --git a/assets/layers/toilets/license_info.json b/assets/layers/toilets/license_info.json new file mode 100644 index 000000000..3df10fd6a --- /dev/null +++ b/assets/layers/toilets/license_info.json @@ -0,0 +1,27 @@ +[ + { + "authors": [], + "path": "toilets.svg", + "license": "CC0", + "sources": [ + "https://wiki.openstreetmap.org/wiki/File:Toilets-16.svg" + ] + }, + { + "authors": [ + "asianson.design", + " Pieter Vander Vennet" + ], + "path": "urinal.svg", + "license": "CC-BY", + "sources": [ + "https://thenounproject.com/term/urinal/1307984/" + ] + }, + { + "authors": [], + "path": "wheelchair.svg", + "license": "CC0", + "sources": [] + } +] \ No newline at end of file diff --git a/assets/layers/trees/Onroerend_Erfgoed_logo_without_text.svg b/assets/layers/trees/Onroerend_Erfgoed_logo_without_text.svg new file mode 100644 index 000000000..08a1c7ffe --- /dev/null +++ b/assets/layers/trees/Onroerend_Erfgoed_logo_without_text.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/assets/layers/trees/license_info.json b/assets/layers/trees/license_info.json new file mode 100644 index 000000000..934db7908 --- /dev/null +++ b/assets/layers/trees/license_info.json @@ -0,0 +1,12 @@ +[ + { + "authors": [ + "M!dgard" + ], + "path": "Onroerend_Erfgoed_logo_without_text.svg", + "license": "CC0", + "sources": [ + "https://commons.wikimedia.org/wiki/File:Onroerend_Erfgoed_logo_without_text.svg" + ] + } +] \ No newline at end of file diff --git a/assets/layers/trees/tree_nodes.json b/assets/layers/trees/tree_nodes.json index 644ce3932..3efd8808a 100644 --- a/assets/layers/trees/tree_nodes.json +++ b/assets/layers/trees/tree_nodes.json @@ -277,8 +277,8 @@ ] }, "then": { - "nl": "\"\"/ Erkend als houtig erfgoed door Onroerend Erfgoed Vlaanderen", - "en": "\"\"/ Registered as heritage by Onroerend Erfgoed Flanders" + "nl": "\"\"/ Erkend als houtig erfgoed door Onroerend Erfgoed Vlaanderen", + "en": "\"\"/ Registered as heritage by Onroerend Erfgoed Flanders" } }, { @@ -339,8 +339,8 @@ }, { "render": { - "nl": "\"\"/ Onroerend Erfgoed-ID: {ref:OnroerendErfgoed}", - "en": "\"\"/ Onroerend Erfgoed ID: {ref:OnroerendErfgoed}" + "nl": "\"\"/ Onroerend Erfgoed-ID: {ref:OnroerendErfgoed}", + "en": "\"\"/ Onroerend Erfgoed ID: {ref:OnroerendErfgoed}" }, "question": { "nl": "Wat is het ID uitgegeven door Onroerend Erfgoed Vlaanderen?", @@ -359,8 +359,8 @@ }, { "render": { - "nl": "\"\"/ Wikidata: {wikidata}", - "en": "\"\"/ Wikidata: {wikidata}" + "nl": "\"\"/ Wikidata: {wikidata}", + "en": "\"\"/ Wikidata: {wikidata}" }, "question": { "nl": "Wat is het Wikidata-ID van deze boom?", diff --git a/assets/layers/viewpoint/license_info.json b/assets/layers/viewpoint/license_info.json new file mode 100644 index 000000000..3c96b2fed --- /dev/null +++ b/assets/layers/viewpoint/license_info.json @@ -0,0 +1,10 @@ +[ + { + "authors": [], + "path": "viewpoint.svg", + "license": "CC0", + "sources": [ + "https://wiki.openstreetmap.org/wiki/File:Viewpoint-16.svg" + ] + } +] \ No newline at end of file diff --git a/assets/license_info.json b/assets/license_info.json new file mode 100644 index 000000000..79c035029 --- /dev/null +++ b/assets/license_info.json @@ -0,0 +1,64 @@ +[ + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "SocialImage.png", + "license": "CC0", + "sources": [] + }, + { + "authors": [ + "OpenStreetMap" + ], + "path": "generic_osm_background.png", + "license": "CC-BY-SA", + "sources": [ + "https://www.OpenStreetMap.org" + ] + }, + { + "authors": [ + "Mangrove Reviews" + ], + "path": "mangrove_logo.png", + "license": "logo; All rights reserved", + "sources": [ + "https://mangrove.reviews/" + ] + }, + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "social_image_front.png", + "license": "CC-BY-SA", + "sources": [ + "" + ] + }, + { + "authors": [ + "Pieter Vander Vennet", + " OpenStreetMap" + ], + "path": "weblogo.png", + "license": "Logo; CC-BY-SA", + "sources": [ + "https://mapcomplete.osm.be", + " https://www.openstreetmap.org" + ] + }, + { + "authors": [ + "Pieter Vander Vennet", + " OpenStreetMap" + ], + "path": "weblogo.svg", + "license": "Logo; CC-BY-SA", + "sources": [ + "https://mapcomplete.osm.be", + " https://www.openstreetmap.org" + ] + } +] \ No newline at end of file diff --git a/assets/svg/license_info.json b/assets/svg/license_info.json new file mode 100644 index 000000000..1587895cb --- /dev/null +++ b/assets/svg/license_info.json @@ -0,0 +1,554 @@ +[ + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "SocialImageForeground.svg", + "license": "CC-BY-SA", + "sources": [ + "" + ] + }, + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "add.svg", + "license": "CC0", + "sources": [ + "" + ] + }, + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "addSmall.svg", + "license": "CC0", + "sources": [] + }, + { + "authors": [], + "path": "ampersand.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "arrow-left-smooth.svg", + "license": "CC0", + "sources": [] + }, + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "arrow-right-smooth.svg", + "license": "CC0", + "sources": [] + }, + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "back.svg", + "license": "CC0", + "sources": [] + }, + { + "authors": [ + "Github" + ], + "path": "bug.svg", + "license": "MIT", + "sources": [ + "https://commons.wikimedia.org/wiki/File:Octicons-bug.svg", + " https://github.com/primer/octicons" + ] + }, + { + "path": "camera-plus.svg", + "license": "CC-BY-SA 3.0", + "authors": [ + "Dave Gandy", + "Pieter Vander Vennet" + ], + "sources": [ + "https://fontawesome.com/", + "https://commons.wikimedia.org/wiki/File:Camera_font_awesome.svg" + ] + }, + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "checkmark.svg", + "license": "CC0", + "sources": [] + }, + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "circle.svg", + "license": "CC0", + "sources": [] + }, + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "clock.svg", + "license": "CC0", + "sources": [] + }, + { + "authors": [], + "path": "close.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "compass.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "cross_bottom_right.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "crosshair-blue-center.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "crosshair-blue.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "crosshair.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [ + "Dave Gandy" + ], + "path": "delete_icon.svg", + "license": "CC-BY-SA", + "sources": [ + "https://commons.wikimedia.org/wiki/File:Trash_font_awesome.svg\rT" + ] + }, + { + "authors": [], + "path": "direction.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "direction_gradient.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "down.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "envelope.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [ + "The Tango Desktop Project" + ], + "path": "floppy.svg", + "license": "CC0", + "sources": [ + "https://commons.wikimedia.org/wiki/File:Media-floppy.svg", + " http://tango.freedesktop.org/Tango_Desktop_Project" + ] + }, + { + "authors": [], + "path": "gear.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "help.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [ + "Timothy Miller" + ], + "path": "home.svg", + "license": "CC-BY-SA 3.0", + "sources": [ + "https://commons.wikimedia.org/wiki/File:Home-icon.svg" + ] + }, + { + "authors": [ + "Timothy Miller" + ], + "path": "home_white_bg.svg", + "license": "CC-BY-SA 3.0", + "sources": [ + "https://commons.wikimedia.org/wiki/File:Home-icon.svg" + ] + }, + { + "authors": [ + "JOSM Team" + ], + "path": "josm_logo.svg", + "license": "CC0", + "sources": [ + "https://wiki.openstreetmap.org/wiki/File:JOSM_Logotype_2019.svg", + " https://josm.openstreetmap.de/" + ] + }, + { + "authors": [], + "path": "layers.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "layersAdd.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "path": "Ornament-Horiz-0.svg", + "license": "CC-BY", + "authors": [ + "Nightwolfdezines" + ], + "sources": [ + "https://www.vecteezy.com/vector-art/226361-ornaments-and-flourishes" + ] + }, + { + "path": "Ornament-Horiz-1.svg", + "license": "CC-BY", + "authors": [ + "Nightwolfdezines" + ], + "sources": [ + "https://www.vecteezy.com/vector-art/226361-ornaments-and-flourishes" + ] + }, + { + "path": "Ornament-Horiz-2.svg", + "license": "CC-BY", + "authors": [ + "Nightwolfdezines" + ], + "sources": [ + "https://www.vecteezy.com/vector-art/226361-ornaments-and-flourishes" + ] + }, + { + "path": "Ornament-Horiz-3.svg", + "license": "CC-BY", + "authors": [ + "Nightwolfdezines" + ], + "sources": [ + "https://www.vecteezy.com/vector-art/226361-ornaments-and-flourishes" + ] + }, + { + "path": "Ornament-Horiz-4.svg", + "license": "CC-BY", + "authors": [ + "Nightwolfdezines" + ], + "sources": [ + "https://www.vecteezy.com/vector-art/226361-ornaments-and-flourishes" + ] + }, + { + "path": "Ornament-Horiz-5.svg", + "license": "CC-BY", + "authors": [ + "Nightwolfdezines" + ], + "sources": [ + "https://www.vecteezy.com/vector-art/226361-ornaments-and-flourishes" + ] + }, + { + "path": "Ornament-Horiz-6.svg", + "license": "CC-BY", + "authors": [ + "Nightwolfdezines" + ], + "sources": [ + "https://www.vecteezy.com/vector-art/226361-ornaments-and-flourishes" + ] + }, + { + "authors": [], + "path": "star.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "star_outline.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "star_half.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "star_outline_half.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "logo.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "logout.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [ + "Pieter Vander Vennet", + " OSM" + ], + "path": "mapcomplete_logo.svg", + "license": "Logo; CC-BY-SA", + "sources": [ + "" + ] + }, + { + "authors": [ + "Mapillary" + ], + "path": "mapillary.svg", + "license": "Logo; All rights reserved", + "sources": [ + "https://mapillary.com/" + ] + }, + { + "authors": [], + "path": "min.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "no_checkmark.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "or.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "osm-copyright.svg", + "license": "logo; all rights reserved", + "sources": [ + "https://www.OpenStreetMap.org" + ] + }, + { + "authors": [ + "OpenStreetMap U.S. Chapter" + ], + "path": "osm-logo-us.svg", + "license": "Logo", + "sources": [ + "https://www.openstreetmap.us/" + ] + }, + { + "authors": [], + "path": "osm-logo.svg", + "license": "logo; all rights reserved", + "sources": [ + "https://www.OpenStreetMap.org" + ] + }, + { + "authors": [ + "GitHub Octicons" + ], + "path": "pencil.svg", + "license": "MIT", + "sources": [ + "https://github.com/primer/octicons", + " https://commons.wikimedia.org/wiki/File:Octicons-pencil.svg" + ] + }, + { + "authors": [ + "@ tyskrat" + ], + "path": "phone.svg", + "license": "CC-BY 3.0", + "sources": [ + "https://www.onlinewebfonts.com/icon/1059" + ] + }, + { + "authors": [], + "path": "pin.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "plus.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [ + "@fatih" + ], + "path": "pop-out.svg", + "license": "CC-BY 3.0", + "sources": [ + "https://www.onlinewebfonts.com/icon/2151" + ] + }, + { + "authors": [], + "path": "reload.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "ring.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [ + "OOjs UI Team and other contributors" + ], + "path": "search.svg", + "license": "MIT", + "sources": [ + "https://commons.wikimedia.org/wiki/File:OOjs_UI_indicator_search-rtl.svg", + "https://phabricator.wikimedia.org/diffusion/GOJU/browse/master/AUTHORS.txt" + ] + }, + { + "authors": [], + "path": "send_email.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "share.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "square.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [ + "@felpgrc" + ], + "path": "statistics.svg", + "license": "CC-BY 3.0", + "sources": [ + "https://www.onlinewebfonts.com/icon/197818" + ] + }, + { + "authors": [ + "MGalloway (WMF)" + ], + "path": "translate.svg", + "license": "CC-BY-SA 3.0", + "sources": [ + "https://commons.wikimedia.org/wiki/File:OOjs_UI_icon_language-ltr.svg" + ] + }, + { + "authors": [], + "path": "up.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [ + "Wikidata" + ], + "path": "wikidata.svg", + "license": "Logo; All rights reserved", + "sources": [ + "https://www.wikidata.org" + ] + }, + { + "authors": [ + "Wikimedia" + ], + "path": "wikimedia-commons-white.svg", + "license": "Logo; All rights reserved", + "sources": [ + "https://commons.wikimedia.org" + ] + }, + { + "authors": [ + "Wikipedia" + ], + "path": "wikipedia.svg", + "license": "Logo; All rights reserved", + "sources": [ + "https://www.wikipedia.org/" + ] + } +] \ No newline at end of file diff --git a/assets/themes/aed/license_info.json b/assets/themes/aed/license_info.json new file mode 100644 index 000000000..bc067510b --- /dev/null +++ b/assets/themes/aed/license_info.json @@ -0,0 +1,22 @@ +[ + { + "authors": [ + "MaxxL" + ], + "path": "aed.svg", + "license": "CC0", + "sources": [ + "https://commons.wikimedia.org/wiki/File:ISO_7010_E010.svg" + ] + }, + { + "authors": [ + "M!dgard" + ], + "path": "logo.svg", + "license": "CC-BY-SA 4.0", + "sources": [ + "https://commons.wikimedia.org/wiki/File:ISO_7010_E010.svg" + ] + } +] \ No newline at end of file diff --git a/assets/themes/artwork/license_info.json b/assets/themes/artwork/license_info.json new file mode 100644 index 000000000..ea964fe40 --- /dev/null +++ b/assets/themes/artwork/license_info.json @@ -0,0 +1,10 @@ +[ + { + "authors": [], + "path": "artwork.svg", + "license": "CC0", + "sources": [ + "https://wiki.openstreetmap.org/wiki/File:Statue-14.svg" + ] + } +] \ No newline at end of file diff --git a/assets/themes/benches/license_info.json b/assets/themes/benches/license_info.json new file mode 100644 index 000000000..864ed5e7e --- /dev/null +++ b/assets/themes/benches/license_info.json @@ -0,0 +1,24 @@ +[ + { + "authors": [ + "Tobias Zwick" + ], + "path": "bench_poi.svg", + "license": "CC-BY-SA 4.0", + "sources": [ + "https://github.com/streetcomplete/StreetComplete/blob/v25.0-beta1/res/graphics/authors.txt", + "https://github.com/streetcomplete/StreetComplete/tree/v25.0-beta1/res/graphics/quest%20icons" + ] + }, + { + "authors": [ + "Tobias Zwick" + ], + "path": "bench_public_transport.svg", + "license": "CC-BY-SA 4.0", + "sources": [ + "https://github.com/streetcomplete/StreetComplete/blob/v25.0-beta1/res/graphics/authors.txt", + "https://github.com/streetcomplete/StreetComplete/tree/v25.0-beta1/res/graphics/quest%20icons" + ] + } +] \ No newline at end of file diff --git a/assets/themes/bicycle_library/license_info.json b/assets/themes/bicycle_library/license_info.json new file mode 100644 index 000000000..baa0c3ddc --- /dev/null +++ b/assets/themes/bicycle_library/license_info.json @@ -0,0 +1,10 @@ +[ + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "logo.svg", + "license": "CC0", + "sources": [] + } +] \ No newline at end of file diff --git a/assets/themes/bookcases/license_info.json b/assets/themes/bookcases/license_info.json new file mode 100644 index 000000000..3d08aae6c --- /dev/null +++ b/assets/themes/bookcases/license_info.json @@ -0,0 +1,10 @@ +[ + { + "authors": [], + "path": "bookcase.svg", + "license": "CC0", + "sources": [ + "https://wiki.openstreetmap.org/wiki/File:Public_bookcase-14.svg" + ] + } +] \ No newline at end of file diff --git a/assets/themes/buurtnatuur/license_info.json b/assets/themes/buurtnatuur/license_info.json new file mode 100644 index 000000000..83a40d025 --- /dev/null +++ b/assets/themes/buurtnatuur/license_info.json @@ -0,0 +1,72 @@ +[ + { + "authors": [ + "Agentschap Natuur en Bos" + ], + "path": "ANB.jpg", + "license": "Logo; All rights reserved", + "sources": [ + "https://www.natuurenbos.be/" + ] + }, + { + "authors": [ + "Natuurpunt" + ], + "path": "Natuurpunt.jpg", + "license": "Logo; All rights reserved", + "sources": [ + "https://www.natuurpunt.be/" + ] + }, + { + "authors": [ + "@ Federico Sibella" + ], + "path": "forest.svg", + "license": "CC-BY 3.0", + "sources": [ + "https://www.onlinewebfonts.com/icon/498112" + ] + }, + { + "authors": [ + "Groen" + ], + "path": "groen_logo.svg", + "license": "Logo; All rights reserved", + "sources": [ + "https://www.groen.be/" + ] + }, + { + "authors": [ + "@ Эдуард Черных" + ], + "path": "nature_reserve.svg", + "license": "CC-BY 3.0", + "sources": [ + "https://www.onlinewebfonts.com/icon/389579" + ] + }, + { + "authors": [ + "@sterankofrank" + ], + "path": "park.svg", + "license": "CC-BY 3.0", + "sources": [ + "http://www.onlinewebfonts.com/icon/425974" + ] + }, + { + "authors": [ + "Groen" + ], + "path": "social_image.jpg", + "license": "All rights reserved", + "sources": [ + "" + ] + } +] \ No newline at end of file diff --git a/assets/themes/campersites/license_info.json b/assets/themes/campersites/license_info.json new file mode 100644 index 000000000..05ac3dab3 --- /dev/null +++ b/assets/themes/campersites/license_info.json @@ -0,0 +1,45 @@ +[ + { + "path": "Barßel_Wohnmobilstellplatz.jpg", + "authors": [ + "ES01" + ], + "license": "CC-BY-SA 3.0", + "sources": [ + "https://commons.wikimedia.org/wiki/File:Bar%C3%9Fel_Wohnmobilstellplatz.jpg" + ] + }, + { + "path": "caravan.svg", + "authors": [ + "OsmAnd" + ], + "license": "CC0", + "sources": [ + "https://github.com/osmandapp/Osmand/blob/master/LICENSE", + "https://github.com/osmandapp/OsmAnd-resources/blob/36d8b90a8933e0a28f9ee19f28abbd520f9da3c7/icons/svg/accommodation/caravan.svg" + ] + }, + { + "path": "caravan_green.svg", + "authors": [ + "OsmAnd" + ], + "license": "CC0", + "sources": [ + "https://github.com/osmandapp/Osmand/blob/master/LICENSE", + "https://github.com/osmandapp/OsmAnd-resources/blob/36d8b90a8933e0a28f9ee19f28abbd520f9da3c7/icons/svg/accommodation/caravan.svg" + ] + }, + { + "path": "sanitary_dump_station.svg", + "authors": [ + "OsmAnd" + ], + "license": "CC0", + "sources": [ + "https://github.com/osmandapp/Osmand/blob/master/LICENSE", + "https://github.com/osmandapp/OsmAnd-resources/blob/16892d8b2fc00dd422abfb2fef967d5ccd05eeac/icons/svg/poi/sanitary_dump_station.svg" + ] + } +] \ No newline at end of file diff --git a/assets/themes/charging_stations/license_info.json b/assets/themes/charging_stations/license_info.json new file mode 100644 index 000000000..9c36751fc --- /dev/null +++ b/assets/themes/charging_stations/license_info.json @@ -0,0 +1,31 @@ +[ + { + "authors": [ + "Stijn Wens" + ], + "path": "bicycle.svg", + "license": "CC0", + "sources": [ + "https://wens.be/free-antwerpenize-bicycle-font", + " https://antwerpenize.be/post/634396003556081664/antwerpenize-lettertype" + ] + }, + { + "authors": [], + "path": "car.svg", + "license": "CC0", + "sources": [] + }, + { + "authors": [], + "path": "logo.svg", + "license": "CC0; trivial", + "sources": [] + }, + { + "authors": [], + "path": "plug.svg", + "license": "CC0; trivial", + "sources": [] + } +] \ No newline at end of file diff --git a/assets/themes/climbing/license_info.json b/assets/themes/climbing/license_info.json new file mode 100644 index 000000000..d6036112a --- /dev/null +++ b/assets/themes/climbing/license_info.json @@ -0,0 +1,79 @@ +[ + { + "authors": [ + "Christian Neumann" + ], + "path": "climbing_gym.svg", + "license": "CC-BY-SA 3.0", + "sources": [ + "https://utopicode.de/", + "https://github.com/chrneumann/MapComplete" + ] + }, + { + "authors": [ + "Christian Neumann" + ], + "path": "climbing_icon.svg", + "license": "CC-BY-SA 3.0", + "sources": [ + "https://utopicode.de/", + "https://github.com/chrneumann/MapComplete" + ] + }, + { + "authors": [ + "Christian Neumann" + ], + "path": "climbing_no_rope.svg", + "license": "CC-BY-SA 3.0", + "sources": [ + "https://utopicode.de/", + "https://github.com/chrneumann/MapComplete" + ] + }, + { + "authors": [ + "Christian Neumann" + ], + "path": "climbing_rope.svg", + "license": "CC-BY-SA 3.0", + "sources": [ + "https://utopicode.de/", + "https://github.com/chrneumann/MapComplete" + ] + }, + { + "authors": [ + "Christian Neumann" + ], + "path": "climbing_route.svg", + "license": "CC-BY-SA 3.0", + "sources": [ + "https://utopicode.de/", + "https://github.com/chrneumann/MapComplete" + ] + }, + { + "authors": [ + "Christian Neumann" + ], + "path": "climbing_unknown.svg", + "license": "CC-BY-SA 3.0", + "sources": [ + "https://utopicode.de/", + "https://github.com/chrneumann/MapComplete" + ] + }, + { + "authors": [ + "Christian Neumann" + ], + "path": "club.svg", + "license": "CC-BY-SA 3.0", + "sources": [ + "https://utopicode.de/", + "https://github.com/chrneumann/MapComplete" + ] + } +] \ No newline at end of file diff --git a/assets/themes/cyclestreets/license_info.json b/assets/themes/cyclestreets/license_info.json new file mode 100644 index 000000000..95ff5ded6 --- /dev/null +++ b/assets/themes/cyclestreets/license_info.json @@ -0,0 +1,22 @@ +[ + { + "authors": [ + "The RedBurn" + ], + "path": "F111.svg", + "license": "CC0", + "sources": [ + "https://commons.wikimedia.org/wiki/File:Belgian_road_sign_F111_nl.svg" + ] + }, + { + "authors": [ + "The RedBurn" + ], + "path": "F113.svg", + "license": "CC0", + "sources": [ + "https://commons.wikimedia.org/wiki/File:Belgian_road_sign_F113_nl.svg" + ] + } +] \ No newline at end of file diff --git a/assets/themes/cyclofix/cyclofix.json b/assets/themes/cyclofix/cyclofix.json index da22a9319..c3b4947f3 100644 --- a/assets/themes/cyclofix/cyclofix.json +++ b/assets/themes/cyclofix/cyclofix.json @@ -22,6 +22,7 @@ "de" ], "maintainer": "MapComplete", + "credits": "Originally created during Open Summer of Code by Pieter Fiers, Thibault Declercq, Pierre Barban, Joost Schouppe and Pieter Vander Vennet", "icon": "assets/themes/cyclofix/logo.svg", "version": "0", "startLat": 0, diff --git a/assets/themes/cyclofix/license_info.json b/assets/themes/cyclofix/license_info.json new file mode 100644 index 000000000..a754752ed --- /dev/null +++ b/assets/themes/cyclofix/license_info.json @@ -0,0 +1,36 @@ +[ + { + "authors": [ + "De Fietsambassade" + ], + "path": "fietsambassade_gent_logo.svg", + "license": "Logo; All rights reserved", + "sources": [ + "https://fietsambassade.gent.be" + ] + }, + { + "authors": [ + "De Fietsambassade" + ], + "path": "fietsambassade_gent_logo_small.svg", + "license": "Logo; All rights reserved", + "sources": [ + "https://fietsambassade.gent.be" + ] + }, + { + "authors": [ + "Pieter Fiers", + "Thibault Declercq", + "Pierre Barban", + "Joost Schouppe", + "Pieter Vander Vennet" + ], + "path": "logo.svg", + "license": "CC-BY-SA", + "sources": [ + "https://osoc.be/editions/2020/cyclofix" + ] + } +] \ No newline at end of file diff --git a/assets/themes/cyclofix/unused_assets/license_info.json b/assets/themes/cyclofix/unused_assets/license_info.json new file mode 100644 index 000000000..acc14aefb --- /dev/null +++ b/assets/themes/cyclofix/unused_assets/license_info.json @@ -0,0 +1,44 @@ +[ + { + "authors": [ + "Pieter Fiers", + "Thibault Declercq", + "Pierre Barban", + "Joost Schouppe", + "Pieter Vander Vennet" + ], + "path": "place_with_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": "non_bike_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": "non_bike_shop.svg", + "license": "CC-BY-SA", + "sources": [ + "https://osoc.be/editions/2020/cyclofix" + ] + } +] \ No newline at end of file diff --git a/assets/themes/cyclofix/unused_assets/staples-annotated.png b/assets/themes/cyclofix/unused_assets/staples-annotated.png deleted file mode 100644 index a5580831e..000000000 Binary files a/assets/themes/cyclofix/unused_assets/staples-annotated.png and /dev/null differ diff --git a/assets/themes/drinking_water/license_info.json b/assets/themes/drinking_water/license_info.json new file mode 100644 index 000000000..b4a60e965 --- /dev/null +++ b/assets/themes/drinking_water/license_info.json @@ -0,0 +1,16 @@ +[ + { + "authors": [ + "Pieter Fiers", + "Thibault Declercq", + "Pierre Barban", + "Joost Schouppe", + "Pieter Vander Vennet" + ], + "path": "logo.svg", + "license": "CC-BY-SA", + "sources": [ + "https://osoc.be/editions/2020/cyclofix" + ] + } +] \ No newline at end of file diff --git a/assets/themes/facadegardens/license_info.json b/assets/themes/facadegardens/license_info.json new file mode 100644 index 000000000..05d8cb18d --- /dev/null +++ b/assets/themes/facadegardens/license_info.json @@ -0,0 +1,102 @@ +[ + { + "authors": [ + "Klimaan VZW" + ], + "path": "bloei.svg", + "license": "CC-BY-SA 3.0", + "sources": [ + "https://klimaan.be/" + ] + }, + { + "authors": [ + "Klimaan VZW" + ], + "path": "bodembedekker.svg", + "license": "CC-BY-SA 3.0", + "sources": [ + "https://klimaan.be/" + ] + }, + { + "authors": [ + "Klimaan VZW" + ], + "path": "eetbaar.svg", + "license": "CC-BY-SA 3.0", + "sources": [ + "https://klimaan.be/" + ] + }, + { + "authors": [ + "Klimaan VZW" + ], + "path": "gevelton.svg", + "license": "CC-BY-SA 3.0", + "sources": [ + "https://klimaan.be/" + ] + }, + { + "authors": [ + "Klimaan VZW" + ], + "path": "geveltuin.svg", + "license": "CC-BY-SA 3.0", + "sources": [ + "https://klimaan.be/" + ] + }, + { + "authors": [ + "Klimaan VZW" + ], + "path": "halfzon.svg", + "license": "CC-BY-SA 3.0", + "sources": [ + "https://klimaan.be/" + ] + }, + { + "authors": [ + "Klimaan VZW" + ], + "path": "klimplant.svg", + "license": "CC-BY-SA 3.0", + "sources": [ + "https://klimaan.be/" + ] + }, + { + "authors": [ + "Klimaan VZW" + ], + "path": "schaduw.svg", + "license": "CC-BY-SA 3.0", + "sources": [ + "https://klimaan.be/" + ] + }, + { + "authors": [ + "Klimaan VZW" + ], + "path": "struik.svg", + "license": "CC-BY-SA 3.0", + "sources": [ + "https://klimaan.be/" + ] + }, + { + "authors": [ + "Klimaan VZW" + ], + "path": "zon.svg", + "license": "CC-BY-SA 3.0", + "sources": [ + "https://klimaan.be/" + ] + } +] \ No newline at end of file diff --git a/assets/themes/fritures/license_info.json b/assets/themes/fritures/license_info.json new file mode 100644 index 000000000..ba2f607f6 --- /dev/null +++ b/assets/themes/fritures/license_info.json @@ -0,0 +1,22 @@ +[ + { + "authors": [ + "Freepik" + ], + "path": "logo.svg", + "license": "CC-BY", + "sources": [ + "https://www.flaticon.com/free-icon/french-fries_1144288" + ] + }, + { + "authors": [ + "Freepik" + ], + "path": "fries.svg", + "license": "CC-BY", + "sources": [ + "https://www.flaticon.com/free-icon/french-fries_1144288" + ] + } +] \ No newline at end of file diff --git a/assets/themes/fruit_trees/fruit_tree.svg b/assets/themes/fruit_trees/fruit_tree.svg new file mode 100644 index 000000000..5a0e8e9c8 --- /dev/null +++ b/assets/themes/fruit_trees/fruit_tree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/themes/fruit_trees/fruit_trees.json b/assets/themes/fruit_trees/fruit_trees.json index 9189a3ebe..d6a2e6ba3 100644 --- a/assets/themes/fruit_trees/fruit_trees.json +++ b/assets/themes/fruit_trees/fruit_trees.json @@ -13,13 +13,14 @@ "nl" ], "maintainer": "", - "icon": "https://upload.wikimedia.org/wikipedia/commons/4/4e/Low_Hanging_Fruit_-_The_Noun_Project.svg", + "icon": "./assets/themes/fruit_trees/fruit_tree.svg", "version": "0", "startLat": 0, "startLon": 0, "startZoom": 1, "widenFactor": 0.001, "socialImage": "", + "hideFromOverview": true, "layers": [ { "id": "orchards", @@ -27,11 +28,12 @@ "nl": "Boomgaarden" }, "minzoom": 12, - "overpassTags": { + "source":{ + "osmTags": { "and": [ "landuse=orchard" ] - }, + } }, "title": { "render": { "nl": "Boomgaard" @@ -72,11 +74,12 @@ "nl": "Boom" }, "minzoom": 12, - "overpassTags": { + "source":{ + "osmTags": { "and": [ "natural=tree" ] - }, + }}, "title": { "render": { "nl": "Boom" @@ -138,7 +141,7 @@ ], "hideUnderlayingFeaturesMinPercentage": 0, "icon": { - "render": "https://upload.wikimedia.org/wikipedia/commons/4/4e/Low_Hanging_Fruit_-_The_Noun_Project.svg" + "render": "./assets/themes/fruit_trees/fruit_tree.svg" }, "width": { "render": "8" @@ -165,4 +168,4 @@ } ], "roamingRenderings": [] -} \ No newline at end of file +} diff --git a/assets/themes/fruit_trees/license_info.json b/assets/themes/fruit_trees/license_info.json new file mode 100644 index 000000000..2a9ca2e05 --- /dev/null +++ b/assets/themes/fruit_trees/license_info.json @@ -0,0 +1,12 @@ +[ + { + "path": "fruit_tree.svg", + "authors": [ + "The Noun Project" + ], + "license": "CC0", + "sources": [ + "https://upload.wikimedia.org/wikipedia/commons/4/4e/Low_Hanging_Fruit_-_The_Noun_Project.svg" + ] + } +] \ No newline at end of file diff --git a/assets/themes/ghostbikes/license_info.json b/assets/themes/ghostbikes/license_info.json new file mode 100644 index 000000000..baa0c3ddc --- /dev/null +++ b/assets/themes/ghostbikes/license_info.json @@ -0,0 +1,10 @@ +[ + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "logo.svg", + "license": "CC0", + "sources": [] + } +] \ No newline at end of file diff --git a/assets/themes/maps/license_info.json b/assets/themes/maps/license_info.json new file mode 100644 index 000000000..48ba539b3 --- /dev/null +++ b/assets/themes/maps/license_info.json @@ -0,0 +1,12 @@ +[ + { + "authors": [ + "CC0" + ], + "path": "logo.svg", + "license": "CC0", + "sources": [ + "https://wiki.openstreetmap.org/wiki/File:Board-14.svg" + ] + } +] \ No newline at end of file diff --git a/assets/themes/nature/license_info.json b/assets/themes/nature/license_info.json new file mode 100644 index 000000000..743737675 --- /dev/null +++ b/assets/themes/nature/license_info.json @@ -0,0 +1,12 @@ +[ + { + "authors": [ + "Font Awesome" + ], + "path": "logo.svg", + "license": "CC-BY 4.0", + "sources": [ + "https://fontawesome.com" + ] + } +] \ No newline at end of file diff --git a/assets/themes/nature/tree.svg b/assets/themes/nature/tree.svg deleted file mode 100644 index 0b5a59550..000000000 --- a/assets/themes/nature/tree.svg +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - - image/svg+xml - - - - - Openclipart - - - Oak Tree - 2013-11-13T14:27:32 - Oak tree in black and white - https://openclipart.org/detail/188718/oak-tree-by-iggyoblomov-188718 - - - IggyOblomov - - - - - forest - nature - oak - oak tree - quercus - tree - trees - vegetation - - - - - - - - - - - diff --git a/assets/themes/playgrounds/license_info.json b/assets/themes/playgrounds/license_info.json new file mode 100644 index 000000000..f2f73d0ad --- /dev/null +++ b/assets/themes/playgrounds/license_info.json @@ -0,0 +1,13 @@ +[ + { + "authors": [ + "Scott de Jonge" + ], + "path": "playground.svg", + "license": "CC-BY 3.0", + "sources": [ + "https://commons.wikimedia.org/wiki/File:Map_icons_by_Scott_de_Jonge_-_playground.svg", + " https://github.com/scottdejonge/map-icons/blob/b74a0e00afdd6b734883671da7a9e5f30497353e/src/icons/playground.svg" + ] + } +] \ No newline at end of file diff --git a/assets/themes/shops/license_info.json b/assets/themes/shops/license_info.json new file mode 100644 index 000000000..2bacdcb3a --- /dev/null +++ b/assets/themes/shops/license_info.json @@ -0,0 +1,10 @@ +[ + { + "authors": [], + "path": "shop.svg", + "license": "CC0", + "sources": [ + "https://wiki.openstreetmap.org/wiki/File:Convenience-14.svg" + ] + } +] \ No newline at end of file diff --git a/assets/themes/surveillance_cameras/direction_360.svg b/assets/themes/surveillance_cameras/direction_360.svg deleted file mode 100644 index fc60a9ade..000000000 --- a/assets/themes/surveillance_cameras/direction_360.svg +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - diff --git a/assets/themes/surveillance_cameras/license_info.json b/assets/themes/surveillance_cameras/license_info.json new file mode 100644 index 000000000..a3303b966 --- /dev/null +++ b/assets/themes/surveillance_cameras/license_info.json @@ -0,0 +1,34 @@ +[ + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "cam_left.svg", + "license": "CC0", + "sources": [] + }, + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "cam_right.svg", + "license": "CC0", + "sources": [] + }, + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "dome.svg", + "license": "CC0", + "sources": [] + }, + { + "authors": [ + "Pieter Vander Vennet" + ], + "path": "logo.svg", + "license": "CC0", + "sources": [] + } +] \ No newline at end of file diff --git a/assets/themes/toilets/license_info.json b/assets/themes/toilets/license_info.json new file mode 100644 index 000000000..663374b2e --- /dev/null +++ b/assets/themes/toilets/license_info.json @@ -0,0 +1,10 @@ +[ + { + "authors": [], + "path": "toilets.svg", + "license": "CC0", + "sources": [ + "https://wiki.openstreetmap.org/wiki/File:Toilets-16.svg" + ] + } +] \ No newline at end of file diff --git a/assets/themes/tourism_information/logo.svg b/assets/themes/tourism_information/logo.svg deleted file mode 100644 index a1c0b6bb5..000000000 --- a/assets/themes/tourism_information/logo.svg +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - diff --git a/assets/themes/trees/license_info.json b/assets/themes/trees/license_info.json new file mode 100644 index 000000000..e2c358d72 --- /dev/null +++ b/assets/themes/trees/license_info.json @@ -0,0 +1,52 @@ +[ + { + "path": "broadleaved.svg", + "authors": [ + "Midgard" + ], + "license": "CC BY-SA 4.0", + "sources": [ + "https://www.openstreetmap.org/user/M!dgard" + ] + }, + { + "path": "leafless.svg", + "authors": [ + "Midgard" + ], + "license": "CC BY-SA 4.0", + "sources": [ + "https://www.openstreetmap.org/user/M!dgard" + ] + }, + { + "path": "logo.svg", + "authors": [ + "Midgard" + ], + "license": "CC BY-SA 4.0", + "sources": [ + "https://www.openstreetmap.org/user/M!dgard" + ] + }, + { + "path": "needleleaved.svg", + "authors": [ + "Midgard" + ], + "license": "CC BY-SA 4.0", + "sources": [ + "https://www.openstreetmap.org/user/M!dgard" + ] + }, + { + "path": "unknown.svg", + "authors": [ + "Midgard" + ], + "license": "CC BY-SA 4.0", + "sources": [ + "https://www.openstreetmap.org/user/M!dgard" + ] + } +] \ No newline at end of file diff --git a/assets/themes/widths/license_info.json b/assets/themes/widths/license_info.json new file mode 100644 index 000000000..8034093f0 --- /dev/null +++ b/assets/themes/widths/license_info.json @@ -0,0 +1,8 @@ +[ + { + "authors": [], + "path": "icon.svg", + "license": "CC0; trivial", + "sources": [] + } +] \ No newline at end of file diff --git a/assets/translations.json b/assets/translations.json index bd79b4fed..feec34170 100644 --- a/assets/translations.json +++ b/assets/translations.json @@ -519,7 +519,15 @@ "nl": "Met dank aan" }, "attributionContent": { - "en": "

All data is provided by OpenStreetMap, freely reusable under the Open DataBase License.

Some images are provided by Wikimedia

" + "en": "

All data is provided by OpenStreetMap, freely reusable under the Open DataBase License.

" + }, + "themeBy": { + "en": "Theme maintained by {author}" + }, + "iconAttribution": { + "title": { + "en": "Used icons" + } } }, diff --git a/assets/weblogo1000.png b/assets/weblogo1000.png deleted file mode 100644 index 13b18c479..000000000 Binary files a/assets/weblogo1000.png and /dev/null differ diff --git a/index.html b/index.html index 8f0253acf..c2cd5e8df 100644 --- a/index.html +++ b/index.html @@ -27,7 +27,7 @@ - ` + diff --git a/index.ts b/index.ts index 111f92ab0..d145cd1ae 100644 --- a/index.ts +++ b/index.ts @@ -52,7 +52,7 @@ if (location.hostname === "localhost" || location.hostname === "127.0.0.1") { } -// ----------------- SELECT THE RIGHT QUESTSET ----------------- +// ----------------- SELECT THE RIGHT Theme ----------------- const path = window.location.pathname.split("/").slice(-1)[0]; @@ -64,7 +64,7 @@ if (path !== "index.html" && path !== "") { console.log("Using layout", defaultLayout); } defaultLayout = QueryParameters.GetQueryParameter("layout", defaultLayout, "The layout to load into MapComplete").data; -let layoutToUse: LayoutConfig = AllKnownLayouts.allSets[defaultLayout.toLowerCase()]; +let layoutToUse: LayoutConfig = AllKnownLayouts.allKnownLayouts.get(defaultLayout.toLowerCase()); const userLayoutParam = QueryParameters.GetQueryParameter("userlayout", "false"); @@ -81,7 +81,7 @@ if (layoutFromBase64.startsWith("http")) { $.ajax({ url: link, - success: function (data) { + success: (data) => { try { const parsed = JSON.parse(data); @@ -121,6 +121,6 @@ if (layoutFromBase64.startsWith("http")) { .SetStyle("pointer-events: all;") .AttachTo("topleft-tools"); } -window.addEventListener('contextmenu', function (e) { // Not compatible with IE < 9 +window.addEventListener('contextmenu', (e) => { // Not compatible with IE < 9 e.preventDefault(); }, false); diff --git a/package-lock.json b/package-lock.json index 988d8658d..304c00fde 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4029,6 +4029,11 @@ "integrity": "sha512-29GS75BE8asnTno3yB6ubOJOO0FboExEqNJy4bpz0GSmW/8wPTNL4h9h63c6s1uTrOopCmJYe/4yJLh5r92ZUA==", "dev": true }, + "@types/prompt-sync": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@types/prompt-sync/-/prompt-sync-4.1.0.tgz", + "integrity": "sha1-utMynv9bQRXjTvRpgjckTUEdRHA=" + }, "@types/q": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", @@ -4726,6 +4731,11 @@ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + }, "builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", @@ -9903,6 +9913,29 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "prompt-sync": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prompt-sync/-/prompt-sync-4.2.0.tgz", + "integrity": "sha512-BuEzzc5zptP5LsgV5MZETjDaKSWfchl5U9Luiu8SKp7iZWD5tZalOxvNcZRwv+d2phNFr8xlbxmFNcRKfJOzJw==", + "requires": { + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, "protocol-buffers-schema": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.5.1.tgz", @@ -11583,6 +11616,75 @@ } } }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "tslint": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", + "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^4.0.1", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.3", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.13.0", + "tsutils": "^2.29.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "tslint-no-circular-imports": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/tslint-no-circular-imports/-/tslint-no-circular-imports-0.7.0.tgz", + "integrity": "sha512-k3wxpeMC4ef40UbpfBVHEHIzKfNZq5/SCtAO1YjGsaNTklo+K53/TWLrym+poA65RJFDiYgYNWvkeIIkJNA0Vw==", + "dev": true + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "requires": { + "tslib": "^1.8.1" + } + }, "tty-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", diff --git a/package.json b/package.json index 2ab8e4d9e..3b3f47048 100644 --- a/package.json +++ b/package.json @@ -8,19 +8,25 @@ "main": "index.js", "scripts": { "increase-memory": "export NODE_OPTIONS=--max_old_space_size=4096", - "start": "npm run increase-memory && parcel *.html UI/** Logic/** assets/** assets/**/** assets/**/**/** vendor/* vendor/*/*", - "test": "ts-node test/Tag.spec.ts && ts-node test/TagQuestion.spec.ts && ts-node test/ImageSearcher.spec.ts", + "start": "ts-node scripts/generateLayerOverview.ts && npm run increase-memory && parcel *.html UI/** Logic/** assets/** assets/**/** assets/**/**/** vendor/* vendor/*/*", + "test": "ts-node test/Tag.spec.ts && ts-node test/TagQuestion.spec.ts && ts-node test/ImageSearcher.spec.ts && ts-node test/ImageAttribution.spec.ts", "generate:editor-layer-index": "cd assets/ && wget https://osmlab.github.io/editor-layer-index/imagery.geojson --output-document=editor-layer-index.json", "generate:images": "ts-node scripts/generateIncludedImages.ts", "generate:translations": "ts-node scripts/generateTranslations.ts", - "generate:layouts": "ts-node scripts/createLayouts.ts", + "generate:layouts": "ts-node scripts/generateLayouts.ts", + "generate:docs": "ts-node scripts/generateDocs.ts", + "generate:layeroverview": "ts-node scripts/generateLayerOverview.ts", + "generate:licenses": "ts-node scripts/generateLicenseInfo.ts", + "validate:layeroverview": "ts-node scripts/generateLayerOverview.ts", + "validate:licenses": "ts-node scripts/generateLicenseInfo.ts", "optimize-images": "cd assets/generated/ && find -name '*.png' -exec optipng '{}' \\; && echo 'PNGs are optimized'", - "generate": "npm run generate:images && npm run generate:translations", + "generate": "npm run generate:images && npm run generate:translations && npm run generate:licenses", "build": "rm -rf dist/ && npm run generate && parcel build --public-url ./ *.html assets/** assets/**/** assets/**/**/** vendor/* vendor/*/*", - "prepare-deploy": "npm run test && npm run generate:editor-layer-index && npm run generate:layouts && npm run generate && npm run build && rm -rf .cache", + "prepare-deploy": "npm run test && npm run generate:editor-layer-index && npm run generate:layouts && npm run generate:layeroverview && npm run generate && npm run build && rm -rf .cache && npm run generate:docs", "deploy:staging": "npm run prepare-deploy && rm -rf /home/pietervdvn/git/pietervdvn.github.io/Staging/* && cp -r dist/* /home/pietervdvn/git/pietervdvn.github.io/Staging/ && cd /home/pietervdvn/git/pietervdvn.github.io/ && git add * && git commit -m 'New MapComplete Version' && git push && cd - && npm run clean", "deploy:pietervdvn": "npm run prepare-deploy && rm -rf /home/pietervdvn/git/pietervdvn.github.io/MapComplete/* && cp -r dist/* /home/pietervdvn/git/pietervdvn.github.io/MapComplete/ && cd /home/pietervdvn/git/pietervdvn.github.io/ && git add * && git commit -m 'New MapComplete Version' && git push && cd - && npm run clean", "deploy:production": "rm -rf ./assets/generated && npm run prepare-deploy && npm run optimize-images && rm -rf /home/pietervdvn/git/mapcomplete.github.io/* && cp -r dist/* /home/pietervdvn/git/mapcomplete.github.io/ && cd /home/pietervdvn/git/mapcomplete.github.io/ && echo \"mapcomplete.osm.be\" > CNAME && git add * && git commit -m 'New MapComplete Version' && git push && cd - && npm run clean", + "lint": "tslint --project . -c tslint.json '**.ts' ", "clean": "rm -rf .cache/ && (find *.html | grep -v \"\\(index\\|land\\|test\\|preferences\\|customGenerator\\).html\" | xargs rm) && rm *.webmanifest" }, "keywords": [ @@ -38,6 +44,7 @@ "@types/leaflet-providers": "^1.2.0", "@types/leaflet.markercluster": "^1.4.3", "@types/lz-string": "^1.3.34", + "@types/prompt-sync": "^4.1.0", "autoprefixer": "^9.8.6", "country-language": "^0.1.7", "email-validator": "^2.0.4", @@ -58,9 +65,11 @@ "osmtogeojson": "^3.0.0-beta.4", "parcel": "^1.2.4", "postcss": "^7.0.35", + "prompt-sync": "^4.2.0", "sharp": "^0.27.0", "slick-carousel": "^1.8.1", "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.0.2", + "tslint": "^6.1.3", "turf": "^3.0.14" }, "devDependencies": { @@ -73,6 +82,7 @@ "read-file": "^0.2.0", "ts-node": "^9.0.0", "ts-node-dev": "^1.0.0-pre.63", + "tslint-no-circular-imports": "^0.7.0", "turndown": "^7.0.0", "typescript": "^3.9.7", "write-file": "^1.0.0" diff --git a/preferences.ts b/preferences.ts index b2974323d..5343444f8 100644 --- a/preferences.ts +++ b/preferences.ts @@ -17,9 +17,7 @@ function createTable(preferences: any) { } rendered = true; const prefs = []; - console.log(preferences); for (const key in preferences) { - console.log(key) const pref = connection.GetPreference(key, ""); let value: UIElement = new FixedUiElement(pref.data); @@ -36,19 +34,17 @@ function createTable(preferences: any) { "", new Button("delete", () => pref.setData("")), "", - value, + value, "" ]; prefs.push(...c); } - const el = new Combine( + new Combine( ["", ...prefs, "
"] - ); // .AttachTo("maindiv"); - console.log(el.InnerRender()) - el.AttachTo("maindiv"); + ).AttachTo("maindiv"); } connection.preferencesHandler.preferences.addCallback((prefs) => createTable(prefs)) diff --git a/scripts/ScriptUtils.ts b/scripts/ScriptUtils.ts new file mode 100644 index 000000000..36ecc2468 --- /dev/null +++ b/scripts/ScriptUtils.ts @@ -0,0 +1,20 @@ +import {lstatSync, readdirSync} from "fs"; + +export default class ScriptUtils { + public static readDirRecSync(path): string[] { + const result = [] + for (const entry of readdirSync(path)) { + const fullEntry = path + "/" + entry + const stats = lstatSync(fullEntry) + if (stats.isDirectory()) { + // Subdirectory + // @ts-ignore + result.push(...ScriptUtils.readDirRecSync(fullEntry)) + } else { + result.push(fullEntry) + } + } + return result; + } + +} diff --git a/scripts/generateIncludedImages.ts b/scripts/generateIncludedImages.ts index 1eb4e92ad..f240be7ef 100644 --- a/scripts/generateIncludedImages.ts +++ b/scripts/generateIncludedImages.ts @@ -9,6 +9,10 @@ function genImages() { const allNames: string[] = []; for (const path of dir) { + if(path.endsWith("license_info.json")){ + continue; + } + if (!path.endsWith(".svg")) { throw "Non-svg file detected in the svg files: " + path; } diff --git a/scripts/generateLayerOverview.ts b/scripts/generateLayerOverview.ts new file mode 100644 index 000000000..638b50b64 --- /dev/null +++ b/scripts/generateLayerOverview.ts @@ -0,0 +1,136 @@ +import ScriptUtils from "./ScriptUtils"; +import {Utils} from "../Utils"; +import {lstatSync, readdirSync, readFileSync, writeFileSync} from "fs"; + +Utils.runningFromConsole = true +import LayerConfig from "../Customizations/JSON/LayerConfig"; +import {error} from "util"; +import * as licenses from "../assets/generated/license_info.json" +import SmallLicense from "../Models/smallLicense"; +import LayoutConfig from "../Customizations/JSON/LayoutConfig"; +import {LayerConfigJson} from "../Customizations/JSON/LayerConfigJson"; +import {Layer} from "leaflet"; +// This scripts scans 'assets/layers/*.json' for layer definition files and 'assets/themes/*.json' for theme definition files. +// It spits out an overview of those to be used to load them + + +// First, remove the old file. It might be buggy! +writeFileSync("./assets/generated/known_layers_and_themes.json", JSON.stringify({ + "layers": [], + "themes": [] +})) +const layerFiles = ScriptUtils.readDirRecSync("./assets/layers") + .filter(path => path.indexOf(".json") > 0) + .filter(path => path.indexOf("license_info.json") < 0) + .map(path => { + try { + const parsed = JSON.parse(readFileSync(path, "UTF8")); + return parsed + } catch (e) { + console.error("Could not parse file ", path, "due to ", e) + } + }) +const themeFiles: any[] = ScriptUtils.readDirRecSync("./assets/themes") + .filter(path => path.indexOf(".json") > 0) + .filter(path => path.indexOf("license_info.json") < 0) + .map(path => { + return JSON.parse(readFileSync(path, "UTF8")); + }) +writeFileSync("./assets/generated/known_layers_and_themes.json", JSON.stringify({ + "layers": layerFiles, + "themes": themeFiles +})) + + +console.log("Discovered ", layerFiles.length, "layers and ", themeFiles.length, "themes\n") +console.log(" ---------- VALIDATING ---------") +// ------------- VALIDATION -------------- +const licensePaths = [] +for (const i in licenses) { + licensePaths.push(licenses[i].path) +} +const knownPaths = new Set(licensePaths) + +function validateLayer(layerJson: LayerConfigJson, context?: string): string[] { + let errorCount = []; + if (layerJson["overpassTags"] !== undefined) { + errorCount.push("CRIT! Layer ", layerJson.id, "still uses the old 'overpassTags'-format. Please use 'source: {osmTags: }' instead") + } + try { + const layer = new LayerConfig(layerJson, "test", true) + const images = Array.from(layer.ExtractImages()) + const remoteImages = images.filter(img => img.indexOf("http") == 0) + for (const remoteImage of remoteImages) { + errorCount.push("Found a remote image: "+ remoteImage+ " in layer "+ layer.id) + } + for (const image of images) { + if (!knownPaths.has(image)) { + const ctx = context === undefined ? "" : ` in a layer defined in the theme ${context}` + errorCount.push(`Image with path ${image} not found or not attributed; it is used in ${layer.id}${ctx}`) + } + } + + } catch (e) { + return [`Layer ${layerJson.id}` ?? JSON.stringify(layerJson).substring(0, 50)+" is invalid: "+ e] + } + return errorCount +} + +let layerErrorCount = [] +const knownLayerIds = new Set(); +for (const layerFile of layerFiles) { + knownLayerIds.add(layerFile.id) + layerErrorCount .push(...validateLayer(layerFile)) +} + +let themeErrorCount = [] +for (const themeFile of themeFiles) { + + for (const layer of themeFile.layers) { + if (typeof layer === "string") { + if (!knownLayerIds.has(layer)) { + themeErrorCount.push("Unknown layer id: "+ layer) + } + } else { + if (layer.builtin !== undefined) { + if (!knownLayerIds.has(layer.builtin)) { + themeErrorCount.push("Unknown layer id: "+ layer.builtin+ "(which uses inheritance)") + } + } else { + // layer.builtin contains layer overrides - we can skip those + layerErrorCount .push(...validateLayer(layer, themeFile.id)) + } + } + } + + themeFile.layers = themeFile.layers + .filter(l => typeof l != "string") // We remove all the builtin layer references as they don't work with ts-node for some weird reason + .filter(l => l.builtin === undefined) + + try { + const theme = new LayoutConfig(themeFile, true, "test") + if (theme.id !== theme.id.toLowerCase()) { + themeErrorCount.push("Theme ids should be in lowercase, but it is "+ theme.id) + } + } catch (e) { + themeErrorCount.push("Could not parse theme "+ themeFile["id"]+ "due to", e) + } +} + +console.log("LE", layerErrorCount) + +if (layerErrorCount.length + themeErrorCount.length == 0) { + console.log("All good!") +} else { + const errors = layerErrorCount.concat(themeErrorCount).join("\n") + console.log(errors) + const msg = (`Found ${errors.length} errors in the layers; ${themeErrorCount} errors in the themes`) + if(process.argv.indexOf("--no-fail") >= 0) { + console.log(msg) + }else if(process.argv.indexOf("--report") >= 0){ + writeFileSync("layer_report.txt", errors) + }else{ + + throw msg; + } +} diff --git a/scripts/createLayouts.ts b/scripts/generateLayouts.ts similarity index 95% rename from scripts/createLayouts.ts rename to scripts/generateLayouts.ts index 6a5a18932..da0c909de 100644 --- a/scripts/createLayouts.ts +++ b/scripts/generateLayouts.ts @@ -3,13 +3,13 @@ import {Utils} from "../Utils"; Utils.runningFromConsole = true; import LayoutConfig from "../Customizations/JSON/LayoutConfig"; -import {AllKnownLayouts} from "../Customizations/AllKnownLayouts"; import {existsSync, mkdirSync, readFileSync, writeFile, writeFileSync} from "fs"; import Locale from "../UI/i18n/Locale"; import Translations from "../UI/i18n/Translations"; import {Translation} from "../UI/i18n/Translation"; import Constants from "../Models/Constants"; - +import * as all_known_layouts from "../assets/generated/known_layers_and_themes.json" +import {LayoutConfigJson} from "../Customizations/JSON/LayoutConfigJson"; const sharp = require('sharp'); @@ -234,9 +234,12 @@ if (!existsSync(generatedDir)) { } const blacklist = ["", "test", ".", "..", "manifest", "index", "land", "preferences", "account", "openstreetmap", "custom"] -const all = AllKnownLayouts.allSets; +const all : LayoutConfigJson[] = all_known_layouts.themes; -for (const layoutName in all) { +for (const i in all) { + const layoutConfigJson : LayoutConfigJson = all[i] + const layout = new LayoutConfig(layoutConfigJson, true, "generating layouts") + const layoutName = layout.id if (blacklist.indexOf(layoutName.toLowerCase()) >= 0) { console.log(`Skipping a layout with name${layoutName}, it is on the blacklist`); continue; @@ -246,7 +249,6 @@ for (const layoutName in all) { console.log("Could not write manifest for ", layoutName, " because ", err) } }; - const layout = all[layoutName]; validate(layout) createManifest(layout, "").then(manifObj => { const manif = JSON.stringify(manifObj, undefined, 2); diff --git a/scripts/generateLicenseInfo.ts b/scripts/generateLicenseInfo.ts new file mode 100644 index 000000000..95dbbcc20 --- /dev/null +++ b/scripts/generateLicenseInfo.ts @@ -0,0 +1,213 @@ +import {Utils} from "../Utils"; +import {lstatSync, readdirSync, readFileSync, writeFileSync, unlinkSync} from "fs"; +import SmallLicense from "../Models/smallLicense"; +import ScriptUtils from "./ScriptUtils"; + +Utils.runningFromConsole = true; + +/** + * Sweeps the entire 'assets/' (except assets/generated) directory for image files and any 'license_info.json'-file. + * Checks that the license info is included for each of them and generates a compiles license_info.json for those + */ + +function generateLicenseInfos(paths: string[]): SmallLicense[] { + const licenses = [] + for (const path of paths) { + const parsed = JSON.parse(readFileSync(path, "UTF-8")) + if (Array.isArray(parsed)) { + const l: SmallLicense[] = parsed + for (const smallLicens of l) { + smallLicens.path = path.substring(0, path.length - "license_info.json".length) + smallLicens.path + } + licenses.push(...l) + } else { + const smallLicens: SmallLicense = parsed; + /*if(parsed.license === "CC-BY"){ + console.log("Rewriting ", path) + parsed.license === "CC-BY 4.0" + writeFileSync(path, JSON.stringify(smallLicens, null, " ")) + }*/ + + smallLicens.path = path.substring(0, 1 + path.lastIndexOf("/")) + smallLicens.path + licenses.push(smallLicens) + } + + } + return licenses +} + +function missingLicenseInfos(licenseInfos: SmallLicense[], allIcons: string[]) { + const missing = [] + + const knownPaths = new Set() + for (const licenseInfo of licenseInfos) { + knownPaths.add(licenseInfo.path) + } + + for (const iconPath of allIcons) { + if (iconPath.indexOf("license_info.json") >= 0) { + continue; + } + if (knownPaths.has(iconPath)) { + continue; + } + missing.push(iconPath) + } + return missing; +} + +const prompt = require('prompt-sync')(); + +const knownLicenses = new Map() +knownLicenses.set("cf", { + authors: ["Pieter Fiers", "Thibault Declercq", "Pierre Barban", "Joost Schouppe", "Pieter Vander Vennet"], + path: undefined, + license: "CC-BY-SA", + sources: ["https://osoc.be/editions/2020/cyclofix"] +}) +knownLicenses.set("me", { + authors: ["Pieter Vander Vennet"], + path: undefined, + license: "CC0", + sources: [] +}) + +knownLicenses.set("t", { + authors: [], + path: undefined, + license: "CC0; trivial", + sources: [] +}) + +knownLicenses.set("na", { + authors: [], + path: undefined, + license: "CC0", + sources: [] +}) + +knownLicenses.set("chrn", { + authors: ["Christian Neumann"], + path: undefined, + license: "CC-BY-SA 3.0", + sources: ["https://utopicode.de/", "https://github.com/chrneumann/MapComplete"] +}) + +knownLicenses.set("klimaan", { + authors: ["Klimaan VZW"], + path: undefined, + license: "CC-BY-SA 3.0", + sources: ["https://klimaan.be/"] +}) + +function promptLicenseFor(path): SmallLicense { + console.log("License abbreviations:") + knownLicenses.forEach((value, key) => { + console.log(key, " => ", value) + }) + const author = prompt("What is the author for artwork " + path + "? (or: [Q]uit, [S]kip) > ") + path = path.substring(path.lastIndexOf("/") + 1) + + if (knownLicenses.has(author)) { + const license = knownLicenses.get(author); + license.path = path; + return license; + } + + if (author == "s") { + return null; + } + if (author == "Q" || author == "q" || author == "") { + throw "Quitting now!" + } + let authors = author.split(";") + if (author.toLowerCase() == "none") { + authors = [] + } + return { + authors: author.split(";"), + path: path, + license: prompt("What is the license for artwork " + path + "? > "), + sources: prompt("Where was this artwork found? > ").split(";") + } +} + +function createLicenseInfoFor(path): void { + const li = promptLicenseFor(path); + if (li == null) { + return; + } + writeFileSync(path + ".license_info.json", JSON.stringify(li, null, " ")) +} + +function cleanLicenseInfo(allPaths: string[], allLicenseInfos: SmallLicense[]){ + // Read the license info file from the generated assets, creates a compiled license info in every directory + // Note: this removes all the old license infos + for (const licensePath of licensePaths) { + unlinkSync(licensePath) + } + + const perDirectory = new Map() + + for (const license of allLicenseInfos) { + const p = license.path + const dir = p.substring(0, p.lastIndexOf("/")) + license.path = p.substring(dir.length + 1) + if(!perDirectory.has(dir)){ + perDirectory.set(dir, []) + } + perDirectory.get(dir).push(license) + } + + perDirectory.forEach((licenses, dir) => { + writeFileSync( dir+"/license_info.json", JSON.stringify(licenses, null, 2)) + }) + +} + +function queryMissingLicenses(missingLicenses: string[]) { + process.on('SIGINT', function () { + console.log("Aborting... Bye!"); + process.exit(); + }); + + let i = 1; + for (const missingLicens of missingLicenses) { + console.log(i + " / " + missingLicenses.length) + i++; + if (i < missingLicenses.length - 5) { + // continue + } + createLicenseInfoFor(missingLicens) + } + + console.log("You're through!") +} + +console.log("Checking and compiling license info") +const contents = ScriptUtils.readDirRecSync("./assets") + .filter(entry => entry.indexOf("./assets/generated") != 0) +const licensePaths = contents.filter(entry => entry.indexOf("license_info.json") >= 0) +const licenseInfos = generateLicenseInfos(licensePaths); +writeFileSync("./assets/generated/license_info.json", JSON.stringify(licenseInfos, null, " ")) + +const artwork = contents.filter(pth => pth.match(/(.svg|.png|.jpg)$/i) != null) +const missingLicenses = missingLicenseInfos(licenseInfos, artwork) + + +if(process.argv.indexOf("--prompt") >= 0 || process.argv.indexOf("--query") >= 0 ) { + queryMissingLicenses(missingLicenses) +} +if(missingLicenses.length > 0){ + const msg = `There are ${missingLicenses.length} licenses missing.` + if(process.argv.indexOf("--no-fail") >= 0){ + console.log(msg) + }else if(process.argv.indexOf("--report") >= 0){ + writeFileSync("missing_licenses.txt", missingLicenses.join("\n")) + } else{ + + throw msg + } +} + +cleanLicenseInfo(licensePaths, licenseInfos) diff --git a/scripts/createWikiPage.ts b/scripts/generateWikiPage.ts similarity index 100% rename from scripts/createWikiPage.ts rename to scripts/generateWikiPage.ts diff --git a/test/ImageAttribution.spec.ts b/test/ImageAttribution.spec.ts new file mode 100644 index 000000000..1292c0765 --- /dev/null +++ b/test/ImageAttribution.spec.ts @@ -0,0 +1,58 @@ +import {Utils} from "../Utils"; + +Utils.runningFromConsole = true; +import {equal} from "assert"; +import T from "./TestHelper"; +import {FromJSON} from "../Customizations/JSON/FromJSON"; +import Locale from "../UI/i18n/Locale"; +import Translations from "../UI/i18n/Translations"; +import {UIEventSource} from "../Logic/UIEventSource"; +import TagRenderingConfig from "../Customizations/JSON/TagRenderingConfig"; +import EditableTagRendering from "../UI/Popup/EditableTagRendering"; +import {Translation} from "../UI/i18n/Translation"; +import {OH, OpeningHour} from "../UI/OpeningHours/OpeningHours"; +import PublicHolidayInput from "../UI/OpeningHours/PublicHolidayInput"; +import {SubstitutedTranslation} from "../UI/SubstitutedTranslation"; +import {Tag} from "../Logic/Tags/Tag"; +import {And} from "../Logic/Tags/And"; +import {ImageSearcher} from "../Logic/Actors/ImageSearcher"; +import {AllKnownLayouts} from "../Customizations/AllKnownLayouts"; +import AllKnownLayers from "../Customizations/AllKnownLayers"; +import LayerConfig from "../Customizations/JSON/LayerConfig"; + + +new T("ImageAttribution Tests", [ + [ + "Should find all the images", + () => { + const pumps: LayerConfig = AllKnownLayers.sharedLayers["bike_repair_station"] + const images = pumps.ExtractImages(); + const expectedValues = ['./assets/layers/bike_repair_station/repair_station.svg', + './assets/layers/bike_repair_station/repair_station_pump.svg', + './assets/layers/bike_repair_station/broken_pump_2.svg', + './assets/layers/bike_repair_station/pump.svg', + './assets/themes/cyclofix/fietsambassade_gent_logo_small.svg', + './assets/layers/bike_repair_station/pump_example_manual.jpg', + './assets/layers/bike_repair_station/pump_example.png', + './assets/layers/bike_repair_station/pump_example_round.jpg', + './assets/layers/bike_repair_station/repair_station_example.jpg'] + for (const expected of expectedValues) { + T.isTrue(images.has(expected), expected + " not found") + } + } + ], + [ + "Test image discovery regex", + () => { + const tr = new Translation({en: "XYZ XYZ XYZ "}) + const images = new Set(tr.ExtractImages(false)); + equal(3, images.size) + T.isTrue(images.has("a.svg"), "a.svg not found") + T.isTrue(images.has("b.svg"), "b.svg not found") + T.isTrue(images.has("some image.svg"), "some image.svg not found") + + } + ] + + +]) \ No newline at end of file diff --git a/test/TestHelper.ts b/test/TestHelper.ts index 958728439..4232f2cf9 100644 --- a/test/TestHelper.ts +++ b/test/TestHelper.ts @@ -25,4 +25,9 @@ export default class T { } } + static isTrue(b: boolean, msg: string) { + if(!b){ + throw "Expected true, but got false: "+msg + } + } } diff --git a/tslint.json b/tslint.json new file mode 100644 index 000000000..6a204a045 --- /dev/null +++ b/tslint.json @@ -0,0 +1,10 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:recommended", + "tslint-no-circular-imports" + ], + "jsRules": {}, + "rules": {}, + "rulesDirectory": [] +} \ No newline at end of file