forked from MapComplete/MapComplete
Styling tweak
Add mapillary link to nearby_images Fix licenses Add missing assets First version of nearby-images
This commit is contained in:
parent
a4f2fa63a5
commit
7559f9259b
52 changed files with 674 additions and 207 deletions
BIN
Docs/Misc/TreesWithWikidata.gif
Normal file
BIN
Docs/Misc/TreesWithWikidata.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 MiB |
|
@ -167,6 +167,7 @@ export default class DetermineLayout {
|
||||||
const raw = json;
|
const raw = json;
|
||||||
|
|
||||||
json = new FixImages(DetermineLayout._knownImages).convertStrict(json, "While fixing the images")
|
json = new FixImages(DetermineLayout._knownImages).convertStrict(json, "While fixing the images")
|
||||||
|
json.enableNoteImports = json.enableNoteImports ?? false;
|
||||||
json = new PrepareTheme(converState).convertStrict(json, "While preparing a dynamic theme")
|
json = new PrepareTheme(converState).convertStrict(json, "While preparing a dynamic theme")
|
||||||
console.log("The layoutconfig is ", json)
|
console.log("The layoutconfig is ", json)
|
||||||
|
|
||||||
|
|
|
@ -19,9 +19,19 @@ export default class AllImageProviders {
|
||||||
new GenericImageProvider(
|
new GenericImageProvider(
|
||||||
[].concat(...Imgur.defaultValuePrefix, ...WikimediaImageProvider.commonsPrefixes, ...Mapillary.valuePrefixes)
|
[].concat(...Imgur.defaultValuePrefix, ...WikimediaImageProvider.commonsPrefixes, ...Mapillary.valuePrefixes)
|
||||||
)
|
)
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
private static providersByName= {
|
||||||
|
"imgur": Imgur.singleton,
|
||||||
|
"mapillary": Mapillary.singleton,
|
||||||
|
"wikidata": WikidataImageProvider.singleton,
|
||||||
|
"wikimedia": WikimediaImageProvider.singleton
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byName(name: string){
|
||||||
|
return AllImageProviders.providersByName[name.toLowerCase()]
|
||||||
|
}
|
||||||
|
|
||||||
public static defaultKeys = [].concat(AllImageProviders.ImageAttributionSource.map(provider => provider.defaultKeyPrefixes))
|
public static defaultKeys = [].concat(AllImageProviders.ImageAttributionSource.map(provider => provider.defaultKeyPrefixes))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import Svg from "../../Svg";
|
||||||
import {Utils} from "../../Utils";
|
import {Utils} from "../../Utils";
|
||||||
import {LicenseInfo} from "./LicenseInfo";
|
import {LicenseInfo} from "./LicenseInfo";
|
||||||
import Constants from "../../Models/Constants";
|
import Constants from "../../Models/Constants";
|
||||||
|
import * as Console from "console";
|
||||||
|
|
||||||
export class Mapillary extends ImageProvider {
|
export class Mapillary extends ImageProvider {
|
||||||
|
|
||||||
|
@ -12,11 +13,49 @@ export class Mapillary extends ImageProvider {
|
||||||
public static readonly valuePrefixes = [Mapillary.valuePrefix, "http://mapillary.com", "https://mapillary.com", "http://www.mapillary.com", "https://www.mapillary.com"]
|
public static readonly valuePrefixes = [Mapillary.valuePrefix, "http://mapillary.com", "https://mapillary.com", "http://www.mapillary.com", "https://www.mapillary.com"]
|
||||||
defaultKeyPrefixes = ["mapillary", "image"]
|
defaultKeyPrefixes = ["mapillary", "image"]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that this is the same URL
|
||||||
|
* Ignores 'stp' parameter
|
||||||
|
*
|
||||||
|
* const a = "https://scontent-bru2-1.xx.fbcdn.net/m1/v/t6/An8xm5SGLt20ETziNqzhhBd8b8S5GHLiIu8N6BbyqHFohFAQoaJJPG8i5yQiSwjYmEqXSfVeoCmpiyBJICEkQK98JOB21kkJoBS8VdhYa-Ty93lBnznQyesJBtKcb32foGut2Hgt10hEMWJbE3dDgA?stp=s1024x768&ccb=10-5&oh=00_AT-ZGTXHzihoaQYBILmEiAEKR64z_IWiTlcAYq_D7Ka0-Q&oe=6278C456&_nc_sid=122ab1"
|
||||||
|
* const b = "https://scontent-bru2-1.xx.fbcdn.net/m1/v/t6/An8xm5SGLt20ETziNqzhhBd8b8S5GHLiIu8N6BbyqHFohFAQoaJJPG8i5yQiSwjYmEqXSfVeoCmpiyBJICEkQK98JOB21kkJoBS8VdhYa-Ty93lBnznQyesJBtKcb32foGut2Hgt10hEMWJbE3dDgA?stp=s256x192&ccb=10-5&oh=00_AT9BZ1Rpc9zbY_uNu92A_4gj1joiy1b6VtgtLIu_7wh9Bg&oe=6278C456&_nc_sid=122ab1"
|
||||||
|
* Mapillary.sameUrl(a, b) => true
|
||||||
|
*/
|
||||||
|
static sameUrl(a: string, b: string): boolean {
|
||||||
|
if (a === b) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
console.log("COmparing",a,b)
|
||||||
|
const aUrl = new URL(a)
|
||||||
|
const bUrl = new URL(b)
|
||||||
|
if (aUrl.host !== bUrl.host || aUrl.pathname !== bUrl.pathname) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let allSame = true;
|
||||||
|
aUrl.searchParams.forEach((value, key) => {
|
||||||
|
if (key === "stp") {
|
||||||
|
// This is the key indicating the image size on mapillary; we ignore it
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (value !== bUrl.searchParams.get(key)) {
|
||||||
|
allSame = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return allSame;
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
Console.debug("Could not compare ", a, "and", b, "due to", e)
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the correct key for API v4.0
|
* Returns the correct key for API v4.0
|
||||||
*/
|
*/
|
||||||
private static ExtractKeyFromURL(value: string): number {
|
private static ExtractKeyFromURL(value: string): number {
|
||||||
|
|
||||||
let key: string;
|
let key: string;
|
||||||
|
|
||||||
const newApiFormat = value.match(/https?:\/\/www.mapillary.com\/app\/\?pKey=([0-9]*)/)
|
const newApiFormat = value.match(/https?:\/\/www.mapillary.com\/app\/\?pKey=([0-9]*)/)
|
||||||
|
@ -24,6 +63,8 @@ export class Mapillary extends ImageProvider {
|
||||||
key = newApiFormat[1]
|
key = newApiFormat[1]
|
||||||
} else if (value.startsWith(Mapillary.valuePrefix)) {
|
} else if (value.startsWith(Mapillary.valuePrefix)) {
|
||||||
key = value.substring(0, value.lastIndexOf("?")).substring(value.lastIndexOf("/") + 1)
|
key = value.substring(0, value.lastIndexOf("?")).substring(value.lastIndexOf("/") + 1)
|
||||||
|
} else if (value.match("[0-9]*")) {
|
||||||
|
key = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const keyAsNumber = Number(key)
|
const keyAsNumber = Number(key)
|
||||||
|
|
|
@ -9,7 +9,8 @@ export default class ChangeTagAction extends OsmChangeAction {
|
||||||
private readonly _currentTags: any;
|
private readonly _currentTags: any;
|
||||||
private readonly _meta: { theme: string, changeType: string };
|
private readonly _meta: { theme: string, changeType: string };
|
||||||
|
|
||||||
constructor(elementId: string, tagsFilter: TagsFilter, currentTags: any, meta: {
|
constructor(elementId: string,
|
||||||
|
tagsFilter: TagsFilter, currentTags: any, meta: {
|
||||||
theme: string,
|
theme: string,
|
||||||
changeType: "answer" | "soft-delete" | "add-image" | string
|
changeType: "answer" | "soft-delete" | "add-image" | string
|
||||||
}) {
|
}) {
|
||||||
|
|
|
@ -53,6 +53,10 @@ export default class UserRelatedState extends ElementsState {
|
||||||
osmConfiguration: <'osm' | 'osm-test'>this.featureSwitchApiURL.data,
|
osmConfiguration: <'osm' | 'osm-test'>this.featureSwitchApiURL.data,
|
||||||
attemptLogin: options?.attemptLogin
|
attemptLogin: options?.attemptLogin
|
||||||
})
|
})
|
||||||
|
const translationMode = this.osmConnection.GetPreference("translation-mode").map(str => str === undefined ? undefined : str === "true", [], b => b === undefined ? undefined : b+"")
|
||||||
|
|
||||||
|
translationMode.syncWith(Locale.showLinkToWeblate)
|
||||||
|
|
||||||
this.isTranslator = this.osmConnection.userDetails.map(ud => {
|
this.isTranslator = this.osmConnection.userDetails.map(ud => {
|
||||||
if(!ud.loggedIn){
|
if(!ud.loggedIn){
|
||||||
return false;
|
return false;
|
||||||
|
@ -60,6 +64,7 @@ export default class UserRelatedState extends ElementsState {
|
||||||
const name= ud.name.toLowerCase().replace(/\s+/g, '')
|
const name= ud.name.toLowerCase().replace(/\s+/g, '')
|
||||||
return translators.contributors.some(c => c.contributor.toLowerCase().replace(/\s+/g, '') === name)
|
return translators.contributors.some(c => c.contributor.toLowerCase().replace(/\s+/g, '') === name)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.isTranslator.addCallbackAndRunD(ud => {
|
this.isTranslator.addCallbackAndRunD(ud => {
|
||||||
if(ud){
|
if(ud){
|
||||||
Locale.showLinkToWeblate.setData(true)
|
Locale.showLinkToWeblate.setData(true)
|
||||||
|
|
|
@ -163,6 +163,11 @@ export default class CreateNoteImportLayer extends Conversion<LayerConfigJson, L
|
||||||
{
|
{
|
||||||
"id": "add_image",
|
"id": "add_image",
|
||||||
"render": "{add_image_to_note()}"
|
"render": "{add_image_to_note()}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":"nearby_images",
|
||||||
|
render: tr(t.nearbyImagesIntro)
|
||||||
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"mapRendering": [
|
"mapRendering": [
|
||||||
|
|
|
@ -8,6 +8,7 @@ import Translations from "../../../UI/i18n/Translations";
|
||||||
import {Translation} from "../../../UI/i18n/Translation";
|
import {Translation} from "../../../UI/i18n/Translation";
|
||||||
import * as tagrenderingconfigmeta from "../../../assets/tagrenderingconfigmeta.json"
|
import * as tagrenderingconfigmeta from "../../../assets/tagrenderingconfigmeta.json"
|
||||||
import {AddContextToTranslations} from "./AddContextToTranslations";
|
import {AddContextToTranslations} from "./AddContextToTranslations";
|
||||||
|
import spec = Mocha.reporters.spec;
|
||||||
|
|
||||||
class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | { builtin: string | string[], override: any }, TagRenderingConfigJson[]> {
|
class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | { builtin: string | string[], override: any }, TagRenderingConfigJson[]> {
|
||||||
private readonly _state: DesugaringContext;
|
private readonly _state: DesugaringContext;
|
||||||
|
@ -302,11 +303,6 @@ export class ExpandRewrite<T> extends Conversion<T | RewritableConfigJson<T>, T[
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a 'special' translation into a regular translation which uses parameters
|
* Converts a 'special' translation into a regular translation which uses parameters
|
||||||
* E.g.
|
|
||||||
*
|
|
||||||
* const tr = <TagRenderingJson> {
|
|
||||||
* "special":
|
|
||||||
* }
|
|
||||||
*/
|
*/
|
||||||
export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
|
export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -330,6 +326,11 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
|
||||||
* const r = RewriteSpecial.convertIfNeeded(spec, [], "test")
|
* const r = RewriteSpecial.convertIfNeeded(spec, [], "test")
|
||||||
* r // => {"en": "{image_upload(,Add a picture to this object)}", "nl": "{image_upload(,Voeg een afbeelding toe)}" }
|
* r // => {"en": "{image_upload(,Add a picture to this object)}", "nl": "{image_upload(,Voeg een afbeelding toe)}" }
|
||||||
*
|
*
|
||||||
|
* // should handle special case with a prefix and postfix
|
||||||
|
* const spec = {"special": {"type":"image_upload" }, before: {"en": "PREFIX "}, after: {"en": " POSTFIX", nl: " Achtervoegsel"} }
|
||||||
|
* const r = RewriteSpecial.convertIfNeeded(spec, [], "test")
|
||||||
|
* r // => {"en": "PREFIX {image_upload(,)} POSTFIX", "nl": "PREFIX {image_upload(,)} Achtervoegsel" }
|
||||||
|
*
|
||||||
* // should warn for unexpected keys
|
* // should warn for unexpected keys
|
||||||
* const errors = []
|
* const errors = []
|
||||||
* RewriteSpecial.convertIfNeeded({"special": {type: "image_carousel"}, "en": "xyz"}, errors, "test") // => {'*': "{image_carousel()}"}
|
* RewriteSpecial.convertIfNeeded({"special": {type: "image_carousel"}, "en": "xyz"}, errors, "test") // => {'*': "{image_carousel()}"}
|
||||||
|
@ -352,7 +353,7 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
|
||||||
return input
|
return input
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const wrongKey of Object.keys(input).filter(k => k !== "special")) {
|
for (const wrongKey of Object.keys(input).filter(k => k !== "special" && k !== "before" && k !== "after")) {
|
||||||
errors.push(`At ${context}: Unexpected key in a special block: ${wrongKey}`)
|
errors.push(`At ${context}: Unexpected key in a special block: ${wrongKey}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,6 +401,16 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const before = Translations.T(input.before)
|
||||||
|
const after = Translations.T(input.after)
|
||||||
|
|
||||||
|
for (const ln of Object.keys(before?.translations??{})) {
|
||||||
|
foundLanguages.add(ln)
|
||||||
|
}
|
||||||
|
for (const ln of Object.keys(after?.translations??{})) {
|
||||||
|
foundLanguages.add(ln)
|
||||||
|
}
|
||||||
|
|
||||||
if(foundLanguages.size === 0){
|
if(foundLanguages.size === 0){
|
||||||
const args= argNamesList.map(nm => special[nm] ?? "").join(",")
|
const args= argNamesList.map(nm => special[nm] ?? "").join(",")
|
||||||
return {'*': `{${type}(${args})}`
|
return {'*': `{${type}(${args})}`
|
||||||
|
@ -419,7 +430,9 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
|
||||||
args.push(v)
|
args.push(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result[ln] = `{${type}(${args.join(",")})}`
|
const beforeText = before?.textFor(ln) ?? ""
|
||||||
|
const afterText = after?.textFor(ln) ?? ""
|
||||||
|
result[ln] = `${beforeText}{${type}(${args.join(",")})}${afterText}`
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -437,6 +450,13 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
|
||||||
* const result = new RewriteSpecial().convert(tr,"test").result
|
* const result = new RewriteSpecial().convert(tr,"test").result
|
||||||
* const expected = {render: {'*': "{image_carousel(image)}"}, mappings: [{if: "other_image_key", then: {'*': "{image_carousel(other_image_key)}"}} ]}
|
* const expected = {render: {'*': "{image_carousel(image)}"}, mappings: [{if: "other_image_key", then: {'*': "{image_carousel(other_image_key)}"}} ]}
|
||||||
* result // => expected
|
* result // => expected
|
||||||
|
*
|
||||||
|
* const tr = {
|
||||||
|
* render: {special: {type: "image_carousel", image_key: "image"}, before: {en: "Some introduction"} },
|
||||||
|
* }
|
||||||
|
* const result = new RewriteSpecial().convert(tr,"test").result
|
||||||
|
* const expected = {render: {'en': "Some introduction{image_carousel(image)}"}}
|
||||||
|
* result // => expected
|
||||||
*/
|
*/
|
||||||
convert(json: TagRenderingConfigJson, context: string): { result: TagRenderingConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } {
|
convert(json: TagRenderingConfigJson, context: string): { result: TagRenderingConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } {
|
||||||
const errors = []
|
const errors = []
|
||||||
|
|
|
@ -170,7 +170,13 @@ class AddImportLayers extends DesugaringStep<LayoutConfigJson> {
|
||||||
super("For every layer in the 'layers'-list, create a new layer which'll import notes. (Note that priviliged layers and layers which have a geojson-source set are ignored)", ["layers"], "AddImportLayers");
|
super("For every layer in the 'layers'-list, create a new layer which'll import notes. (Note that priviliged layers and layers which have a geojson-source set are ignored)", ["layers"], "AddImportLayers");
|
||||||
}
|
}
|
||||||
|
|
||||||
convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[] } {
|
convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors?: string[], warnings?: string[] } {
|
||||||
|
if (!(json.enableNoteImports ?? true)) {
|
||||||
|
return {
|
||||||
|
warnings: ["Not creating a note import layers for theme "+json.id+" as they are disabled"],
|
||||||
|
result: json
|
||||||
|
};
|
||||||
|
}
|
||||||
const errors = []
|
const errors = []
|
||||||
|
|
||||||
json = {...json}
|
json = {...json}
|
||||||
|
@ -178,39 +184,37 @@ class AddImportLayers extends DesugaringStep<LayoutConfigJson> {
|
||||||
json.layers = [...json.layers]
|
json.layers = [...json.layers]
|
||||||
|
|
||||||
|
|
||||||
if (json.enableNoteImports ?? true) {
|
const creator = new CreateNoteImportLayer()
|
||||||
const creator = new CreateNoteImportLayer()
|
for (let i1 = 0; i1 < allLayers.length; i1++) {
|
||||||
for (let i1 = 0; i1 < allLayers.length; i1++) {
|
const layer = allLayers[i1];
|
||||||
const layer = allLayers[i1];
|
if (Constants.priviliged_layers.indexOf(layer.id) >= 0) {
|
||||||
if (Constants.priviliged_layers.indexOf(layer.id) >= 0) {
|
// Priviliged layers are skipped
|
||||||
// Priviliged layers are skipped
|
continue
|
||||||
continue
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (layer.source["geoJson"] !== undefined) {
|
if (layer.source["geoJson"] !== undefined) {
|
||||||
// Layer which don't get their data from OSM are skipped
|
// Layer which don't get their data from OSM are skipped
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (layer.title === undefined || layer.name === undefined) {
|
if (layer.title === undefined || layer.name === undefined) {
|
||||||
// Anonymous layers and layers without popup are skipped
|
// Anonymous layers and layers without popup are skipped
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (layer.presets === undefined || layer.presets.length == 0) {
|
if (layer.presets === undefined || layer.presets.length == 0) {
|
||||||
// A preset is needed to be able to generate a new point
|
// A preset is needed to be able to generate a new point
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
const importLayerResult = creator.convert(layer, context + ".(noteimportlayer)[" + i1 + "]")
|
const importLayerResult = creator.convert(layer, context + ".(noteimportlayer)[" + i1 + "]")
|
||||||
if (importLayerResult.result !== undefined) {
|
if (importLayerResult.result !== undefined) {
|
||||||
json.layers.push(importLayerResult.result)
|
json.layers.push(importLayerResult.result)
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
errors.push("Could not generate an import-layer for " + layer.id + " due to " + e)
|
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
errors.push("Could not generate an import-layer for " + layer.id + " due to " + e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,6 +259,7 @@ export class AddMiniMap extends DesugaringStep<LayerConfigJson> {
|
||||||
if (!translation.hasOwnProperty(key)) {
|
if (!translation.hasOwnProperty(key)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const template = translation[key]
|
const template = translation[key]
|
||||||
const parts = SubstitutedTranslation.ExtractSpecialComponents(template)
|
const parts = SubstitutedTranslation.ExtractSpecialComponents(template)
|
||||||
const hasMiniMap = parts.filter(part => part.special !== undefined).some(special => special.special.func.funcName === "minimap")
|
const hasMiniMap = parts.filter(part => part.special !== undefined).some(special => special.special.func.funcName === "minimap")
|
||||||
|
|
|
@ -308,6 +308,8 @@ export interface LayoutConfigJson {
|
||||||
/**
|
/**
|
||||||
* If true, notes will be loaded and parsed. If a note is an import (as created by the import_helper.html-tool from mapcomplete),
|
* If true, notes will be loaded and parsed. If a note is an import (as created by the import_helper.html-tool from mapcomplete),
|
||||||
* these notes will be shown if a relevant layer is present.
|
* these notes will be shown if a relevant layer is present.
|
||||||
|
*
|
||||||
|
* Default is true for official layers and false for unofficial (sideloaded) layers
|
||||||
*/
|
*/
|
||||||
enableNoteImports?: true | boolean;
|
enableNoteImports?: true | boolean;
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ import ContributorCount from "../../Logic/ContributorCount";
|
||||||
import Img from "../Base/Img";
|
import Img from "../Base/Img";
|
||||||
import {TypedTranslation} from "../i18n/Translation";
|
import {TypedTranslation} from "../i18n/Translation";
|
||||||
import TranslatorsPanel from "./TranslatorsPanel";
|
import TranslatorsPanel from "./TranslatorsPanel";
|
||||||
|
import {MapillaryLink} from "./MapillaryLink";
|
||||||
|
|
||||||
export class OpenIdEditor extends VariableUiElement {
|
export class OpenIdEditor extends VariableUiElement {
|
||||||
constructor(state: { locationControl: UIEventSource<Loc> }, iconStyle?: string, objectId?: string) {
|
constructor(state: { locationControl: UIEventSource<Loc> }, iconStyle?: string, objectId?: string) {
|
||||||
|
@ -44,19 +45,6 @@ export class OpenIdEditor extends VariableUiElement {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class OpenMapillary extends VariableUiElement {
|
|
||||||
constructor(state: { locationControl: UIEventSource<Loc> }, iconStyle?: string) {
|
|
||||||
const t = Translations.t.general.attribution
|
|
||||||
super(state.locationControl.map(location => {
|
|
||||||
const mapillaryLink = `https://www.mapillary.com/app/?focus=map&lat=${location?.lat ?? 0}&lng=${location?.lon ?? 0}&z=${Math.max((location?.zoom ?? 2) - 1, 1)}`
|
|
||||||
return new SubtleButton(Svg.mapillary_black_ui().SetStyle(iconStyle), t.openMapillary, {
|
|
||||||
url: mapillaryLink,
|
|
||||||
newTab: true
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class OpenJosm extends Combine {
|
export class OpenJosm extends Combine {
|
||||||
|
|
||||||
constructor(state: { osmConnection: OsmConnection, currentBounds: UIEventSource<BBox>, }, iconStyle?: string) {
|
constructor(state: { osmConnection: OsmConnection, currentBounds: UIEventSource<BBox>, }, iconStyle?: string) {
|
||||||
|
@ -132,7 +120,7 @@ export default class CopyrightPanel extends Combine {
|
||||||
newTab: true
|
newTab: true
|
||||||
}),
|
}),
|
||||||
new OpenIdEditor(state, iconStyle),
|
new OpenIdEditor(state, iconStyle),
|
||||||
new OpenMapillary(state, iconStyle),
|
new MapillaryLink(state, iconStyle),
|
||||||
new OpenJosm(state, iconStyle),
|
new OpenJosm(state, iconStyle),
|
||||||
new TranslatorsPanel(state, iconStyle)
|
new TranslatorsPanel(state, iconStyle)
|
||||||
|
|
||||||
|
|
24
UI/BigComponents/MapillaryLink.ts
Normal file
24
UI/BigComponents/MapillaryLink.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||||
|
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
|
import Loc from "../../Models/Loc";
|
||||||
|
import Translations from "../i18n/Translations";
|
||||||
|
import {SubtleButton} from "../Base/SubtleButton";
|
||||||
|
import Svg from "../../Svg";
|
||||||
|
import Combine from "../Base/Combine";
|
||||||
|
import Title from "../Base/Title";
|
||||||
|
|
||||||
|
export class MapillaryLink extends VariableUiElement {
|
||||||
|
constructor(state: { locationControl: UIEventSource<Loc> }, iconStyle?: string) {
|
||||||
|
const t = Translations.t.general.attribution
|
||||||
|
super(state.locationControl.map(location => {
|
||||||
|
const mapillaryLink = `https://www.mapillary.com/app/?focus=map&lat=${location?.lat ?? 0}&lng=${location?.lon ?? 0}&z=${Math.max((location?.zoom ?? 2) - 1, 1)}`
|
||||||
|
return new SubtleButton(Svg.mapillary_black_ui().SetStyle(iconStyle),
|
||||||
|
new Combine([
|
||||||
|
new Title(t.openMapillary,3),
|
||||||
|
t.mapillaryHelp]), {
|
||||||
|
url: mapillaryLink,
|
||||||
|
newTab: true
|
||||||
|
}).SetClass("flex flex-col link-no-underline")
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,22 +1,31 @@
|
||||||
import Combine from "../Base/Combine";
|
import Combine from "../Base/Combine";
|
||||||
import Attribution from "./Attribution";
|
import Attribution from "./Attribution";
|
||||||
import Img from "../Base/Img";
|
import Img from "../Base/Img";
|
||||||
import {ProvidedImage} from "../../Logic/ImageProviders/ImageProvider";
|
import ImageProvider, {ProvidedImage} from "../../Logic/ImageProviders/ImageProvider";
|
||||||
import BaseUIElement from "../BaseUIElement";
|
import BaseUIElement from "../BaseUIElement";
|
||||||
import {Mapillary} from "../../Logic/ImageProviders/Mapillary";
|
import {Mapillary} from "../../Logic/ImageProviders/Mapillary";
|
||||||
|
|
||||||
|
|
||||||
export class AttributedImage extends Combine {
|
export class AttributedImage extends Combine {
|
||||||
|
|
||||||
constructor(imageInfo: ProvidedImage) {
|
constructor(imageInfo: {
|
||||||
|
url: string,
|
||||||
|
provider?: ImageProvider,
|
||||||
|
date?: Date
|
||||||
|
}
|
||||||
|
) {
|
||||||
let img: BaseUIElement;
|
let img: BaseUIElement;
|
||||||
let attr: BaseUIElement
|
|
||||||
img = new Img(imageInfo.url, false, {
|
img = new Img(imageInfo.url, false, {
|
||||||
fallbackImage: imageInfo.provider === Mapillary.singleton ? "./assets/svg/blocked.svg" : undefined
|
fallbackImage: imageInfo.provider === Mapillary.singleton ? "./assets/svg/blocked.svg" : undefined
|
||||||
});
|
});
|
||||||
attr = new Attribution(imageInfo.provider.GetAttributionFor(imageInfo.url),
|
|
||||||
imageInfo.provider.SourceIcon(),
|
let attr: BaseUIElement = undefined
|
||||||
)
|
if(imageInfo.provider !== undefined){
|
||||||
|
attr = new Attribution(imageInfo.provider?.GetAttributionFor(imageInfo.url),
|
||||||
|
imageInfo.provider?.SourceIcon(),
|
||||||
|
imageInfo.date
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
super([img, attr]);
|
super([img, attr]);
|
||||||
|
|
|
@ -4,10 +4,11 @@ import BaseUIElement from "../BaseUIElement";
|
||||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
import {LicenseInfo} from "../../Logic/ImageProviders/LicenseInfo";
|
import {LicenseInfo} from "../../Logic/ImageProviders/LicenseInfo";
|
||||||
|
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||||
|
|
||||||
export default class Attribution extends VariableUiElement {
|
export default class Attribution extends VariableUiElement {
|
||||||
|
|
||||||
constructor(license: UIEventSource<LicenseInfo>, icon: BaseUIElement) {
|
constructor(license: UIEventSource<LicenseInfo>, icon: BaseUIElement, date?: Date) {
|
||||||
if (license === undefined) {
|
if (license === undefined) {
|
||||||
throw "No license source given in the attribution element"
|
throw "No license source given in the attribution element"
|
||||||
}
|
}
|
||||||
|
@ -23,7 +24,8 @@ export default class Attribution extends VariableUiElement {
|
||||||
new Combine([
|
new Combine([
|
||||||
Translations.W(license?.title).SetClass("block"),
|
Translations.W(license?.title).SetClass("block"),
|
||||||
Translations.W(license?.artist ?? "").SetClass("block font-bold"),
|
Translations.W(license?.artist ?? "").SetClass("block font-bold"),
|
||||||
Translations.W((license?.license ?? "") === "" ? "CC0" : (license?.license ?? ""))
|
Translations.W((license?.license ?? "") === "" ? "CC0" : (license?.license ?? "")),
|
||||||
|
date === undefined ? undefined : new FixedUiElement(date.toLocaleDateString())
|
||||||
]).SetClass("flex flex-col")
|
]).SetClass("flex flex-col")
|
||||||
]).SetClass("flex flex-row bg-black text-white text-sm absolute bottom-0 left-0 p-0.5 pl-5 pr-3 rounded-lg no-images")
|
]).SetClass("flex flex-row bg-black text-white text-sm absolute bottom-0 left-0 p-0.5 pl-5 pr-3 rounded-lg no-images")
|
||||||
|
|
||||||
|
|
133
UI/Popup/NearbyImages.ts
Normal file
133
UI/Popup/NearbyImages.ts
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
import Combine from "../Base/Combine";
|
||||||
|
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
|
import {SlideShow} from "../Image/SlideShow";
|
||||||
|
import Toggle from "../Input/Toggle";
|
||||||
|
import Loading from "../Base/Loading";
|
||||||
|
import {AttributedImage} from "../Image/AttributedImage";
|
||||||
|
import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders";
|
||||||
|
import Svg from "../../Svg";
|
||||||
|
import BaseUIElement from "../BaseUIElement";
|
||||||
|
import {InputElement} from "../Input/InputElement";
|
||||||
|
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||||
|
import Translations from "../i18n/Translations";
|
||||||
|
import {Mapillary} from "../../Logic/ImageProviders/Mapillary";
|
||||||
|
|
||||||
|
export interface P4CPicture {
|
||||||
|
pictureUrl: string,
|
||||||
|
date: number,
|
||||||
|
coordinates: { lat: number, lng: number },
|
||||||
|
provider: "Mapillary" | string,
|
||||||
|
author,
|
||||||
|
license,
|
||||||
|
detailsUrl: string,
|
||||||
|
direction,
|
||||||
|
osmTags: object /*To copy straight into OSM!*/
|
||||||
|
,
|
||||||
|
thumbUrl: string,
|
||||||
|
details: {
|
||||||
|
isSpherical: boolean,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface NearbyImageOptions {
|
||||||
|
lon: number,
|
||||||
|
lat: number,
|
||||||
|
radius: number,
|
||||||
|
maxDaysOld?: 1095,
|
||||||
|
blacklist: UIEventSource<{url: string}[]>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class NearbyImages extends VariableUiElement {
|
||||||
|
|
||||||
|
constructor(options: NearbyImageOptions) {
|
||||||
|
const t = Translations.t.image.nearbyPictures
|
||||||
|
const P4C = require("../../vendor/P4C.min")
|
||||||
|
const picManager = new P4C.PicturesManager({});
|
||||||
|
|
||||||
|
const loadedPictures = UIEventSource.FromPromise<P4CPicture[]>(
|
||||||
|
picManager.startPicsRetrievalAround(new P4C.LatLng(options.lat, options.lon), options.radius, {
|
||||||
|
mindate: new Date().getTime() - (options.maxDaysOld ?? 1095) * 24 * 60 * 60 * 1000
|
||||||
|
})
|
||||||
|
).map(images => {
|
||||||
|
console.log("Images are" ,images, "blacklisted is", options.blacklist.data)
|
||||||
|
images?.sort((a, b) => b.date - a.date)
|
||||||
|
return images ?.filter(i => !(options.blacklist?.data?.some(blacklisted =>
|
||||||
|
Mapillary.sameUrl(i.pictureUrl, blacklisted.url)))
|
||||||
|
&& i.details.isSpherical === false);
|
||||||
|
}, [options.blacklist])
|
||||||
|
|
||||||
|
super(loadedPictures.map(images => {
|
||||||
|
if(images === undefined){
|
||||||
|
return new Loading(t.loading);
|
||||||
|
}
|
||||||
|
if(images.length === 0){
|
||||||
|
return t.nothingFound.SetClass("alert block")
|
||||||
|
}
|
||||||
|
return new SlideShow(loadedPictures.map(imgs => (imgs ?? []).slice(0, 25).map(i => this.prepareElement(i))))
|
||||||
|
}));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected prepareElement(info: P4CPicture): BaseUIElement {
|
||||||
|
const provider = AllImageProviders.byName(info.provider);
|
||||||
|
return new AttributedImage({url: info.pictureUrl, provider})
|
||||||
|
}
|
||||||
|
|
||||||
|
private asAttributedImage(info: P4CPicture): AttributedImage {
|
||||||
|
const provider = AllImageProviders.byName(info.provider);
|
||||||
|
return new AttributedImage({url: info.thumbUrl, provider, date: new Date(info.date)})
|
||||||
|
}
|
||||||
|
|
||||||
|
protected asToggle(info:P4CPicture): Toggle {
|
||||||
|
const imgNonSelected = this.asAttributedImage(info);
|
||||||
|
const imageSelected = this.asAttributedImage(info);
|
||||||
|
|
||||||
|
const nonSelected = new Combine([imgNonSelected]).SetClass("relative block")
|
||||||
|
const hoveringCheckmark =
|
||||||
|
new Combine([Svg.confirm_svg().SetClass("block w-24 h-24 -ml-12 -mt-12")]).SetClass("absolute left-1/2 top-1/2 w-0")
|
||||||
|
const selected = new Combine([
|
||||||
|
imageSelected,
|
||||||
|
hoveringCheckmark,
|
||||||
|
]).SetClass("relative block")
|
||||||
|
|
||||||
|
return new Toggle(selected, nonSelected).SetClass("").ToggleOnClick();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SelectOneNearbyImage extends NearbyImages implements InputElement<P4CPicture> {
|
||||||
|
private readonly value: UIEventSource<P4CPicture>;
|
||||||
|
|
||||||
|
constructor(options: NearbyImageOptions & {value?: UIEventSource<P4CPicture>}) {
|
||||||
|
super(options)
|
||||||
|
this.value = options.value ?? new UIEventSource<P4CPicture>(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
GetValue(): UIEventSource<P4CPicture> {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
IsValid(t: P4CPicture): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected prepareElement(info: P4CPicture): BaseUIElement {
|
||||||
|
const toggle = super.asToggle(info)
|
||||||
|
toggle.isEnabled.addCallback(enabled => {
|
||||||
|
if (enabled) {
|
||||||
|
this.value.setData(info)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.value.addCallback(inf => {
|
||||||
|
if(inf !== info){
|
||||||
|
toggle.isEnabled.setData(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return toggle
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,10 +2,11 @@ import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
import Translations from "../i18n/Translations";
|
import Translations from "../i18n/Translations";
|
||||||
import {OsmConnection} from "../../Logic/Osm/OsmConnection";
|
import {OsmConnection} from "../../Logic/Osm/OsmConnection";
|
||||||
import Toggle from "../Input/Toggle";
|
import Toggle from "../Input/Toggle";
|
||||||
|
import BaseUIElement from "../BaseUIElement";
|
||||||
|
|
||||||
export class SaveButton extends Toggle {
|
export class SaveButton extends Toggle {
|
||||||
|
|
||||||
constructor(value: UIEventSource<any>, osmConnection: OsmConnection) {
|
constructor(value: UIEventSource<any>, osmConnection: OsmConnection, textEnabled ?: BaseUIElement, textDisabled ?: BaseUIElement) {
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
throw "No event source for savebutton, something is wrong"
|
throw "No event source for savebutton, something is wrong"
|
||||||
}
|
}
|
||||||
|
@ -17,9 +18,9 @@ export class SaveButton extends Toggle {
|
||||||
|
|
||||||
const isSaveable = value.map(v => v !== false && (v ?? "") !== "")
|
const isSaveable = value.map(v => v !== false && (v ?? "") !== "")
|
||||||
|
|
||||||
const text = Translations.t.general.save
|
const saveEnabled = (textEnabled ?? Translations.t.general.save.Clone()).SetClass(`btn`);
|
||||||
const saveEnabled = text.Clone().SetClass(`btn`);
|
const saveDisabled = (textDisabled ?? Translations.t.general.save.Clone()).SetClass(`btn btn-disabled`);
|
||||||
const saveDisabled = text.Clone().SetClass(`btn btn-disabled`);
|
|
||||||
const save = new Toggle(
|
const save = new Toggle(
|
||||||
saveEnabled,
|
saveEnabled,
|
||||||
saveDisabled,
|
saveDisabled,
|
||||||
|
|
|
@ -48,6 +48,13 @@ import {TextField} from "./Input/TextField";
|
||||||
import Wikidata, {WikidataResponse} from "../Logic/Web/Wikidata";
|
import Wikidata, {WikidataResponse} from "../Logic/Web/Wikidata";
|
||||||
import {Translation} from "./i18n/Translation";
|
import {Translation} from "./i18n/Translation";
|
||||||
import {AllTagsPanel} from "./AllTagsPanel";
|
import {AllTagsPanel} from "./AllTagsPanel";
|
||||||
|
import NearbyImages, {P4CPicture, SelectOneNearbyImage} from "./Popup/NearbyImages";
|
||||||
|
import Lazy from "./Base/Lazy";
|
||||||
|
import ChangeTagAction from "../Logic/Osm/Actions/ChangeTagAction";
|
||||||
|
import {Tag} from "../Logic/Tags/Tag";
|
||||||
|
import {And} from "../Logic/Tags/And";
|
||||||
|
import {SaveButton} from "./Popup/SaveButton";
|
||||||
|
import {MapillaryLink} from "./BigComponents/MapillaryLink";
|
||||||
|
|
||||||
export interface SpecialVisualization {
|
export interface SpecialVisualization {
|
||||||
funcName: string,
|
funcName: string,
|
||||||
|
@ -141,6 +148,116 @@ class CloseNoteButton implements SpecialVisualization {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class NearbyImageVis implements SpecialVisualization {
|
||||||
|
args: { name: string; defaultValue?: string; doc: string; required?: boolean }[] = [
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "mode",
|
||||||
|
defaultValue: "expandable",
|
||||||
|
doc: "Indicates how this component is initialized. Options are: \n\n- `open`: always show and load the pictures\n- `collapsable`: show the pictures, but a user can collapse them\n- `expandable`: shown by default; but a user can collapse them."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mapillary",
|
||||||
|
defaultValue: "true",
|
||||||
|
doc: "If 'true', includes a link to mapillary on this location."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
docs = "A component showing nearby images loaded from various online services such as Mapillary. In edit mode and when used on a feature, the user can select an image to add to the feature";
|
||||||
|
funcName = "nearby_images";
|
||||||
|
|
||||||
|
constr(state: FeaturePipelineState, tagSource: UIEventSource<any>, args: string[], guistate: DefaultGuiState): BaseUIElement {
|
||||||
|
const t = Translations.t.image.nearbyPictures
|
||||||
|
const mode: "open" | "expandable" | "collapsable" = <any>args[0]
|
||||||
|
const feature = state.allElements.ContainingFeatures.get(tagSource.data.id)
|
||||||
|
const [lon, lat] = GeoOperations.centerpointCoordinates(feature)
|
||||||
|
const id: string = tagSource.data["id"]
|
||||||
|
const canBeEdited: boolean = !!(id?.match("(node|way|relation)/-?[0-9]+"))
|
||||||
|
const selectedImage = new UIEventSource<P4CPicture>(undefined);
|
||||||
|
|
||||||
|
const nearby = new Lazy(() => {
|
||||||
|
const alreadyInTheImage = AllImageProviders.LoadImagesFor(tagSource)
|
||||||
|
const options = {
|
||||||
|
lon, lat, radius: 50,
|
||||||
|
value: selectedImage,
|
||||||
|
blacklist: alreadyInTheImage
|
||||||
|
};
|
||||||
|
const slideshow = canBeEdited ? new SelectOneNearbyImage(options) : new NearbyImages(options);
|
||||||
|
return new Combine([slideshow, new MapillaryLinkVis().constr(state, tagSource, [])])
|
||||||
|
});
|
||||||
|
|
||||||
|
let withEdit: BaseUIElement = nearby;
|
||||||
|
|
||||||
|
if (canBeEdited) {
|
||||||
|
const confirmText: BaseUIElement = new SubstitutedTranslation(t.confirm, tagSource, state)
|
||||||
|
|
||||||
|
const onSave = async () => {
|
||||||
|
console.log("Selected a picture...", selectedImage.data)
|
||||||
|
const osmTags = selectedImage.data.osmTags
|
||||||
|
const tags: Tag[] = []
|
||||||
|
for (const key in osmTags) {
|
||||||
|
tags.push(new Tag(key, osmTags[key]))
|
||||||
|
}
|
||||||
|
await state?.changes?.applyAction(
|
||||||
|
new ChangeTagAction(
|
||||||
|
id,
|
||||||
|
new And(tags),
|
||||||
|
tagSource,
|
||||||
|
{
|
||||||
|
theme: state?.layoutToUse.id,
|
||||||
|
changeType: "link-image"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveButton = new SaveButton(selectedImage, state.osmConnection, confirmText, t.noImageSelected)
|
||||||
|
.onClick(onSave)
|
||||||
|
|
||||||
|
withEdit = new Combine([
|
||||||
|
t.hasMatchingPicture,
|
||||||
|
nearby,
|
||||||
|
saveButton
|
||||||
|
.SetClass("flex justify-end")
|
||||||
|
]).SetClass("flex flex-col")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === 'open') {
|
||||||
|
return withEdit
|
||||||
|
}
|
||||||
|
const toggleState = new UIEventSource<boolean>(mode === 'collapsable')
|
||||||
|
return new Toggle(
|
||||||
|
new Combine([new Title(t.title), withEdit]),
|
||||||
|
new Title(t.browseNearby).onClick(() => toggleState.setData(true)),
|
||||||
|
toggleState
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MapillaryLinkVis implements SpecialVisualization {
|
||||||
|
funcName = "mapillary_link"
|
||||||
|
docs = "Adds a button to open mapillary on the specified location"
|
||||||
|
args = [{
|
||||||
|
name: "zoom",
|
||||||
|
doc: "The startzoom of mapillary",
|
||||||
|
defaultValue: "18"
|
||||||
|
}];
|
||||||
|
|
||||||
|
public constr(state, tagsSource, args) {
|
||||||
|
const feat = state.allElements.ContainingFeatures.get(tagsSource.data.id);
|
||||||
|
const [lon, lat] = GeoOperations.centerpointCoordinates(feat);
|
||||||
|
let zoom = Number(args[0])
|
||||||
|
if (isNaN(zoom)) {
|
||||||
|
zoom = 18
|
||||||
|
}
|
||||||
|
return new MapillaryLink({
|
||||||
|
locationControl: new UIEventSource<Loc>({
|
||||||
|
lat, lon, zoom
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default class SpecialVisualizations {
|
export default class SpecialVisualizations {
|
||||||
|
|
||||||
public static specialVisualizations: SpecialVisualization[] = SpecialVisualizations.init()
|
public static specialVisualizations: SpecialVisualization[] = SpecialVisualizations.init()
|
||||||
|
@ -309,7 +426,7 @@ export default class SpecialVisualizations {
|
||||||
example: "`{minimap()}`, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}`",
|
example: "`{minimap()}`, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}`",
|
||||||
constr: (state, tagSource, args, _) => {
|
constr: (state, tagSource, args, _) => {
|
||||||
|
|
||||||
if(state === undefined){
|
if (state === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
const keys = [...args]
|
const keys = [...args]
|
||||||
|
@ -940,7 +1057,9 @@ export default class SpecialVisualizations {
|
||||||
}
|
}
|
||||||
return new SubstitutedTranslation(title, tagsSource, state)
|
return new SubstitutedTranslation(title, tagsSource, state)
|
||||||
}))
|
}))
|
||||||
}
|
},
|
||||||
|
new NearbyImageVis(),
|
||||||
|
new MapillaryLinkVis()
|
||||||
]
|
]
|
||||||
|
|
||||||
specialVisualizations.push(new AutoApplyButton(specialVisualizations))
|
specialVisualizations.push(new AutoApplyButton(specialVisualizations))
|
||||||
|
|
|
@ -81,7 +81,7 @@ export class SubstitutedTranslation extends VariableUiElement {
|
||||||
}[] {
|
}[] {
|
||||||
|
|
||||||
for (const knownSpecial of extraMappings.concat(SpecialVisualizations.specialVisualizations)) {
|
for (const knownSpecial of extraMappings.concat(SpecialVisualizations.specialVisualizations)) {
|
||||||
|
|
||||||
// Note: the '.*?' in the regex reads as 'any character, but in a non-greedy way'
|
// Note: the '.*?' in the regex reads as 'any character, but in a non-greedy way'
|
||||||
const matched = template.match(`(.*){${knownSpecial.funcName}\\((.*?)\\)(:.*)?}(.*)`);
|
const matched = template.match(`(.*){${knownSpecial.funcName}\\((.*?)\\)(:.*)?}(.*)`);
|
||||||
if (matched != null) {
|
if (matched != null) {
|
||||||
|
|
|
@ -273,4 +273,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -59,6 +59,18 @@
|
||||||
"id": "comment",
|
"id": "comment",
|
||||||
"render": "{add_note_comment()}"
|
"render": "{add_note_comment()}"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "nearby-images",
|
||||||
|
"render": {
|
||||||
|
"before": {
|
||||||
|
"en": "<h3>Nearby images</h3>The pictures below are nearby geotagged images and might be helpful to handle this note."
|
||||||
|
},
|
||||||
|
"special": {
|
||||||
|
"type": "nearby_images",
|
||||||
|
"mode": "open"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "report-contributor",
|
"id": "report-contributor",
|
||||||
"render": {
|
"render": {
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
{
|
|
||||||
"id": "note_import",
|
|
||||||
"name": {
|
|
||||||
"en": "Possible bookcases",
|
|
||||||
"nl": "Mogelijke publieke boekenkastjes",
|
|
||||||
"de": "Mögliche Bücherschränke"
|
|
||||||
},
|
|
||||||
"description": "Template for note note imports.",
|
|
||||||
"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": {
|
|
||||||
"en": "Possible feature",
|
|
||||||
"nl": "Mogelijk object",
|
|
||||||
"de": "Mögliches Objekt"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"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 == '' ? '<br/>' : 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": "yes",
|
|
||||||
"mappings": [
|
|
||||||
{
|
|
||||||
"if": "_trigger_index=",
|
|
||||||
"then": "no"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"titleIcons": [
|
|
||||||
{
|
|
||||||
"render": "<a href='https://openstreetmap.org/note/{id}' target='_blank'><img src='./assets/svg/osm-logo-us.svg'></a>"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"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<br/>, ./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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
37
assets/svg/confirm.svg
Normal file
37
assets/svg/confirm.svg
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
width="157.33984"
|
||||||
|
height="157.33984"
|
||||||
|
viewBox="0 0 157.33984 157.33984"
|
||||||
|
version="1.1"
|
||||||
|
id="svg9"
|
||||||
|
sodipodi:docname="confirm.svg"
|
||||||
|
inkscape:version="1.1.2 (1:1.1+202202050950+0a00cf5339)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs13" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview11"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:pageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.312"
|
||||||
|
inkscape:cx="-18.598616"
|
||||||
|
inkscape:cy="57.093426"
|
||||||
|
inkscape:current-layer="svg9" />
|
||||||
|
<path
|
||||||
|
style="fill:#35d447;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
d="m 157.33984,78.66796 c 0,43.44922 -35.21875,78.67188 -78.66796,78.67188 C 35.22266,157.33984 0,122.11718 0,78.66796 0,35.22265 35.22266,0 78.67188,0 c 43.44921,0 78.66796,35.22265 78.66796,78.66796 z m 0,0"
|
||||||
|
id="path4" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#ffffff;stroke-width:19.7495;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
|
||||||
|
d="m 37.69921,75.49609 35.55078,39.5 47.39844,-63.19922"
|
||||||
|
id="path6" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -239,6 +239,14 @@
|
||||||
"authors": [],
|
"authors": [],
|
||||||
"sources": []
|
"sources": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "confirm.svg",
|
||||||
|
"license": "CC0",
|
||||||
|
"authors": [
|
||||||
|
"Pieter Vander Vennet"
|
||||||
|
],
|
||||||
|
"sources": []
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "copyright.svg",
|
"path": "copyright.svg",
|
||||||
"license": "CC0",
|
"license": "CC0",
|
||||||
|
@ -1115,11 +1123,15 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "search_disable.svg",
|
"path": "search_disable.svg",
|
||||||
"license": "CC0",
|
"license": "MIT",
|
||||||
"authors": [
|
"authors": [
|
||||||
|
"OOjs UI Team and other contributors",
|
||||||
"Pieter Vander Vennet"
|
"Pieter Vander Vennet"
|
||||||
],
|
],
|
||||||
"sources": []
|
"sources": [
|
||||||
|
"https://commons.wikimedia.org/wiki/File:OOjs_UI_indicator_search-rtl.svg",
|
||||||
|
"https://phabricator.wikimedia.org/diffusion/GOJU/browse/master/AUTHORS.txt"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "send_email.svg",
|
"path": "send_email.svg",
|
||||||
|
|
|
@ -4,7 +4,10 @@
|
||||||
"id": "questions"
|
"id": "questions"
|
||||||
},
|
},
|
||||||
"images": {
|
"images": {
|
||||||
"render": "{image_carousel()}{image_upload()}"
|
"render": "{image_carousel()}{image_upload()}{nearby_images(expandable)}"
|
||||||
|
},
|
||||||
|
"mapillary": {
|
||||||
|
"render": "{mapillary()}"
|
||||||
},
|
},
|
||||||
"export_as_gpx": {
|
"export_as_gpx": {
|
||||||
"render": "{export_as_gpx()}"
|
"render": "{export_as_gpx()}"
|
||||||
|
@ -36,7 +39,25 @@
|
||||||
{
|
{
|
||||||
"if": "wikipedia~*",
|
"if": "wikipedia~*",
|
||||||
"then": {
|
"then": {
|
||||||
"*": "{wikipedia():max-height:25rem}"
|
"*": "{wikipedia():max-height:25rem}",
|
||||||
|
"ca": "No hi ha cap enllaça a Viquipèdia encara",
|
||||||
|
"da": "Der er endnu ikke linket til nogen Wikipedia-side",
|
||||||
|
"de": "Es wurde noch keine Wikipedia-Seite verlinkt",
|
||||||
|
"en": "No Wikipedia page has been linked yet",
|
||||||
|
"es": "Todavía no se ha enlazado una página de wikipedia",
|
||||||
|
"fil": "Wala pang kawing ng Wikipedia page",
|
||||||
|
"fr": "Pas encore de lien vers une page Wikipedia",
|
||||||
|
"hu": "Még nincs Wikipédia-oldal belinkelve",
|
||||||
|
"it": "Nessuna pagina Wikipedia è ancora stata collegata",
|
||||||
|
"ja": "ウィキペディアのページはまだリンクされていません",
|
||||||
|
"nb_NO": "Ingen Wikipedia-side lenket enda",
|
||||||
|
"nl": "Er werd nog geen Wikipedia-pagina gekoppeld",
|
||||||
|
"pl": "Link do strony Wikipedii nie został jeszcze określony",
|
||||||
|
"pt": "Ainda não foi vinculada nenhuma página da Wikipédia",
|
||||||
|
"ru": "Никакой страницы на Википедии не было прикреплено",
|
||||||
|
"sv": "Ingen Wikipedia-sida har länkats än",
|
||||||
|
"zh_Hans": "尚未有连接到的维基百科页面",
|
||||||
|
"zh_Hant": "還沒有連結到維基百科頁面"
|
||||||
},
|
},
|
||||||
"hideInAnswer": true
|
"hideInAnswer": true
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,13 +1,19 @@
|
||||||
{
|
{
|
||||||
"id": "mapcomplete-changes",
|
"id": "mapcomplete-changes",
|
||||||
"title": {
|
"title": {
|
||||||
"en": "Changes made with MapComplete"
|
"en": "Changes made with MapComplete",
|
||||||
|
"de": "Änderungen mit MapComplete",
|
||||||
|
"es": "Cambios hechos con MapComplete"
|
||||||
},
|
},
|
||||||
"shortDescription": {
|
"shortDescription": {
|
||||||
"en": "Shows changes made by MapComplete"
|
"en": "Shows changes made by MapComplete",
|
||||||
|
"de": "Zeigt Änderungen von MapComplete",
|
||||||
|
"es": "Muestra los cambios hechos por MapComplete"
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"en": "This maps shows all the changes made with MapComplete"
|
"en": "This maps shows all the changes made with MapComplete",
|
||||||
|
"de": "Diese Karte zeigt alle Änderungen die mit MapComplete gemacht wurden",
|
||||||
|
"es": "Este mapa muestra todos los cambios hechos con MapComplete"
|
||||||
},
|
},
|
||||||
"maintainer": "",
|
"maintainer": "",
|
||||||
"icon": "./assets/svg/logo.svg",
|
"icon": "./assets/svg/logo.svg",
|
||||||
|
@ -22,7 +28,9 @@
|
||||||
{
|
{
|
||||||
"id": "mapcomplete-changes",
|
"id": "mapcomplete-changes",
|
||||||
"name": {
|
"name": {
|
||||||
"en": "Changeset centers"
|
"en": "Changeset centers",
|
||||||
|
"de": "Schwerpunkte von Änderungssätzen",
|
||||||
|
"es": "Centros de conjuntos de cambios"
|
||||||
},
|
},
|
||||||
"minzoom": 0,
|
"minzoom": 0,
|
||||||
"source": {
|
"source": {
|
||||||
|
@ -36,35 +44,45 @@
|
||||||
],
|
],
|
||||||
"title": {
|
"title": {
|
||||||
"render": {
|
"render": {
|
||||||
"en": "Changeset for {theme}"
|
"en": "Changeset for {theme}",
|
||||||
|
"de": "Änderungen für {theme}",
|
||||||
|
"es": "Conjunto de cambios para {theme}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"en": "Shows all MapComplete changes"
|
"en": "Shows all MapComplete changes",
|
||||||
|
"de": "Zeigt alle MapComplete Änderungen",
|
||||||
|
"es": "Muestra todos los cambios de MapComplete"
|
||||||
},
|
},
|
||||||
"tagRenderings": [
|
"tagRenderings": [
|
||||||
{
|
{
|
||||||
"id": "render_id",
|
"id": "render_id",
|
||||||
"render": {
|
"render": {
|
||||||
"en": "Changeset <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>"
|
"en": "Changeset <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>",
|
||||||
|
"de": "Änderung <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>",
|
||||||
|
"es": "Conjunto de cambios <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "contributor",
|
"id": "contributor",
|
||||||
"render": {
|
"render": {
|
||||||
"en": "Change made by <a href='https://openstreetmap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a>"
|
"en": "Change made by <a href='https://openstreetmap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a>",
|
||||||
|
"de": "Änderung wurde von <a href='https://openstreetmap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a> gemacht",
|
||||||
|
"es": "Cambio hecho por <a href='https://openstreetmap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a>"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "theme",
|
"id": "theme",
|
||||||
"render": {
|
"render": {
|
||||||
"en": "Change with theme <a href='https://mapcomplete.osm.be/{theme}'>{theme}</a>"
|
"en": "Change with theme <a href='https://mapcomplete.osm.be/{theme}'>{theme}</a>",
|
||||||
|
"de": "Änderung mit Thema <a href='https://mapcomplete.osm.be/{theme}'>{theme}</a>"
|
||||||
},
|
},
|
||||||
"mappings": [
|
"mappings": [
|
||||||
{
|
{
|
||||||
"if": "theme~http.*",
|
"if": "theme~http.*",
|
||||||
"then": {
|
"then": {
|
||||||
"en": "Change with <b>unofficial</b> theme <a href='https://mapcomplete.osm.be/theme.html?userlayout={theme}'>{theme}</a>"
|
"en": "Change with <b>unofficial</b> theme <a href='https://mapcomplete.osm.be/theme.html?userlayout={theme}'>{theme}</a>",
|
||||||
|
"de": "Änderung mit <b>inoffiziellem</b> Thema <a href='https://mapcomplete.osm.be/theme.html?userlayout={theme}'>{theme}</a>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -332,7 +350,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Themename contains {search}"
|
"en": "Themename contains {search}",
|
||||||
|
"de": "Themenname enthält {search}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -348,7 +367,9 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Made by contributor {search}"
|
"en": "Made by contributor {search}",
|
||||||
|
"de": "Erstellt von {search}",
|
||||||
|
"es": "Hecho por contributor/a {search}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -364,7 +385,9 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"question": {
|
"question": {
|
||||||
"en": "<b>Not</b> made by contributor {search}"
|
"en": "<b>Not</b> made by contributor {search}",
|
||||||
|
"de": "<b>Nicht</b> erstellt von {search}",
|
||||||
|
"es": "<b>No</b> hecho por contributor/a {search}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -379,7 +402,9 @@
|
||||||
{
|
{
|
||||||
"id": "link_to_more",
|
"id": "link_to_more",
|
||||||
"render": {
|
"render": {
|
||||||
"en": "More statistics can be found <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>here</a>"
|
"en": "More statistics can be found <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>here</a>",
|
||||||
|
"de": "Weitere Statistiken finden Sie <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>hier</a>",
|
||||||
|
"es": "Se pueden encontrar más estadísticas <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>aquí</a>"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -773,6 +773,14 @@ video {
|
||||||
left: 0px;
|
left: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.left-1\/2 {
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-1\/2 {
|
||||||
|
top: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
.isolate {
|
.isolate {
|
||||||
isolation: isolate;
|
isolation: isolate;
|
||||||
}
|
}
|
||||||
|
@ -932,6 +940,14 @@ video {
|
||||||
margin-right: 0.75rem;
|
margin-right: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.-ml-12 {
|
||||||
|
margin-left: -3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.-mt-12 {
|
||||||
|
margin-top: -3rem;
|
||||||
|
}
|
||||||
|
|
||||||
.mb-0 {
|
.mb-0 {
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
}
|
}
|
||||||
|
@ -1436,6 +1452,11 @@ video {
|
||||||
border-color: rgba(229, 231, 235, var(--tw-border-opacity));
|
border-color: rgba(229, 231, 235, var(--tw-border-opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.border-green-500 {
|
||||||
|
--tw-border-opacity: 1;
|
||||||
|
border-color: rgba(16, 185, 129, var(--tw-border-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
.border-opacity-50 {
|
.border-opacity-50 {
|
||||||
--tw-border-opacity: 0.5;
|
--tw-border-opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,7 @@
|
||||||
"josmOpened": "JOSM is opened",
|
"josmOpened": "JOSM is opened",
|
||||||
"mapContributionsBy": "The current visible data has edits made by {contributors}",
|
"mapContributionsBy": "The current visible data has edits made by {contributors}",
|
||||||
"mapContributionsByAndHidden": "The current visible data has edits made by {contributors} and {hiddenCount} more contributors",
|
"mapContributionsByAndHidden": "The current visible data has edits made by {contributors} and {hiddenCount} more contributors",
|
||||||
|
"mapillaryHelp": "<b>Mapillary</b> is an online service which gathers street-level pictures and offers them under a free license. Contributors are allowed to use these pictures to improve OpenStreetMap",
|
||||||
"openIssueTracker": "File a bug",
|
"openIssueTracker": "File a bug",
|
||||||
"openMapillary": "Open Mapillary here",
|
"openMapillary": "Open Mapillary here",
|
||||||
"openOsmcha": "See latest edits made with {theme}",
|
"openOsmcha": "See latest edits made with {theme}",
|
||||||
|
@ -275,6 +276,15 @@
|
||||||
"doDelete": "Remove image",
|
"doDelete": "Remove image",
|
||||||
"dontDelete": "Cancel",
|
"dontDelete": "Cancel",
|
||||||
"isDeleted": "Deleted",
|
"isDeleted": "Deleted",
|
||||||
|
"nearbyPictures": {
|
||||||
|
"browseNearby": "Browse nearby images...",
|
||||||
|
"confirm": "The selected image shows {title()}",
|
||||||
|
"hasMatchingPicture": "Does a picture match the object? Select it below",
|
||||||
|
"loading": "Loading nearby images...",
|
||||||
|
"noImageSelected": "Select an image to link it to the object",
|
||||||
|
"nothingFound": "No nearby images found...",
|
||||||
|
"title": "Nearby pictures"
|
||||||
|
},
|
||||||
"pleaseLogin": "Please log in to add a picture",
|
"pleaseLogin": "Please log in to add a picture",
|
||||||
"respectPrivacy": "Do not photograph people nor license plates. Do not upload Google Maps, Google Streetview or other copyrighted sources.",
|
"respectPrivacy": "Do not photograph people nor license plates. Do not upload Google Maps, Google Streetview or other copyrighted sources.",
|
||||||
"toBig": "Your image is too large as it is {actual_size}. Please use images of at most {max_size}",
|
"toBig": "Your image is too large as it is {actual_size}. Please use images of at most {max_size}",
|
||||||
|
@ -417,6 +427,7 @@
|
||||||
"importButton": "import_button({layerId}, _tags, I have found a {title} here - add it to the map,./assets/svg/addSmall.svg,,,id)",
|
"importButton": "import_button({layerId}, _tags, I have found a {title} here - add it to the map,./assets/svg/addSmall.svg,,,id)",
|
||||||
"importHandled": "<div class='thanks'>This feature has been handled! Thanks for your effort</div>",
|
"importHandled": "<div class='thanks'>This feature has been handled! Thanks for your effort</div>",
|
||||||
"layerName": "Possible {title}",
|
"layerName": "Possible {title}",
|
||||||
|
"nearbyImagesIntro": "<h3>Nearby pictures</h3>The following pictures are nearby geotagged pictures from various online services. They might help you to resolve this note.{nearby_images(open)}",
|
||||||
"notFound": "I could not find {title} - remove it",
|
"notFound": "I could not find {title} - remove it",
|
||||||
"popupTitle": "There might be {title} here"
|
"popupTitle": "There might be {title} here"
|
||||||
},
|
},
|
||||||
|
|
|
@ -3353,12 +3353,6 @@
|
||||||
"render": "Notiz"
|
"render": "Notiz"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"note_import": {
|
|
||||||
"name": "Mögliche Bücherschränke",
|
|
||||||
"title": {
|
|
||||||
"render": "Mögliches Objekt"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"observation_tower": {
|
"observation_tower": {
|
||||||
"description": "Türme zur Aussicht auf die umgebende Landschaft",
|
"description": "Türme zur Aussicht auf die umgebende Landschaft",
|
||||||
"name": "Aussichtstürme",
|
"name": "Aussichtstürme",
|
||||||
|
|
|
@ -4252,6 +4252,11 @@
|
||||||
},
|
},
|
||||||
"name": "OpenStreetMap notes",
|
"name": "OpenStreetMap notes",
|
||||||
"tagRenderings": {
|
"tagRenderings": {
|
||||||
|
"nearby-images": {
|
||||||
|
"render": {
|
||||||
|
"before": "<h3>Nearby images</h3>The pictures below are nearby geotagged images and might be helpful to handle this note."
|
||||||
|
}
|
||||||
|
},
|
||||||
"report-contributor": {
|
"report-contributor": {
|
||||||
"render": "<a href='https://www.openstreetmap.org/reports/new?reportable_id={_first_user_id}&reportable_type=User' target='_blank' class='subtle'>Report {_first_user} as spam</a>"
|
"render": "<a href='https://www.openstreetmap.org/reports/new?reportable_id={_first_user_id}&reportable_type=User' target='_blank' class='subtle'>Report {_first_user} as spam</a>"
|
||||||
},
|
},
|
||||||
|
@ -4268,12 +4273,6 @@
|
||||||
"render": "Note"
|
"render": "Note"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"note_import": {
|
|
||||||
"name": "Possible bookcases",
|
|
||||||
"title": {
|
|
||||||
"render": "Possible feature"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"observation_tower": {
|
"observation_tower": {
|
||||||
"description": "Towers with a panoramic view",
|
"description": "Towers with a panoramic view",
|
||||||
"name": "Observation towers",
|
"name": "Observation towers",
|
||||||
|
|
|
@ -4122,12 +4122,6 @@
|
||||||
"render": "Note"
|
"render": "Note"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"note_import": {
|
|
||||||
"name": "Mogelijke publieke boekenkastjes",
|
|
||||||
"title": {
|
|
||||||
"render": "Mogelijk object"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"observation_tower": {
|
"observation_tower": {
|
||||||
"description": "Torens om van het uitzicht te genieten",
|
"description": "Torens om van het uitzicht te genieten",
|
||||||
"name": "Uitkijktorens",
|
"name": "Uitkijktorens",
|
||||||
|
|
|
@ -333,6 +333,7 @@
|
||||||
"importButton": "import_button({layerId}, _tags, Ik heb hier een {title} gevonden - voeg deze toe aan de kaart...,./assets/svg/addSmall.svg,,,id)",
|
"importButton": "import_button({layerId}, _tags, Ik heb hier een {title} gevonden - voeg deze toe aan de kaart...,./assets/svg/addSmall.svg,,,id)",
|
||||||
"importHandled": "<div class='thanks'>Dit punt is afgehandeld. Bedankt om mee te helpen!</div>",
|
"importHandled": "<div class='thanks'>Dit punt is afgehandeld. Bedankt om mee te helpen!</div>",
|
||||||
"layerName": "Hier is misschien een {title}",
|
"layerName": "Hier is misschien een {title}",
|
||||||
|
"nearbyImagesIntro": "<h3>Afbeeldingen in de buurt</h3>De volgende afbeeldingen zijn in de buurt gemaakt en kunnen mogelijks helpen. {nearby_images(open)}",
|
||||||
"notFound": "Ik kon hier g{title} vinden - verwijder deze van de kaart",
|
"notFound": "Ik kon hier g{title} vinden - verwijder deze van de kaart",
|
||||||
"popupTitle": "Is hier {title}?"
|
"popupTitle": "Is hier {title}?"
|
||||||
},
|
},
|
||||||
|
|
|
@ -113,6 +113,9 @@
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
"1": {
|
||||||
|
"then": "No hi ha cap enllaça a Viquipèdia encara"
|
||||||
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"then": "No hi ha cap enllaça a Viquipèdia encara"
|
"then": "No hi ha cap enllaça a Viquipèdia encara"
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,6 +94,9 @@
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
"1": {
|
||||||
|
"then": "Der er endnu ikke linket til nogen Wikipedia-side"
|
||||||
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"then": "Der er endnu ikke linket til nogen Wikipedia-side"
|
"then": "Der er endnu ikke linket til nogen Wikipedia-side"
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,6 +113,9 @@
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
"1": {
|
||||||
|
"then": "Es wurde noch keine Wikipedia-Seite verlinkt"
|
||||||
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"then": "Es wurde noch keine Wikipedia-Seite verlinkt"
|
"then": "Es wurde noch keine Wikipedia-Seite verlinkt"
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,6 +113,9 @@
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
"1": {
|
||||||
|
"then": "No Wikipedia page has been linked yet"
|
||||||
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"then": "No Wikipedia page has been linked yet"
|
"then": "No Wikipedia page has been linked yet"
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,6 +113,9 @@
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
"1": {
|
||||||
|
"then": "Todavía no se ha enlazado una página de wikipedia"
|
||||||
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"then": "Todavía no se ha enlazado una página de wikipedia"
|
"then": "Todavía no se ha enlazado una página de wikipedia"
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,6 +113,9 @@
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
"1": {
|
||||||
|
"then": "Wala pang kawing ng Wikipedia page"
|
||||||
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"then": "Wala pang kawing ng Wikipedia page"
|
"then": "Wala pang kawing ng Wikipedia page"
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,6 +113,9 @@
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
"1": {
|
||||||
|
"then": "Pas encore de lien vers une page Wikipedia"
|
||||||
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"then": "Pas encore de lien vers une page Wikipedia"
|
"then": "Pas encore de lien vers une page Wikipedia"
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,6 +113,9 @@
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
"1": {
|
||||||
|
"then": "Még nincs Wikipédia-oldal belinkelve"
|
||||||
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"then": "Még nincs Wikipédia-oldal belinkelve"
|
"then": "Még nincs Wikipédia-oldal belinkelve"
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,9 @@
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
"1": {
|
||||||
|
"then": "Nessuna pagina Wikipedia è ancora stata collegata"
|
||||||
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"then": "Nessuna pagina Wikipedia è ancora stata collegata"
|
"then": "Nessuna pagina Wikipedia è ancora stata collegata"
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,9 @@
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
"1": {
|
||||||
|
"then": "ウィキペディアのページはまだリンクされていません"
|
||||||
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"then": "ウィキペディアのページはまだリンクされていません"
|
"then": "ウィキペディアのページはまだリンクされていません"
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,6 +96,9 @@
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
"1": {
|
||||||
|
"then": "Ingen Wikipedia-side lenket enda"
|
||||||
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"then": "Ingen Wikipedia-side lenket enda"
|
"then": "Ingen Wikipedia-side lenket enda"
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,6 +113,9 @@
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
"1": {
|
||||||
|
"then": "Er werd nog geen Wikipedia-pagina gekoppeld"
|
||||||
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"then": "Er werd nog geen Wikipedia-pagina gekoppeld"
|
"then": "Er werd nog geen Wikipedia-pagina gekoppeld"
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,9 @@
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
"1": {
|
||||||
|
"then": "Link do strony Wikipedii nie został jeszcze określony"
|
||||||
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"then": "Link do strony Wikipedii nie został jeszcze określony"
|
"then": "Link do strony Wikipedii nie został jeszcze określony"
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,6 +110,9 @@
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
"1": {
|
||||||
|
"then": "Ainda não foi vinculada nenhuma página da Wikipédia"
|
||||||
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"then": "Ainda não foi vinculada nenhuma página da Wikipédia"
|
"then": "Ainda não foi vinculada nenhuma página da Wikipédia"
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,9 @@
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
"1": {
|
||||||
|
"then": "Никакой страницы на Википедии не было прикреплено"
|
||||||
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"then": "Никакой страницы на Википедии не было прикреплено"
|
"then": "Никакой страницы на Википедии не было прикреплено"
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,9 @@
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
"1": {
|
||||||
|
"then": "Ingen Wikipedia-sida har länkats än"
|
||||||
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"then": "Ingen Wikipedia-sida har länkats än"
|
"then": "Ingen Wikipedia-sida har länkats än"
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,9 @@
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
"1": {
|
||||||
|
"then": "尚未有连接到的维基百科页面"
|
||||||
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"then": "尚未有连接到的维基百科页面"
|
"then": "尚未有连接到的维基百科页面"
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,6 +113,9 @@
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
"1": {
|
||||||
|
"then": "還沒有連結到維基百科頁面"
|
||||||
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"then": "還沒有連結到維基百科頁面"
|
"then": "還沒有連結到維基百科頁面"
|
||||||
}
|
}
|
||||||
|
|
28
test.ts
28
test.ts
|
@ -1,17 +1,19 @@
|
||||||
|
import {SelectOneNearbyImage} from "./UI/Popup/NearbyImages";
|
||||||
|
import Minimap from "./UI/Base/Minimap";
|
||||||
|
import MinimapImplementation from "./UI/Base/MinimapImplementation";
|
||||||
import {VariableUiElement} from "./UI/Base/VariableUIElement";
|
import {VariableUiElement} from "./UI/Base/VariableUIElement";
|
||||||
|
import Loc from "./Models/Loc";
|
||||||
import {UIEventSource} from "./Logic/UIEventSource";
|
import {UIEventSource} from "./Logic/UIEventSource";
|
||||||
import Wikidata from "./Logic/Web/Wikidata";
|
|
||||||
import Combine from "./UI/Base/Combine";
|
|
||||||
import {FixedUiElement} from "./UI/Base/FixedUiElement";
|
|
||||||
|
|
||||||
const result = UIEventSource.FromPromise(
|
MinimapImplementation.initialize()
|
||||||
Wikidata.searchAdvanced("WOlf", {
|
const map = Minimap.createMiniMap({
|
||||||
lang: "nl",
|
location: new UIEventSource<Loc>({
|
||||||
maxCount: 100,
|
lon: 3.22457,
|
||||||
instanceOf: 5
|
lat: 51.20876,
|
||||||
|
zoom: 18
|
||||||
})
|
})
|
||||||
)
|
})
|
||||||
result.addCallbackAndRunD(r => console.log(r))
|
map.AttachTo("extradiv")
|
||||||
new VariableUiElement(result.map(items =>new Combine( (items??[])?.map(i =>
|
map.SetStyle("height: 500px")
|
||||||
new FixedUiElement(JSON.stringify(i, null, " ")).SetClass("p-4 block")
|
|
||||||
)) )).SetClass("flex flex-col").AttachTo("maindiv")
|
new VariableUiElement(map.location.map( loc => new SelectOneNearbyImage( {...loc, radius: 50}))).AttachTo("maindiv")
|
|
@ -9,7 +9,7 @@ import {exec} from "child_process";
|
||||||
*/
|
*/
|
||||||
function detectInCode(forbidden: string, reason: string) {
|
function detectInCode(forbidden: string, reason: string) {
|
||||||
|
|
||||||
const excludedDirs = [".git", "node_modules", "dist", ".cache", ".parcel-cache", "assets"]
|
const excludedDirs = [".git", "node_modules", "dist", ".cache", ".parcel-cache", "assets", "vendor"]
|
||||||
|
|
||||||
exec("grep -n \"" + forbidden + "\" -r . " + excludedDirs.map(d => "--exclude-dir=" + d).join(" "), ((error, stdout, stderr) => {
|
exec("grep -n \"" + forbidden + "\" -r . " + excludedDirs.map(d => "--exclude-dir=" + d).join(" "), ((error, stdout, stderr) => {
|
||||||
if (error?.message?.startsWith("Command failed: grep")) {
|
if (error?.message?.startsWith("Command failed: grep")) {
|
||||||
|
|
7
vendor/P4C.min.js
vendored
Normal file
7
vendor/P4C.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue