diff --git a/Logic/ImageProviders/WikidataImageProvider.ts b/Logic/ImageProviders/WikidataImageProvider.ts index 8576de5d9..ea333dfbd 100644 --- a/Logic/ImageProviders/WikidataImageProvider.ts +++ b/Logic/ImageProviders/WikidataImageProvider.ts @@ -3,6 +3,7 @@ import ImageProvider, {ProvidedImage} from "./ImageProvider"; import BaseUIElement from "../../UI/BaseUIElement"; import Svg from "../../Svg"; import {WikimediaImageProvider} from "./WikimediaImageProvider"; +import Wikidata from "../Web/Wikidata"; export class WikidataImageProvider extends ImageProvider { @@ -22,31 +23,18 @@ export class WikidataImageProvider extends ImageProvider { } public async ExtractUrls(key: string, value: string): Promise[]> { - const wikidataUrl = "https://www.wikidata.org/wiki/" - if (value.startsWith(wikidataUrl)) { - value = value.substring(wikidataUrl.length) - } - if(value.startsWith("http")){ - // Probably some random link in the image field - we skip it - return undefined - } - if (!value.startsWith("Q")) { - value = "Q" + value - } - const url = "https://www.wikidata.org/wiki/Special:EntityData/" + value + ".json"; - const response = await Utils.downloadJson(url) - const entity = response.entities[value]; - const commons = entity.sitelinks.commonswiki; + const entity = await Wikidata.LoadWikidataEntry(value) + + const allImages : Promise[] = [] // P18 is the claim 'depicted in this image' - const image = entity.claims.P18?.[0]?.mainsnak?.datavalue?.value; - const allImages = [] - if (image !== undefined) { - // We found a 'File://' - const promises = await WikimediaImageProvider.singleton.ExtractUrls(key, image) + for (const img of Array.from(entity.claims.get("P18") ?? [])) { + const promises = await WikimediaImageProvider.singleton.ExtractUrls(undefined, img) allImages.push(...promises) } + + const commons =entity.wikisites.get("commons") if (commons !== undefined) { - const promises = await WikimediaImageProvider.singleton.ExtractUrls(commons, image) + const promises = await WikimediaImageProvider.singleton.ExtractUrls(undefined , commons) allImages.push(...promises) } return allImages diff --git a/Logic/ImageProviders/WikimediaImageProvider.ts b/Logic/ImageProviders/WikimediaImageProvider.ts index 20f69a7e4..0d2f6c1d0 100644 --- a/Logic/ImageProviders/WikimediaImageProvider.ts +++ b/Logic/ImageProviders/WikimediaImageProvider.ts @@ -44,7 +44,6 @@ export class WikimediaImageProvider extends ImageProvider { if (continueParameter !== undefined) { url = `${url}&cmcontinue=${continueParameter}`; } - console.debug("Loading a wikimedia category: ", url) const response = await Utils.downloadJson(url) const members = response.query?.categorymembers ?? []; const imageOverview: string[] = members.map(member => member.title); @@ -135,8 +134,7 @@ export class WikimediaImageProvider extends ImageProvider { } public async ExtractUrls(key: string, value: string): Promise[]> { - - if(key !== this.commons_key && !value.startsWith(WikimediaImageProvider.commonsPrefix)){ + if(key !== undefined && key !== this.commons_key && !value.startsWith(WikimediaImageProvider.commonsPrefix)){ return [] } diff --git a/Logic/Web/Wikidata.ts b/Logic/Web/Wikidata.ts new file mode 100644 index 000000000..3b60a2e6b --- /dev/null +++ b/Logic/Web/Wikidata.ts @@ -0,0 +1,89 @@ +import {Utils} from "../../Utils"; + + +export interface WikidataResponse { + + id: string, + labels: Map, + descriptions: Map, + claims: Map>, + wikisites: Map + commons: string +} + +/** + * Utility functions around wikidata + */ +export default class Wikidata { + + private static ParseResponse(entity: any): WikidataResponse { + const labels = new Map() + for (const labelName in entity.labels) { + // The labelname is the language code + labels.set(labelName, entity.labels[labelName].value) + } + + const descr = new Map() + for (const labelName in entity.descriptions) { + // The labelname is the language code + descr.set(labelName, entity.descriptions[labelName].value) + } + + const sitelinks = new Map(); + for (const labelName in entity.sitelinks) { + // labelName is `${language}wiki` + const language = labelName.substring(0, labelName.length - 4) + const title = entity.sitelinks[labelName].title + sitelinks.set(language, title) + } + + const commons = sitelinks.get("commons") + sitelinks.delete("commons") + + const claims = new Map>(); + for (const claimId of entity.claims) { + + const claimsList: any[] = entity.claims[claimId] + const values = new Set() + for (const claim of claimsList) { + const value = claim.mainsnak.datavalue.value; + values.add(value) + } + claims.set(claimId, values); + } + + return { + claims: claims, + descriptions: descr, + id: entity.id, + labels: labels, + wikisites: sitelinks, + commons: commons + } + } + + /** + * Loads a wikidata page + * @returns the entity of the given value + */ + public static async LoadWikidataEntry(value: string | number): Promise { + const wikidataUrl = "https://www.wikidata.org/wiki/" + if (typeof value === "number") { + value = "Q" + value + } + if (value.startsWith(wikidataUrl)) { + value = value.substring(wikidataUrl.length) + } + if (value.startsWith("http")) { + // Probably some random link in the image field - we skip it + return undefined + } + if (!value.startsWith("Q")) { + value = "Q" + value + } + const url = "https://www.wikidata.org/wiki/Special:EntityData/" + value + ".json"; + const response = await Utils.downloadJson(url) + return Wikidata.ParseResponse(response.entities[value]); + } + +} \ No newline at end of file diff --git a/Logic/Web/Wikipedia.ts b/Logic/Web/Wikipedia.ts index 7c1260a28..34bde4fc5 100644 --- a/Logic/Web/Wikipedia.ts +++ b/Logic/Web/Wikipedia.ts @@ -2,7 +2,8 @@ * Some usefull utility functions around the wikipedia API */ import {Utils} from "../../Utils"; -import WikipediaBox from "../../UI/WikipediaBox"; +import {UIEventSource} from "../UIEventSource"; +import Wikidata from "./Wikidata"; export default class Wikipedia { @@ -14,22 +15,36 @@ export default class Wikipedia { private static readonly classesToRemove = [ "shortdescription", "sidebar", - "infobox", + "infobox","infobox_v2", + "noprint", + "ambox", "mw-editsection", + "mw-selflink", "hatnote" // Often redirects ] - public static async GetArticle(options: { + private static readonly _cache = new Map>() + + public static GetArticle(options: { pageName: string, - language?: "en" | string, - section?: number, + language?: "en" | string}): UIEventSource<{ success: string } | { error: any }>{ + const key = (options.language ?? "en")+":"+options.pageName + const cached = Wikipedia._cache.get(key) + if(cached !== undefined){ + return cached + } + const v = UIEventSource.FromPromiseWithErr(Wikipedia.GetArticleAsync(options)) + Wikipedia._cache.set(key, v) + return v; + } + + public static async GetArticleAsync(options: { + pageName: string, + language?: "en" | string }): Promise { - let section = "" - if (options.section !== undefined) { - section = "§ion=" + options.section - } - const url = `https://${options.language ?? "en"}.wikipedia.org/w/api.php?action=parse${section}&format=json&origin=*&prop=text&page=` + options.pageName + const language = options.language ?? "en" + const url = `https://${language}.wikipedia.org/w/api.php?action=parse&format=json&origin=*&prop=text&page=` + options.pageName const response = await Utils.downloadJson(url) const html = response["parse"]["text"]["*"]; @@ -43,7 +58,19 @@ export default class Wikipedia { toRemoveElement.parentElement?.removeChild(toRemoveElement) } } - return content.innerHTML; + + const links = Array.from(content.getElementsByTagName("a")) + + console.log("Links are", links) + // Rewrite relative links to absolute links + open them in a new tab + links.filter(link => link.getAttribute("href")?.startsWith("/") ?? false). + forEach(link => { + link.target = '_blank' + // note: link.getAttribute("href") gets the textual value, link.href is the rewritten version which'll contain the host for relative paths + link.href = `https://${language}.wikipedia.org${link.getAttribute("href")}`; + }) + + return content.innerHTML } } \ No newline at end of file diff --git a/UI/Base/Loading.ts b/UI/Base/Loading.ts index 03d0e3fdc..c2636c1f3 100644 --- a/UI/Base/Loading.ts +++ b/UI/Base/Loading.ts @@ -9,9 +9,9 @@ export default class Loading extends Combine { const t = Translations.T(msg ) ?? Translations.t.general.loading.Clone(); t.SetClass("pl-2") super([ - Svg.loading_svg().SetClass("animate-spin").SetStyle("width: 1.5rem; height: 1.5rem; margin-bottom: 4px;"), + Svg.loading_svg().SetClass("animate-spin").SetStyle("width: 1.5rem; height: 1.5rem;"), t ]) - this.SetClass("flex m-1") + this.SetClass("flex p-1") } } \ No newline at end of file diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index b8771aa7a..a0244a33f 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -225,7 +225,6 @@ export default class ShowDataLayer { popup.setContent(`
Popup for ${feature.properties.id} ${feature.geometry.type}
`) leafletLayer.on("popupopen", () => { - console.trace(`Opening the popup for ${feature.properties.id} ${feature.geometry.type}`) if (infobox === undefined) { const tags = State.state.allElements.getEventSourceById(feature.properties.id); infobox = new FeatureInfoBox(tags, layer); @@ -239,7 +238,9 @@ export default class ShowDataLayer { } infobox.AttachTo(id) infobox.Activate(); - State.state.selectedElement.setData(feature) + if (State.state?.selectedElement?.data?.properties?.id !== feature.properties.id) { + State.state.selectedElement.setData(feature) + } }); diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 5801e1d88..de45911db 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -26,6 +26,7 @@ import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSou import ShowDataMultiLayer from "./ShowDataLayer/ShowDataMultiLayer"; import Minimap from "./Base/Minimap"; import AllImageProviders from "../Logic/ImageProviders/AllImageProviders"; +import WikipediaBox from "./WikipediaBox"; export interface SpecialVisualization { funcName: string, @@ -84,6 +85,20 @@ export default class SpecialVisualizations { return new ImageUploadFlow(tags, args[0]) } }, + { + funcName: "wikipedia", + docs: "A box showing the corresponding wikipedia article - based on the wikidata tag", + args: [ + { + name: "keyToShowWikipediaFor", + doc: "Use the wikidata entry from this key to show the wikipedia article for", + defaultValue: "wikidata" + } + ], + example: "`{wikipedia()}` is a basic example, `{wikipedia(name:etymology:wikidata)}` to show the wikipedia page of whom the feature was named after. Also remember that these can be styled, e.g. `{wikipedia():max-height: 10rem}` to limit the height", + constr: (_, tagsSource, args) => + new WikipediaBox( tagsSource.map(tags => tags[args[0]])) + }, { funcName: "minimap", docs: "A small map showing the selected feature. Note that no styling is applied, wrap this in a div", @@ -153,10 +168,10 @@ export default class SpecialVisualizations { } }) - new ShowDataMultiLayer( + new ShowDataMultiLayer( { leafletMap: minimap["leafletMap"], - enablePopups : false, + enablePopups: false, zoomToFeatures: true, layers: State.state.filteredLayers, features: new StaticFeatureSource(featuresToShow, true) @@ -351,17 +366,17 @@ export default class SpecialVisualizations { const key = args [0] return new VariableUiElement( tagSource.map(tags => tags[key]).map(value => { - if (value === undefined) { - return undefined - } - const allUnits = [].concat(...state.layoutToUse.layers.map(lyr => lyr.units)) - const unit = allUnits.filter(unit => unit.isApplicableToKey(key))[0] - if (unit === undefined) { - return value; - } - return unit.asHumanLongValue(value); + if (value === undefined) { + return undefined + } + const allUnits = [].concat(...state.layoutToUse.layers.map(lyr => lyr.units)) + const unit = allUnits.filter(unit => unit.isApplicableToKey(key))[0] + if (unit === undefined) { + return value; + } + return unit.asHumanLongValue(value); - }) + }) ) } }, @@ -411,8 +426,8 @@ There are also some technicalities in your theme to keep in mind: } return kv }) - const rewrittenTags : UIEventSource = tagSource.map(tags => { - const newTags : Tag [] = [] + const rewrittenTags: UIEventSource = tagSource.map(tags => { + const newTags: Tag [] = [] for (const [key, value] of tgsSpec) { if (value.startsWith('$')) { const origKey = value.substring(1) @@ -446,9 +461,9 @@ There are also some technicalities in your theme to keep in mind: [ new Title(viz.funcName, 3), viz.docs, - new Table(["name", "default", "description"], + viz.args.length > 0 ? new Table(["name", "default", "description"], viz.args.map(arg => [arg.name, arg.defaultValue ?? "undefined", arg.doc]) - ), + ) : undefined, new Title("Example usage", 4), new FixedUiElement( viz.example ?? "`{" + viz.funcName + "(" + viz.args.map(arg => arg.defaultValue).join(",") + ")}`" diff --git a/UI/WikipediaBox.ts b/UI/WikipediaBox.ts index c3ab0fab7..f5c582ed2 100644 --- a/UI/WikipediaBox.ts +++ b/UI/WikipediaBox.ts @@ -8,46 +8,109 @@ import BaseUIElement from "./BaseUIElement"; import Title from "./Base/Title"; import Translations from "./i18n/Translations"; import Svg from "../Svg"; +import Wikidata from "../Logic/Web/Wikidata"; +import Locale from "./i18n/Locale"; -export default class WikipediaBox extends Combine{ - - constructor(options: { - pagename: string, - language: string - }) { - - const htmlContent = UIEventSource.FromPromiseWithErr(Wikipedia.GetArticle({ - pageName: options.pagename, - language: options.language, - removeInfoBoxes: true - })) - - const contents : UIEventSource = htmlContent.map(htmlContent => { - if(htmlContent === undefined){ - // Still loading - return new Loading("Loading wikipedia page").SetClass("p-4") - } - if(htmlContent["success"] !== undefined){ - return new FixedUiElement(htmlContent["success"]).SetClass("wikipedia-article") - } - if(htmlContent["error"]){ - return new FixedUiElement(htmlContent["error"]).SetClass("alert p-4") - } - - return undefined - - }) - - const scrollable = new Combine([new VariableUiElement(contents).SetClass("block pl-6 pt-2")]) - .SetClass("block overflow-auto normal-background rounded-lg") - super([ - new Combine([Svg.wikipedia_svg().SetStyle("width: 1.5rem").SetClass("mr-3"), - new Title(Translations.t.general.wikipedia.wikipediaboxTitle, 2)]).SetClass("flex"), - scrollable]) - - this - .SetClass("block rounded-xl subtle-background m-1 p-2 flex flex-col") +export default class WikipediaBox extends Combine { + + private static async ExtractWikiPages(wikidata): Promise> { + return (await Wikidata.LoadWikidataEntry(wikidata)).wikisites } + private static _cache = new Map() + + constructor(wikidataId: string | UIEventSource) { + const wp = Translations.t.general.wikipedia; + if(typeof wikidataId === "string"){ + wikidataId = new UIEventSource(wikidataId) + } + + const knownPages = new UIEventSource<{success:Map}|{error:any}>(undefined) + + wikidataId.addCallbackAndRunD(wikidataId => { + WikipediaBox.ExtractWikiPages(wikidataId).then(pages => { + knownPages.setData({success:pages}) + }).catch(err=> { + knownPages.setData({error: err}) + }) + }) + + const cachedPages = new Map() + + const contents = new VariableUiElement( + knownPages.map(pages => { + + if (pages === undefined) { + return new Loading(wp.loading.Clone()) + } + if (pages["error"] !== undefined) { + return wp.failed.Clone().SetClass("alert p-4") + } + const dict: Map = pages["success"] + + const preferredLanguage = [Locale.language.data, "en", Array.from(dict.keys())[0]] + let language + let pagetitle; + let i = 0 + do { + language = preferredLanguage[i] + pagetitle = dict.get(language) + i++; + if(i >= preferredLanguage.length){ + return wp.noWikipediaPage.Clone() + } + } while (pagetitle === undefined) + + if(cachedPages.has(language)){ + return cachedPages.get(language) + } + + const page = WikipediaBox.createContents(pagetitle, language); + cachedPages.set(language, page) + return page + }, [Locale.language]) + ).SetClass("overflow-auto normal-background rounded-lg") + + + super([ + new Combine([Svg.wikipedia_ui().SetStyle("width: 1.5rem").SetClass("mr-3"), + new Title(Translations.t.general.wikipedia.wikipediaboxTitle.Clone(), 2)]).SetClass("flex"), + contents]) + + this + .SetClass("block rounded-xl subtle-background m-1 p-2 flex flex-col") + } + + /** + * Returns the actual content in a scrollable way + * @param pagename + * @param language + * @private + */ + private static createContents(pagename: string, language: string): BaseUIElement { + const htmlContent = Wikipedia.GetArticle({ + pageName: pagename, + language: language + }) + const wp = Translations.t.general.wikipedia + const contents: UIEventSource = htmlContent.map(htmlContent => { + if (htmlContent === undefined) { + // Still loading + return new Loading(wp.loading.Clone()) + } + if (htmlContent["success"] !== undefined) { + return new FixedUiElement(htmlContent["success"]).SetClass("wikipedia-article") + } + if (htmlContent["error"]) { + return wp.failed.Clone().SetClass("alert p-4") + } + + return undefined + }) + + return new Combine([new VariableUiElement(contents).SetClass("block pl-6 pt-2")]) + .SetClass("block") + } + } \ 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 c6fb976af..29d2e56da 100644 --- a/assets/layers/nature_reserve/nature_reserve.json +++ b/assets/layers/nature_reserve/nature_reserve.json @@ -392,7 +392,8 @@ } ], "id": "Surface area" - } + }, + "wikipedia" ], "wayHandling": 2, "icon": { diff --git a/assets/layers/observation_tower/observation_tower.json b/assets/layers/observation_tower/observation_tower.json index fa9a00262..8443b1213 100644 --- a/assets/layers/observation_tower/observation_tower.json +++ b/assets/layers/observation_tower/observation_tower.json @@ -122,7 +122,8 @@ }, "id": "Payment methods" }, - "wheelchair-access" + "wheelchair-access", + "wikipedia" ], "wayHandling": 1, "icon": { diff --git a/assets/svg/loading.svg b/assets/svg/loading.svg index d7202fda4..27dc3ac6c 100644 --- a/assets/svg/loading.svg +++ b/assets/svg/loading.svg @@ -5,12 +5,17 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" - fill="none" - viewBox="0 0 25 25" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + viewBox="0 0 24.022156 24.021992" version="1.1" - id="svg6"> + id="svg9" + sodipodi:docname="loading.svg" + inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" + width="24.022156" + height="24.021992"> + id="metadata13"> @@ -21,18 +26,53 @@ + - + id="defs4"> + + + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:3.26200151;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.26635515" + id="path821" + sodipodi:type="arc" + sodipodi:cx="12.010992" + sodipodi:cy="12.010992" + sodipodi:rx="10.379992" + sodipodi:ry="10.379992" + sodipodi:start="0" + sodipodi:end="6.2828013" + sodipodi:open="true" + d="M 22.390984,12.010992 A 10.379992,10.379992 0 0 1 12.011989,22.390984 10.379992,10.379992 0 0 1 1.6310007,12.012985 10.379992,10.379992 0 0 1 12.008003,1.6310009 10.379992,10.379992 0 0 1 22.390983,12.007006" /> + diff --git a/assets/tagRenderings/questions.json b/assets/tagRenderings/questions.json index 19a2babaa..509749aa0 100644 --- a/assets/tagRenderings/questions.json +++ b/assets/tagRenderings/questions.json @@ -2,6 +2,9 @@ "images": { "render": "{image_carousel()}{image_upload()}" }, + "wikipedia": { + "render": "{wikipedia():max-height:25rem}" + }, "reviews": { "render": "{reviews()}" }, diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css index 08e6fd3d2..adb7b1ee9 100644 --- a/css/index-tailwind-output.css +++ b/css/index-tailwind-output.css @@ -812,10 +812,6 @@ video { margin: 0px; } -.m-4 { - margin: 1rem; -} - .m-2 { margin: 0.5rem; } @@ -824,8 +820,8 @@ video { margin: 0.75rem; } -.m-6 { - margin: 1.5rem; +.m-4 { + margin: 1rem; } .my-2 { @@ -852,8 +848,8 @@ video { margin-left: 0.75rem; } -.ml-2 { - margin-left: 0.5rem; +.mr-3 { + margin-right: 0.75rem; } .mb-2 { @@ -892,6 +888,10 @@ video { margin-top: 0px; } +.ml-2 { + margin-left: 0.5rem; +} + .mt-4 { margin-top: 1rem; } @@ -912,18 +912,10 @@ video { margin-bottom: 0.25rem; } -.mr-3 { - margin-right: 0.75rem; -} - .mb-4 { margin-bottom: 1rem; } -.ml-4 { - margin-left: 1rem; -} - .box-border { box-sizing: border-box; } @@ -1794,8 +1786,6 @@ svg, img { box-sizing: content-box; width: 100%; height: 100%; - display: unset; - vertical-align: unset; } .mapcontrol svg path { diff --git a/css/wikipedia.css b/css/wikipedia.css index 6bc812f20..99d25c437 100644 --- a/css/wikipedia.css +++ b/css/wikipedia.css @@ -1,7 +1,7 @@ /* This stylesheet reimplements a few classes from wikipedia to show their articles prettily */ .wikipedia-article { - font-family: sans-serif; + font-family: sans-serif !important; } .wikipedia-article .tright { @@ -9,6 +9,12 @@ clear: right; } +.wikipedia-article svg, img { + width: unset; + height: unset; + display: unset; +} + .wikipedia-article .thumb { background: var(--subtle-detail-color); margin: 1rem; @@ -17,15 +23,17 @@ border-radius: 0.5rem; } -.wikipedia-article a { - color: #0645ad; - background: none; +.wikipedia-article a:hover a:focus { + text-decoration: underline !important; } -.wikipedia-article a:hover a:focus { - text-decoration: underline; +.wikipedia-article a { + color: #0645ad !important; + background: none !important; + text-decoration: none; } + .wikipedia-article p { margin-bottom: 0.5rem; } diff --git a/index.css b/index.css index 3c590d974..d5cd06476 100644 --- a/index.css +++ b/index.css @@ -91,8 +91,6 @@ svg, img { box-sizing: content-box; width: 100%; height: 100%; - display: unset; - vertical-align: unset; } .mapcontrol svg path { diff --git a/index.html b/index.html index 777dd921c..a65572209 100644 --- a/index.html +++ b/index.html @@ -14,6 +14,7 @@ + diff --git a/langs/en.json b/langs/en.json index 451e5c227..306ea8e80 100644 --- a/langs/en.json +++ b/langs/en.json @@ -218,7 +218,10 @@ "error_loading": "Could not load the histogram" }, "wikipedia": { - "wikipediaboxTitle": "Wikipedia" + "wikipediaboxTitle": "Wikipedia", + "failed":"Loading the wikipedia entry failed", + "loading": "Loading Wikipedia...", + "noWikipediaPage": "This wikidata item has no corresponding wikipedia page yet." } }, "favourite": { diff --git a/langs/themes/en.json b/langs/themes/en.json index b62768bef..9dcb4b54a 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -743,6 +743,22 @@ "description": "On this map, publicly accessible drinking water spots are shown and can be easily added", "title": "Drinking Water" }, + "etymology": { + "description": "On this map, you can see what an object is named after. The streets, buildings, ... come from OpenStreetMap which got linked with Wikidata. The information comes from Wpikipedia.", + "layers": { + "0": { + "description": "All objects which have an etymology known", + "name": "Has etymolgy", + "tagRenderings": { + "simple etymology": { + "render": "Named after {name:etymology}" + } + } + } + }, + "shortDescription": "What is the origin of a toponym?", + "title": "Open Etymology Map" + }, "facadegardens": { "description": "Facade gardens, green facades and trees in the city not only bring peace and quiet, but also a more beautiful city, greater biodiversity, a cooling effect and better air quality.
Klimaan VZW and Mechelen Klimaatneutraal want to map existing and new facade gardens as an example for people who want to build their own garden or for city walkers who love nature.
More info about the project at klimaan.be.", "layers": { diff --git a/langs/themes/nl.json b/langs/themes/nl.json index e3220df4d..409d96fea 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -624,6 +624,22 @@ "description": "Op deze kaart staan publiek toegankelijke drinkwaterpunten en kan je makkelijk een nieuw drinkwaterpunt toevoegen", "title": "Drinkwaterpunten" }, + "etymology": { + "description": "Op deze kaart zie je waar een plaats naar is vernoemd. De straten, gebouwen, ... komen uit OpenStreetMap, waar een link naar Wikidata werd gelegd. De informatie komt uit wikipedia.", + "layers": { + "0": { + "description": "Alle lagen met een gelinkt etymology", + "name": "Heeft etymology info", + "tagRenderings": { + "simple etymology": { + "render": "Vernoemd naar {name:etymology}" + } + } + } + }, + "shortDescription": "Wat is de oorsprong van een plaatsnaam?", + "title": "Open Etymology-kaart" + }, "facadegardens": { "description": "Ontharde voortuintjes, groene gevels en bomen ín de stad brengen naast rust ook een mooiere stad, een grotere biodiversiteit, een verkoelend effect en een betere luchtkwaliteit.
Klimaan VZW en 'Mechelen Klimaatneutraal' willen met het project Klim(t)aan je Gevel bestaande en nieuwe geveltuintjes in kaart brengen als voorbeeld voor mensen zelf een tuintje willen aanleggen of voor stadwandelaars die houden van de natuur.
Meer info over het project op klimaan.be.", "layers": { diff --git a/test.ts b/test.ts index 49c4379e0..0c746b6eb 100644 --- a/test.ts +++ b/test.ts @@ -1,14 +1,8 @@ -import Wikipedia from "./Logic/Web/Wikipedia"; -import {FixedUiElement} from "./UI/Base/FixedUiElement"; +import Wikidata from "./Logic/Web/Wikidata"; import WikipediaBox from "./UI/WikipediaBox"; -import Loading from "./UI/Base/Loading"; +import Locale from "./UI/i18n/Locale"; +import LanguagePicker from "./UI/LanguagePicker"; - -new WikipediaBox({ - pagename: "Poertoren", - language: "nl" -}) - .SetStyle("max-height: 20rem;") +new WikipediaBox("Q177").SetStyle("max-height: 25rem") .AttachTo("maindiv") - - +LanguagePicker.CreateLanguagePicker(["en","nl","fr","de"]).AttachTo("extradiv") \ No newline at end of file