2023-07-17 22:04:35 +02:00
import { AllKnownLayouts } from "../src/Customizations/AllKnownLayouts"
import Locale from "../src/UI/i18n/Locale"
import { Translation } from "../src/UI/i18n/Translation"
2021-05-05 11:30:44 +02:00
import { readFileSync , writeFileSync } from "fs"
2024-10-17 04:06:03 +02:00
import ThemeConfig from "../src/Models/ThemeConfig/ThemeConfig"
2023-07-17 22:04:35 +02:00
import LayerConfig from "../src/Models/ThemeConfig/LayerConfig"
import { Utils } from "../src/Utils"
2021-04-19 18:17:26 +02:00
2021-05-04 17:46:45 +02:00
/ * *
* 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
* /
2025-03-27 03:50:48 +01:00
interface TagInfoEntry {
2021-06-24 14:02:18 +02:00
key : string
description : string
2025-03-27 03:50:48 +01:00
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
2021-04-20 11:54:45 +02:00
}
2025-03-27 03:50:48 +01:00
const outputDirectory = "Docs/TagInfo"
function generateLayerUsage ( layer : LayerConfig ) : TagInfoPrototype [ ] {
2021-09-09 00:05:51 +02:00
if ( layer . name === undefined ) {
2021-07-28 00:12:14 +02:00
return [ ] // Probably a duplicate or irrelevant layer
}
2021-09-09 00:05:51 +02:00
2021-04-19 18:17:26 +02:00
const usedTags = layer . source . osmTags . asChange ( { } )
2025-03-27 03:50:48 +01:00
const result : TagInfoPrototype [ ] = [ ]
const layerName = layer . name . txt
2021-04-19 18:17:26 +02:00
for ( const kv of usedTags ) {
2025-03-27 03:50:48 +01:00
result . push ( {
key : kv.k , value : kv.v , layerName ,
shownText : "Features with this tag are displayed" ,
layer
} )
2021-04-20 11:54:45 +02:00
}
for ( const tr of layer . tagRenderings ) {
2022-12-23 17:42:53 +01:00
let condition = tr . condition ? . asHumanString ( false , false , { } ) ? ? ""
if ( condition !== "" ) {
condition = ` (This is only shown if ${ condition } ) `
}
2021-05-04 17:46:45 +02:00
{
2021-07-03 22:00:36 +02:00
const usesImageCarousel = ( tr . render ? . txt ? . indexOf ( "image_carousel" ) ? ? - 2 ) > 0
const usesImageUpload = ( tr . render ? . txt ? . indexOf ( "image_upload" ) ? ? - 2 ) > 0
2021-05-04 17:46:45 +02:00
if ( usesImageCarousel || usesImageUpload ) {
2025-04-08 02:48:58 +02:00
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 `
2025-03-27 03:50:48 +01:00
const descrUpload = ` ${ descrNoUpload } Furthermore, this layer shows images based on the keys panoramax, image, wikidata, wikipedia, wikimedia_commons and mapillary `
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"
} )
}
2021-05-04 17:46:45 +02:00
}
}
2021-04-20 11:54:45 +02:00
const q = tr . question ? . txt
const key = tr . freeform ? . key
if ( key != undefined ) {
2025-03-27 03:50:48 +01:00
let descr = "Values of `" + key + "` are shown with \"" + tr . render . txt + "\""
if ( q != undefined ) {
descr += " and can be updated. The question is \"" + q + "\""
2021-04-20 11:54:45 +02:00
}
2025-03-27 03:50:48 +01:00
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
} ) )
2021-04-20 11:54:45 +02:00
}
2025-03-27 03:50:48 +01:00
for ( const mapping of tr . mappings ? ? [ ] ) {
2021-04-20 11:54:45 +02:00
for ( const kv of mapping . if . asChange ( { } ) ) {
2025-03-27 03:50:48 +01:00
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
} )
2021-04-20 11:54:45 +02:00
}
2021-04-19 18:17:26 +02:00
}
}
2021-09-09 00:05:51 +02:00
2021-06-24 14:02:18 +02:00
return result . filter ( ( result ) = > ! result . key . startsWith ( "_" ) )
2021-04-19 18:17:26 +02:00
}
/ * *
* Generates the JSON - object representing the theme for inclusion on taginfo
* @param layout
* /
2025-03-27 03:50:48 +01:00
function generateTagInfoEntry ( layout : ThemeConfig ) : string {
const usedTags : TagInfoPrototype [ ] = [ ]
2021-04-19 18:17:26 +02:00
for ( const layer of layout . layers ) {
2023-03-25 02:48:24 +01:00
if ( layer . source === null ) {
2022-03-12 16:30:43 +01:00
continue
}
if ( layer . source . geojsonSource !== undefined && layer . source . isOsmCacheLayer !== true ) {
continue
}
2025-03-27 03:50:48 +01:00
usedTags . push ( . . . generateLayerUsage ( layer ) )
2021-04-19 18:17:26 +02:00
}
2022-09-08 21:40:48 +02:00
2022-03-12 16:35:27 +01:00
if ( usedTags . length == 0 ) {
return undefined
}
2021-04-19 18:17:26 +02:00
let icon = layout . icon
if ( icon . startsWith ( "./" ) ) {
icon = icon . substring ( 2 )
}
2025-03-27 03:50:48 +01:00
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 = {
2021-04-19 18:17:26 +02:00
// data format version, currently always 1, will get updated if there are incompatible changes to the format (required)
data_format : 1 ,
2022-03-12 16:30:43 +01:00
// timestamp when project file was updated is not given as it pollutes the github history
2021-04-19 18:17:26 +02:00
project : {
name : "MapComplete " + layout . title . txt , // name of the project (required)
description : layout.shortDescription.txt , // short description of the project (required)
2023-08-23 18:33:30 +02:00
project_url : "https://mapcomplete.org/" + layout . id , // home page of the project with general information (required)
2025-03-06 16:21:55 +01:00
doc_url :
2025-03-27 03:50:48 +01:00
repo + "src/branch/develop/Docs/Themes" , // documentation of the project and especially the tags used (optional)
2023-08-23 18:33:30 +02:00
icon_url : "https://mapcomplete.org/" + icon , // project logo, should work in 16x16 pixels on white and light gray backgrounds (optional)
2022-08-24 14:48:33 +02:00
contact_name : "Pieter Vander Vennet" , // contact name, needed for taginfo maintainer (required)
2025-03-27 03:50:48 +01:00
contact_email : "info@mapcomplete.org" // contact email, needed for taginfo maintainer (required)
2021-04-19 18:17:26 +02:00
} ,
2025-03-27 03:50:48 +01:00
tags : entries
2021-04-19 18:17:26 +02:00
}
const filename = "mapcomplete_" + layout . id
console . log ( "Writing info about " + layout . id )
2021-09-09 00:30:48 +02:00
writeFileSync ( ` ${ outputDirectory } / ${ filename } .json ` , JSON . stringify ( themeInfo , null , 2 ) )
2021-04-19 18:17:26 +02:00
return filename
}
2021-05-07 12:39:37 +02:00
// Write the URLS to the taginfo repository. Might fail if the repository is not checked ou
2022-03-12 16:35:27 +01:00
function generateProjectsOverview ( files : string [ ] ) {
2021-05-07 12:39:37 +02:00
try {
2025-02-17 22:47:39 +01:00
const tagInfoList = "../../git/taginfo-projects/project_list.txt"
const projectList = readFileSync ( tagInfoList , { encoding : "utf8" } )
2021-05-07 12:39:37 +02:00
. split ( "\n" )
. filter ( ( entry ) = > entry . indexOf ( "mapcomplete_" ) < 0 )
2022-03-12 16:42:14 +01:00
. concat (
files . map (
( f ) = >
2025-02-17 22:47:39 +01:00
` ${ f } https://source.mapcomplete.org/MapComplete/MapComplete/raw/branch/develop/Docs/TagInfo/ ${ f } .json `
2022-09-08 21:40:48 +02:00
)
)
2021-05-07 12:39:37 +02:00
. sort ( )
. filter ( ( entry ) = > entry != "" )
console . log ( "Writing taginfo project filelist" )
writeFileSync ( tagInfoList , projectList . join ( "\n" ) + "\n" )
} catch ( e ) {
console . warn (
"Could not write the taginfo-projects list - the repository is probably not checked out. Are you creating a fork? Ignore this message then."
)
}
}
2022-03-12 16:35:27 +01:00
function main() {
2021-04-19 18:17:26 +02:00
console . log ( "Creating taginfo project files" )
2021-05-07 12:39:37 +02:00
2021-04-19 18:17:26 +02:00
Locale . language . setData ( "en" )
Translation . forcedLanguage = "en"
2022-03-12 16:35:27 +01:00
const files = [ ]
2022-09-08 21:40:48 +02:00
2023-03-02 05:20:53 +01:00
for ( const layout of AllKnownLayouts . allKnownLayouts . values ( ) ) {
2022-03-12 16:35:27 +01:00
if ( layout . hideFromOverview ) {
continue
}
2024-11-07 11:19:15 +01:00
if ( layout . id === "personal" ) {
2024-11-01 14:08:06 +01:00
continue
}
2022-03-12 16:35:27 +01:00
files . push ( generateTagInfoEntry ( layout ) )
2021-04-19 18:17:26 +02:00
}
2022-03-12 16:35:27 +01:00
generateProjectsOverview ( Utils . NoNull ( files ) )
2021-04-19 18:17:26 +02:00
}
2022-03-12 16:35:27 +01:00
main ( )