forked from MapComplete/MapComplete
		
	Scripts: better output of taginfo project files: add link to docs, add some icons, merge entries to be a bit less spammy
This commit is contained in:
		
							parent
							
								
									fd0e6fc535
								
							
						
					
					
						commit
						3ecbf1163c
					
				
					 1 changed files with 125 additions and 79 deletions
				
			
		|  | @ -9,49 +9,57 @@ import { Utils } from "../src/Utils" | |||
| /** | ||||
|  * Generates all the files in "Docs/TagInfo". These are picked up by the taginfo project, showing a link to the mapcomplete theme if the key is used | ||||
|  */ | ||||
| interface TagInfoEntry { | ||||
|     key: string | ||||
|     description: string | ||||
|     value?: string, | ||||
|     icon_url?: string, | ||||
|     doc_url?: string | ||||
| } | ||||
| 
 | ||||
| interface TagInfoProjectFile { | ||||
|     // data format version, currently always 1, will get updated if there are incompatible changes to the format (required)
 | ||||
|     data_format: 1, | ||||
|     // timestamp when project file was updated is not given as it pollutes the github history
 | ||||
|     project: { | ||||
|         name: string, // name of the project (required)
 | ||||
|         description: string, // short description of the project (required)
 | ||||
|         project_url: string, // home page of the project with general information (required)
 | ||||
|         doc_url: string // documentation of the project and especially the tags used (optional)
 | ||||
|         icon_url: string, // project logo, should work in 16x16 pixels on white and light gray backgrounds (optional)
 | ||||
|         contact_name: string, // contact name, needed for taginfo maintainer (required)
 | ||||
|         contact_email: string // contact email, needed for taginfo maintainer (required)
 | ||||
|     }, | ||||
|     tags: TagInfoEntry[] | ||||
| } | ||||
| 
 | ||||
| interface TagInfoPrototype { | ||||
|     key: string, | ||||
|     value?: string, | ||||
|     shownText: string, | ||||
|     layerName: string, | ||||
|     layer: LayerConfig, | ||||
|     icon?: string | ||||
|     emoji?: string | ||||
|     trid?: string | ||||
| } | ||||
| 
 | ||||
| const outputDirectory = "Docs/TagInfo" | ||||
| 
 | ||||
| function generateTagOverview( | ||||
|     kv: { k: string; v: string }, | ||||
|     description: string | ||||
| ): { | ||||
|     key: string | ||||
|     description: string | ||||
|     value?: string | ||||
| } { | ||||
|     const overview = { | ||||
|         // OSM tag key (required)
 | ||||
|         key: kv.k, | ||||
|         description: description, | ||||
|         value: undefined, | ||||
|     } | ||||
|     if (kv.v !== undefined) { | ||||
|         // OSM tag value (optional, if not supplied it means "all values")
 | ||||
|         overview.value = kv.v | ||||
|     } | ||||
|     return overview | ||||
| } | ||||
| 
 | ||||
| function generateLayerUsage(layer: LayerConfig, layout: ThemeConfig): any[] { | ||||
| function generateLayerUsage(layer: LayerConfig): TagInfoPrototype[] { | ||||
|     if (layer.name === undefined) { | ||||
|         return [] // Probably a duplicate or irrelevant layer
 | ||||
|     } | ||||
| 
 | ||||
|     const usedTags = layer.source.osmTags.asChange({}) | ||||
|     const result: { | ||||
|         key: string | ||||
|         description: string | ||||
|         value?: string | ||||
|     }[] = [] | ||||
|     const result: TagInfoPrototype[] = [] | ||||
|     const layerName = layer.name.txt | ||||
|     for (const kv of usedTags) { | ||||
|         const description = | ||||
|             "The MapComplete theme " + | ||||
|             layout.title.txt + | ||||
|             " has a layer " + | ||||
|             layer.name.txt + | ||||
|             " showing features with this tag" | ||||
|         result.push(generateTagOverview(kv, description)) | ||||
|         result.push({ | ||||
|             key: kv.k, value: kv.v, layerName, | ||||
|             shownText: "Features with this tag are displayed", | ||||
|             layer | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     for (const tr of layer.tagRenderings) { | ||||
|  | @ -66,55 +74,47 @@ function generateLayerUsage(layer: LayerConfig, layout: ThemeConfig): any[] { | |||
|             const usesImageUpload = (tr.render?.txt?.indexOf("image_upload") ?? -2) > 0 | ||||
| 
 | ||||
|             if (usesImageCarousel || usesImageUpload) { | ||||
|                 const descrNoUpload = `The layer '${layer.name.txt} shows images based on the keys image, image:0, image:1,..., panoramax, panoramax:0, panoramx:1, ... ,  wikidata, wikipedia, wikimedia_commons and mapillary` | ||||
|                 const descrUpload = `The layer '${layer.name.txt} allows to upload images and adds them under the 'panoramax'-tag (and panoramax:0, panoramax:1, ... for multiple images). Furthermore, this layer shows images based on the keys panoramax, image, wikidata, wikipedia, wikimedia_commons and mapillary` | ||||
|                 const descrNoUpload = `Images  are displayed based on the keys image, image:0, image:1,..., panoramax, panoramax:0, panoramx:1, ... ,  wikidata, wikipedia, wikimedia_commons and mapillary` | ||||
|                 const descrUpload = `${descrNoUpload} Furthermore, this layer shows images based on the keys panoramax, image, wikidata, wikipedia, wikimedia_commons and mapillary` | ||||
| 
 | ||||
|                 const descr = (usesImageUpload ? descrUpload : descrNoUpload) + condition | ||||
| 
 | ||||
|                 result.push(generateTagOverview({ k: "image", v: undefined }, descr)) | ||||
|                 result.push(generateTagOverview({ k: "panoramax", v: undefined }, descr)) | ||||
| 
 | ||||
|                 result.push(generateTagOverview({ k: "mapillary", v: undefined }, descr)) | ||||
|                 result.push(generateTagOverview({ k: "wikidata", v: undefined }, descr)) | ||||
|                 result.push(generateTagOverview({ k: "wikipedia", v: undefined }, descr)) | ||||
|                 const shownText = (usesImageUpload ? descrUpload : descrNoUpload) + condition | ||||
|                 const keys = ["image", "panoramax", "mapillary", "wikidata", "wikipedia"] | ||||
|                 for (const key of keys) { | ||||
|                     result.push({ | ||||
|                         key, shownText, layerName, layer, emoji: "📷", trid: "images" | ||||
|                     }) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         const q = tr.question?.txt | ||||
|         const key = tr.freeform?.key | ||||
|         if (key != undefined) { | ||||
|             let descr = `Layer '${layer.name.txt}'` | ||||
|             if (q == undefined) { | ||||
|                 descr += " shows values with" | ||||
|             } else { | ||||
|                 descr += " shows and asks freeform values for" | ||||
|             let descr = "Values of `" + key + "` are shown with \"" + tr.render.txt + "\"" | ||||
|             if (q != undefined) { | ||||
|                 descr += " and can be updated. The question is \"" + q + "\"" | ||||
|             } | ||||
|             descr += ` key '${key}' (in the mapcomplete.org theme '${layout.title.txt}')` | ||||
|             result.push(generateTagOverview({ k: key, v: undefined }, descr + condition)) | ||||
|             result.push(({ | ||||
|                 key, layerName, shownText: descr, | ||||
|                 layer, | ||||
|                 icon: !Utils.isEmoji(tr.renderIcon) ? tr.renderIcon : undefined, | ||||
|                 emoji: Utils.isEmoji(tr.renderIcon) ? tr.renderIcon : undefined, | ||||
|                 trid: tr.id | ||||
|             })) | ||||
|         } | ||||
| 
 | ||||
|         const mappings = tr.mappings ?? [] | ||||
|         for (const mapping of mappings) { | ||||
|             let descr = "Layer '" + layer.name.txt + "'" | ||||
|             descr += | ||||
|                 " shows " + | ||||
|                 mapping.if.asHumanString(false, false, {}) + | ||||
|                 " with a fixed text, namely '" + | ||||
|                 mapping.then.txt + | ||||
|                 "'" | ||||
|             if ( | ||||
|                 q != undefined && | ||||
|                 mapping.hideInAnswer != true // != true will also match if a
 | ||||
|             ) { | ||||
|                 descr += " and allows to pick this as a default answer" | ||||
|             } | ||||
|             descr += ` (in the mapcomplete.org theme '${layout.title.txt}')` | ||||
|         for (const mapping of tr.mappings ?? []) { | ||||
|             for (const kv of mapping.if.asChange({})) { | ||||
|                 let d = descr | ||||
|                 if (q != undefined && kv.v == "") { | ||||
|                     d = `${descr} Picking this answer will delete the key ${kv.k}.` | ||||
|                 } | ||||
|                 result.push(generateTagOverview(kv, d + condition)) | ||||
|                 result.push({ | ||||
|                     key: kv.k, | ||||
|                     value: kv.v, | ||||
|                     layerName, | ||||
|                     layer, | ||||
|                     shownText: `${mapping.if.asHumanString()} is displayed as "${mapping.then.txt}"`, | ||||
|                     icon: !Utils.isEmoji(mapping.icon) ? mapping.icon : undefined, | ||||
|                     emoji: Utils.isEmoji(mapping.icon) ? mapping.icon : undefined, | ||||
|                     trid: tr.id | ||||
|                 }) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -126,8 +126,8 @@ function generateLayerUsage(layer: LayerConfig, layout: ThemeConfig): any[] { | |||
|  * Generates the JSON-object representing the theme for inclusion on taginfo | ||||
|  * @param layout | ||||
|  */ | ||||
| function generateTagInfoEntry(layout: ThemeConfig): any { | ||||
|     const usedTags = [] | ||||
| function generateTagInfoEntry(layout: ThemeConfig): string { | ||||
|     const usedTags: TagInfoPrototype[] = [] | ||||
|     for (const layer of layout.layers) { | ||||
|         if (layer.source === null) { | ||||
|             continue | ||||
|  | @ -135,7 +135,7 @@ function generateTagInfoEntry(layout: ThemeConfig): any { | |||
|         if (layer.source.geojsonSource !== undefined && layer.source.isOsmCacheLayer !== true) { | ||||
|             continue | ||||
|         } | ||||
|         usedTags.push(...generateLayerUsage(layer, layout)) | ||||
|         usedTags.push(...generateLayerUsage(layer)) | ||||
|     } | ||||
| 
 | ||||
|     if (usedTags.length == 0) { | ||||
|  | @ -147,7 +147,53 @@ function generateTagInfoEntry(layout: ThemeConfig): any { | |||
|         icon = icon.substring(2) | ||||
|     } | ||||
| 
 | ||||
|     const themeInfo = { | ||||
| 
 | ||||
|     const merged: Map<string, TagInfoPrototype[]> = new Map<string, TagInfoPrototype[]>() | ||||
|     for (const entry of usedTags) { | ||||
|         const key = entry.key + ";" + (entry.value ?? "") + ";" + entry.shownText | ||||
|         let entries = merged.get(key) | ||||
|         if (!entries) { | ||||
|             entries = [] | ||||
|             merged.set(key, entries) | ||||
|         } | ||||
|         entries.push(entry) | ||||
|     } | ||||
| 
 | ||||
|     const entries: TagInfoEntry[] = [] | ||||
| 
 | ||||
|     const repo = "https://source.mapcomplete.org/MapComplete/MapComplete/" | ||||
|     const branchBase = repo + "src/branch/develop/" | ||||
|     Array.from(merged.values()).forEach((prototypes: TagInfoPrototype[]) => { | ||||
|         // We use a prototype without condition, as this has a higher chance of being the "root"-layer
 | ||||
|         const p = prototypes[0] | ||||
|         const layers = prototypes.map(p => p.layerName) | ||||
| 
 | ||||
|         let layerDescr = `layers ${layers.join(", ")}` | ||||
|         if (layers.length === 1) { | ||||
|             layerDescr = `layer ${layers[0]}` | ||||
|         } | ||||
|         let doc_url = branchBase + "Docs/Layers/" + p.layer.id + ".md" | ||||
|         if (p.trid) { | ||||
|             doc_url += "#" + p.trid.replace(/[^a-zA-Z0-9]/g, "_") | ||||
|         } | ||||
|         let defaultIcon = undefined | ||||
|         if (p.layer.hasDefaultIcon()) { | ||||
|             defaultIcon = p.layer.mapRendering.map(pr => pr.marker?.at(-1)?.icon?.render?.txt).find(x => x !== undefined) | ||||
|         } | ||||
|         let value = p.value | ||||
|         if (value === "") { | ||||
|             value = undefined | ||||
|         } | ||||
|         entries.push({ | ||||
|             key: p.key, | ||||
|             value, | ||||
|             description: p.shownText + " by " + layerDescr, | ||||
|             doc_url, | ||||
|             icon_url: p.icon ?? defaultIcon | ||||
|         }) | ||||
|     }) | ||||
| 
 | ||||
|     const themeInfo: TagInfoProjectFile = { | ||||
|         // data format version, currently always 1, will get updated if there are incompatible changes to the format (required)
 | ||||
|         data_format: 1, | ||||
|         // timestamp when project file was updated is not given as it pollutes the github history
 | ||||
|  | @ -156,12 +202,12 @@ function generateTagInfoEntry(layout: ThemeConfig): any { | |||
|             description: layout.shortDescription.txt, // short description of the project (required)
 | ||||
|             project_url: "https://mapcomplete.org/" + layout.id, // home page of the project with general information (required)
 | ||||
|             doc_url: | ||||
|                 "https://source.mapcomplete.org/MapComplete/MapComplete/src/branch/develop/Docs/Themes", // documentation of the project and especially the tags used (optional)
 | ||||
|                 repo + "src/branch/develop/Docs/Themes", // documentation of the project and especially the tags used (optional)
 | ||||
|             icon_url: "https://mapcomplete.org/" + icon, // project logo, should work in 16x16 pixels on white and light gray backgrounds (optional)
 | ||||
|             contact_name: "Pieter Vander Vennet", // contact name, needed for taginfo maintainer (required)
 | ||||
|             contact_email: "pietervdvn@posteo.net", // contact email, needed for taginfo maintainer (required)
 | ||||
|             contact_email: "info@mapcomplete.org" // contact email, needed for taginfo maintainer (required)
 | ||||
|         }, | ||||
|         tags: usedTags, | ||||
|         tags: entries | ||||
|     } | ||||
| 
 | ||||
|     const filename = "mapcomplete_" + layout.id | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue