mapcomplete/Models/ThemeConfig/Conversion/CreateNoteImportLayer.ts

185 lines
7.8 KiB
TypeScript
Raw Normal View History

2022-02-04 01:05:35 +01:00
import {Conversion} from "./Conversion";
import LayerConfig from "../LayerConfig";
import {LayerConfigJson} from "../Json/LayerConfigJson";
import Translations from "../../../UI/i18n/Translations";
import PointRenderingConfigJson from "../Json/PointRenderingConfigJson";
2022-05-01 22:58:59 +02:00
import {Translation, TypedTranslation} from "../../../UI/i18n/Translation";
export default class CreateNoteImportLayer extends Conversion<LayerConfigJson, LayerConfigJson> {
/**
* A closed note is included if it is less then 'n'-days closed
* @private
*/
private readonly _includeClosedNotesDays: number;
2022-01-26 21:40:38 +01:00
constructor(includeClosedNotesDays = 0) {
super([
"Advanced conversion which deducts a layer showing all notes that are 'importable' (i.e. a note that contains a link to some MapComplete theme, with hash '#import').",
"The import buttons and matches will be based on the presets of the given theme",
2022-07-18 02:00:32 +02:00
].join("\n\n"), [], "CreateNoteImportLayer")
this._includeClosedNotesDays = includeClosedNotesDays;
}
2022-02-10 23:16:14 +01:00
convert(layerJson: LayerConfigJson, context: string): { result: LayerConfigJson } {
const t = Translations.t.importLayer;
2022-01-26 21:40:38 +01:00
/**
* The note itself will contain `tags=k=v;k=v;k=v;...
* This must be matched with a regex.
* This is a simple JSON-object as how it'll be put into the layerConfigJson directly
*/
2022-01-26 21:40:38 +01:00
const isShownIfAny: any[] = []
const layer = new LayerConfig(layerJson, "while constructing a note-import layer")
for (const preset of layer.presets) {
const mustMatchAll = []
for (const tag of preset.tags) {
const key = tag.key
const value = tag.value
2022-01-26 21:40:38 +01:00
const condition = "_tags~(^|.*;)" + key + "\=" + value + "($|;.*)"
mustMatchAll.push(condition)
}
2022-01-26 21:40:38 +01:00
isShownIfAny.push({and: mustMatchAll})
}
2022-01-26 21:40:38 +01:00
const pointRenderings = (layerJson.mapRendering ?? []).filter(r => r !== null && r["location"] !== undefined);
const firstRender = <PointRenderingConfigJson>(pointRenderings [0])
2022-07-18 02:00:32 +02:00
if (firstRender === undefined) {
throw `Layer ${layerJson.id} does not have a pointRendering: ` + context
}
2022-01-25 21:55:51 +01:00
const title = layer.presets[0].title
2022-01-26 21:40:38 +01:00
const importButton = {}
{
2022-07-18 02:00:32 +02:00
const translations = trs(t.importButton, {layerId: layer.id, title: layer.presets[0].title})
for (const key in translations) {
2022-07-18 02:00:32 +02:00
if (key !== "_context") {
2022-05-01 22:58:59 +02:00
importButton[key] = "{" + translations[key] + "}"
2022-07-18 02:00:32 +02:00
} else {
2022-05-01 22:58:59 +02:00
importButton[key] = translations[key]
}
2022-01-26 21:40:38 +01:00
}
}
2022-01-26 21:40:38 +01:00
function embed(prefix, translation: Translation, postfix) {
2022-01-25 21:55:51 +01:00
const result = {}
for (const language in translation.translations) {
2022-01-26 21:40:38 +01:00
result[language] = prefix + translation.translations[language] + postfix
2022-01-25 21:55:51 +01:00
}
2022-05-01 22:58:59 +02:00
result["_context"] = translation.context
2022-01-25 21:55:51 +01:00
return result
}
2022-07-18 02:00:32 +02:00
function tr(translation: Translation) {
return {...translation.translations, "_context": translation.context}
2022-05-01 22:58:59 +02:00
}
2022-07-18 02:00:32 +02:00
function trs<T>(translation: TypedTranslation<T>, subs: T): object {
2022-05-01 22:58:59 +02:00
return {...translation.Subs(subs).translations, "_context": translation.context}
}
2022-01-26 21:40:38 +01:00
const result: LayerConfigJson = {
"id": "note_import_" + layer.id,
// By disabling the name, the import-layers won't pollute the filter view "name": t.layerName.Subs({title: layer.title.render}).translations,
2022-07-18 02:00:32 +02:00
"description": trs(t.description, {title: layer.title.render}),
"source": {
"osmTags": {
"and": [
"id~*"
]
},
2022-01-26 21:40:38 +01:00
"geoJson": "https://api.openstreetmap.org/api/0.6/notes.json?limit=10000&closed=" + this._includeClosedNotesDays + "&bbox={x_min},{y_min},{x_max},{y_max}",
"geoJsonZoomLevel": 10,
"maxCacheAge": 0
},
2022-01-26 20:47:08 +01:00
"minzoom": Math.min(12, layerJson.minzoom - 2),
"title": {
2022-07-18 02:00:32 +02:00
"render": trs(t.popupTitle, {title})
},
"calculatedTags": [
"_first_comment=feat.get('comments')[0].text.toLowerCase()",
"_trigger_index=(() => {const lines = feat.properties['_first_comment'].split('\\n'); const matchesMapCompleteURL = lines.map(l => l.match(\".*https://mapcomplete.osm.be/\\([a-zA-Z_-]+\\)\\(.html\\)?.*#import\")); const matchedIndexes = matchesMapCompleteURL.map((doesMatch, i) => [doesMatch !== null, i]).filter(v => v[0]).map(v => v[1]); return matchedIndexes[0] })()",
"_comments_count=feat.get('comments').length",
2022-01-25 21:55:51 +01:00
"_intro=(() => {const lines = feat.get('comments')[0].text.split('\\n'); lines.splice(feat.get('_trigger_index')-1, lines.length); return lines.filter(l => l !== '').join('<br/>');})()",
"_tags=(() => {let lines = feat.get('comments')[0].text.split('\\n').map(l => l.trim()); lines.splice(0, feat.get('_trigger_index') + 1); lines = lines.filter(l => l != ''); return lines.join(';');})()"
],
"isShown": {
2022-07-18 02:00:32 +02:00
and:
["_trigger_index~*",
{or: isShownIfAny}
]
},
"titleIcons": [
{
"render": "<a href='https://openstreetmap.org/note/{id}' target='_blank'><img src='./assets/svg/osm-logo-us.svg'></a>"
}
],
"tagRenderings": [
{
"id": "Intro",
2022-03-10 23:20:50 +01:00
render: "{_intro}"
},
{
"id": "conversation",
"render": "{visualize_note_comments(comments,1)}",
condition: "_comments_count>1"
},
{
"id": "import",
"render": importButton,
condition: "closed_at="
},
{
"id": "close_note_",
2022-01-25 21:55:51 +01:00
"render": embed(
2022-03-10 23:20:50 +01:00
"{close_note(", t.notFound.Subs({title}), ", ./assets/svg/close.svg, id, This feature does not exist, 18)}"),
condition: "closed_at="
},
{
"id": "close_note_mapped",
2022-03-10 23:20:50 +01:00
"render": embed("{close_note(", t.alreadyMapped.Subs({title}), ", ./assets/svg/duplicate.svg, id, Already mapped, 18)}"),
condition: "closed_at="
},
{
"id": "handled",
2022-05-01 22:58:59 +02:00
"render": tr(t.importHandled),
condition: "closed_at~*"
},
{
"id": "comment",
"render": "{add_note_comment()}"
},
{
"id": "add_image",
"render": "{add_image_to_note()}"
},
{
2022-07-18 02:00:32 +02:00
"id": "nearby_images",
render: tr(t.nearbyImagesIntro)
2022-07-18 02:00:32 +02:00
}
],
"mapRendering": [
{
"location": [
2022-01-21 03:57:49 +01:00
"point"
],
"icon": {
"render": "circle:white;help:black",
2022-01-26 21:40:38 +01:00
mappings: [{
if: {or: ["closed_at~*", "_imported=yes"]},
then: "circle:white;checkmark:black"
}]
},
"iconSize": "40,40,center"
}
]
}
2022-01-26 21:40:38 +01:00
return {
2022-02-10 23:16:14 +01:00
result
};
}
}