diff --git a/Logic/Osm/Overpass.ts b/Logic/Osm/Overpass.ts index 79fc2255a0..e9546566d8 100644 --- a/Logic/Osm/Overpass.ts +++ b/Logic/Osm/Overpass.ts @@ -3,7 +3,7 @@ import RelationsTracker from "./RelationsTracker"; import {Utils} from "../../Utils"; import {UIEventSource} from "../UIEventSource"; import {BBox} from "../BBox"; -import osmtogeojson from "osmtogeojson"; +import * as osmtogeojson from "osmtogeojson"; /** * Interfaces overpass to get all the latest data @@ -52,7 +52,8 @@ export class Overpass { } self._relationTracker.RegisterRelations(json) - const geojson = osmtogeojson.toGeoJSON(json); + console.warn("OSMTOGEOJSON:", osmtogeojson) + const geojson = osmtogeojson.default(json); const osmTime = new Date(json.osm3s.timestamp_osm_base); return [geojson, osmTime]; } diff --git a/Logic/Tags/And.ts b/Logic/Tags/And.ts index 78b6d47106..d3a1cbac64 100644 --- a/Logic/Tags/And.ts +++ b/Logic/Tags/And.ts @@ -117,4 +117,10 @@ export class And extends TagsFilter { } return result; } + + AsJson() { + return { + and: this.and.map(a => a.AsJson()) + } + } } \ No newline at end of file diff --git a/Logic/Tags/ComparingTag.ts b/Logic/Tags/ComparingTag.ts index d49dd8bb1d..c09b19fcce 100644 --- a/Logic/Tags/ComparingTag.ts +++ b/Logic/Tags/ComparingTag.ts @@ -38,5 +38,9 @@ export default class ComparingTag implements TagsFilter { usedKeys(): string[] { return [this._key]; } + + AsJson() { + return this.asHumanString(false, false, {}) + } } \ No newline at end of file diff --git a/Logic/Tags/Or.ts b/Logic/Tags/Or.ts index 254e193e9b..d7d0788b40 100644 --- a/Logic/Tags/Or.ts +++ b/Logic/Tags/Or.ts @@ -65,6 +65,12 @@ export class Or extends TagsFilter { } return result; } + + AsJson() { + return { + or: this.or.map(o => o.AsJson()) + } + } } diff --git a/Logic/Tags/RegexTag.ts b/Logic/Tags/RegexTag.ts index 8e120cb718..f747156b61 100644 --- a/Logic/Tags/RegexTag.ts +++ b/Logic/Tags/RegexTag.ts @@ -109,4 +109,8 @@ export class RegexTag extends TagsFilter { console.error("Cannot export regex tag to asChange; ", this.key, this.value) return [] } + + AsJson() { + return this.asHumanString() + } } \ No newline at end of file diff --git a/Logic/Tags/SubstitutingTag.ts b/Logic/Tags/SubstitutingTag.ts index f776d0a407..9e7fb2effa 100644 --- a/Logic/Tags/SubstitutingTag.ts +++ b/Logic/Tags/SubstitutingTag.ts @@ -12,7 +12,8 @@ import {TagsFilter} from "./TagsFilter"; export default class SubstitutingTag implements TagsFilter { private readonly _key: string; private readonly _value: string; -private readonly _invert: boolean + private readonly _invert: boolean + constructor(key: string, value: string, invert = false) { this._key = key; this._value = value; @@ -59,11 +60,17 @@ private readonly _invert: boolean } asChange(properties: any): { k: string; v: string }[] { - if(this._invert){throw "An inverted substituting tag can not be used to create a change"} + if (this._invert) { + throw "An inverted substituting tag can not be used to create a change" + } const v = SubstitutingTag.substituteString(this._value, properties); if (v.match(/{.*}/) !== null) { throw "Could not calculate all the substitutions: still have " + v } return [{k: this._key, v: v}]; } + + AsJson() { + return this._key + (this._invert ? '!' : '') + "=" + this._value + } } \ No newline at end of file diff --git a/Logic/Tags/Tag.ts b/Logic/Tags/Tag.ts index 90b2c8841a..bdf3235adf 100644 --- a/Logic/Tags/Tag.ts +++ b/Logic/Tags/Tag.ts @@ -83,4 +83,8 @@ export class Tag extends TagsFilter { asChange(properties: any): { k: string; v: string }[] { return [{k: this.key, v: this.value}]; } + + AsJson() { + return this.asHumanString(false, false) + } } \ No newline at end of file diff --git a/Logic/Tags/TagsFilter.ts b/Logic/Tags/TagsFilter.ts index cffaf99ca3..687a614215 100644 --- a/Logic/Tags/TagsFilter.ts +++ b/Logic/Tags/TagsFilter.ts @@ -8,7 +8,7 @@ export abstract class TagsFilter { abstract matchesProperties(properties: any): boolean; - abstract asHumanString(linkToWiki: boolean, shorten: boolean, properties: any) : string; + abstract asHumanString(linkToWiki: boolean, shorten: boolean, properties: any): string; abstract usedKeys(): string[]; @@ -20,4 +20,5 @@ export abstract class TagsFilter { */ abstract asChange(properties: any): { k: string, v: string }[] + abstract AsJson() ; } \ No newline at end of file diff --git a/Models/ThemeConfig/Conversion/CreateNoteImportLayer.ts b/Models/ThemeConfig/Conversion/CreateNoteImportLayer.ts new file mode 100644 index 0000000000..a7c50bf646 --- /dev/null +++ b/Models/ThemeConfig/Conversion/CreateNoteImportLayer.ts @@ -0,0 +1,118 @@ +import {Conversion, DesugaringContext} from "./LegacyJsonConvert"; +import LayerConfig from "../LayerConfig"; +import {LayerConfigJson} from "../Json/LayerConfigJson"; +import Translations from "../../../UI/i18n/Translations"; +import {TagsFilter} from "../../../Logic/Tags/TagsFilter"; +import {Tag} from "../../../Logic/Tags/Tag"; +import {And} from "../../../Logic/Tags/And"; + +export default class CreateNoteImportLayer extends Conversion { + + constructor() { + 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", + ].join("\n\n"), []) + } + + convert(state: DesugaringContext, layer: LayerConfig, context: string): { result: LayerConfigJson; errors: string[]; warnings: string[] } { + const errors = [] + const warnings = [] + const t = Translations.t.importLayer; + + const possibleTags: TagsFilter[] = layer.presets.map(p => new And(p.tags)) + + const result : LayerConfigJson = { + "id": "note_import_"+layer.id, + "name": t.layerName.Subs({title: layer.title.render}).translations, + "description": t.description.Subs({title: layer.title.render}).translations, + "source": { + "osmTags": { + "and": [ + "id~*" + ] + }, + "geoJson": "https://api.openstreetmap.org/api/0.6/notes.json?closed=0&bbox={x_min},{y_min},{x_max},{y_max}", + "geoJsonZoomLevel": 12, + "maxCacheAge": 0 + }, + "minzoom": 10, + "title": { + "render": t.popupTitle.Subs({title: layer.presets[0].title}).translations + }, + "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] })()", + "_intro:=(() => {const lines = feat.properties['_first_comment'].split('\\n'); lines.splice(feat.get('_trigger_index')-1, lines.length); return lines.map(l => l == '' ? '
' : l).join('');})()", + "_tags:=(() => {let lines = feat.properties['_first_comment'].split('\\n').map(l => l.trim()); lines.splice(0, feat.get('_trigger_index') + 1); lines = lines.filter(l => l != ''); return lines.join(';');})()" + ], + "isShown": { + "render": "no", + "mappings": [ + { + "if": {and: + ["_trigger_index~*", + {or: possibleTags.map(tf => tf.AsJson())} + ]}, + "then": "yes" + } + ] + }, + "titleIcons": [ + { + "render": "" + } + ], + "tagRenderings": [ + { + "id": "conversation", + "render": "{visualize_note_comments(comments,1)}" + }, + { + "id": "Intro", + "render": "{_intro}" + }, + { + "id": "import", + "render": "{import_button(public_bookcase, _tags, There might be a public bookcase here,./assets/svg/addSmall.svg,,,id)}" + }, + { + "id": "close_note_", + "render": "{close_note(Does not exist
, ./assets/svg/close.svg, id, This feature does not exist)}" + }, + { + "id": "close_note_mapped", + "render": "{close_note(Already mapped, ./assets/svg/checkmark.svg, id, Already mapped)}" + }, + { + "id": "comment", + "render": "{add_note_comment()}" + }, + { + "id": "add_image", + "render": "{add_image_to_note()}" + } + ], + "mapRendering": [ + { + "location": [ + "point", + "centroid" + ], + "icon": { + "render": "teardrop:#3333cc" + }, + "iconSize": "40,40,bottom" + } + ] + } + + + return { + result, + errors, warnings + }; + } + + +} \ No newline at end of file diff --git a/Models/ThemeConfig/Conversion/LegacyJsonConvert.ts b/Models/ThemeConfig/Conversion/LegacyJsonConvert.ts index e118251504..0f696560ca 100644 --- a/Models/ThemeConfig/Conversion/LegacyJsonConvert.ts +++ b/Models/ThemeConfig/Conversion/LegacyJsonConvert.ts @@ -14,7 +14,7 @@ export interface DesugaringContext { sharedLayers: Map } -abstract class Conversion { +export abstract class Conversion { protected readonly doc: string; public readonly modifiedAttributes: string[]; @@ -58,7 +58,7 @@ abstract class Conversion { } -abstract class DesugaringStep extends Conversion { +export abstract class DesugaringStep extends Conversion { } class OnEvery extends DesugaringStep { diff --git a/Utils.ts b/Utils.ts index 3971075cf1..3c1abd1dc6 100644 --- a/Utils.ts +++ b/Utils.ts @@ -262,7 +262,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be const date: Date = el; v = date.toISOString() } - + if (useLang !== undefined && v?.translations !== undefined) { v = v.translations[useLang] ?? v.translations["*"] ?? (v.textFor !== undefined ? v.textFor(useLang) : v); } diff --git a/langs/en.json b/langs/en.json index 942b9b93f1..6954256986 100644 --- a/langs/en.json +++ b/langs/en.json @@ -453,5 +453,10 @@ "noteLayerHasFilters": "Some notes might be hidden by a filter", "disableAllNoteFilters": "Disable all filters", "noteLayerDoEnable": "Enable the layer showing notes" + }, + "importLayer": { + "layerName": "Possible {title}", + "description": "A layer which imports entries for {title}", + "popupTitle": "Possible {title}" } } diff --git a/package.json b/package.json index 8f5207c72d..4f2e3fd66b 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "generate:polygon-features": "cd assets/ && wget https://raw.githubusercontent.com/tyrasd/osm-polygon-features/master/polygon-features.json --output-document=polygon-features.json", "generate:images": "ts-node scripts/generateIncludedImages.ts", "generate:translations": "ts-node scripts/generateTranslations.ts", + "watch:translations": "cd langs && ls | entr npm run generate:translations", "reset:translations": "ts-node scripts/generateTranslations.ts --ignore-weblate", "generate:layouts": "ts-node scripts/generateLayouts.ts", "generate:docs": "ts-node scripts/generateDocs.ts && ts-node scripts/generateTaginfoProjectFiles.ts",