forked from MapComplete/MapComplete
Full code cleanup
This commit is contained in:
parent
3a4a2a2016
commit
fa971ffbbf
300 changed files with 16352 additions and 19284 deletions
|
@ -19,13 +19,13 @@ export default class Constants {
|
|||
|
||||
|
||||
public static readonly added_by_default: string[] = ["gps_location", "gps_location_history", "home_location", "gps_track"]
|
||||
public static readonly no_include: string[] = ["conflation", "left_right_style", "split_point","current_view","matchpoint"]
|
||||
public static readonly no_include: string[] = ["conflation", "left_right_style", "split_point", "current_view", "matchpoint"]
|
||||
/**
|
||||
* Layer IDs of layers which have special properties through built-in hooks
|
||||
*/
|
||||
public static readonly priviliged_layers: string[] = [...Constants.added_by_default, "type_node", "note","import_candidate", ...Constants.no_include]
|
||||
public static readonly priviliged_layers: string[] = [...Constants.added_by_default, "type_node", "note", "import_candidate", ...Constants.no_include]
|
||||
|
||||
|
||||
|
||||
// The user journey states thresholds when a new feature gets unlocked
|
||||
public static userJourney = {
|
||||
moreScreenUnlock: 1,
|
||||
|
@ -54,13 +54,13 @@ export default class Constants {
|
|||
* The point closest to the changed feature will be considered and this distance will be tracked.
|
||||
* ALl these distances are used to calculate a nearby-score
|
||||
*/
|
||||
static nearbyVisitTime: number= 30 * 60;
|
||||
static nearbyVisitTime: number = 30 * 60;
|
||||
/**
|
||||
* If a user makes a change, the distance to the changed object is calculated.
|
||||
* If a user makes multiple changes, all these distances are put into multiple bins, depending on this distance.
|
||||
* For every bin, the totals are uploaded as metadata
|
||||
*/
|
||||
static distanceToChangeObjectBins = [25,50,100,500,1000,5000, Number.MAX_VALUE]
|
||||
static distanceToChangeObjectBins = [25, 50, 100, 500, 1000, 5000, Number.MAX_VALUE]
|
||||
static themeOrder = ["personal", "cyclofix", "hailhydrant", "bookcases", "toilets", "aed"];
|
||||
|
||||
private static isRetina(): boolean {
|
||||
|
|
|
@ -3,7 +3,8 @@ import LayerConfig from "./ThemeConfig/LayerConfig";
|
|||
import {TagsFilter} from "../Logic/Tags/TagsFilter";
|
||||
|
||||
export interface FilterState {
|
||||
currentFilter: TagsFilter, state: string | number
|
||||
currentFilter: TagsFilter,
|
||||
state: string | number
|
||||
}
|
||||
|
||||
export default interface FilteredLayer {
|
||||
|
|
|
@ -12,7 +12,7 @@ export default class CreateNoteImportLayer extends Conversion<LayerConfigJson, L
|
|||
*/
|
||||
private readonly _includeClosedNotesDays: number;
|
||||
|
||||
constructor(includeClosedNotesDays= 0) {
|
||||
constructor(includeClosedNotesDays = 0) {
|
||||
super([
|
||||
"Advanced conversion which deducts a layer showing all notes that are 'importable' (i.e. a note that contains a link to some MapComplete theme, with hash '#import').",
|
||||
"The import buttons and matches will be based on the presets of the given theme",
|
||||
|
@ -24,55 +24,55 @@ export default class CreateNoteImportLayer extends Conversion<LayerConfigJson, L
|
|||
const errors = []
|
||||
const warnings = []
|
||||
const t = Translations.t.importLayer;
|
||||
|
||||
|
||||
/**
|
||||
* The note itself will contain `tags=k=v;k=v;k=v;...
|
||||
* This must be matched with a regex.
|
||||
* This is a simple JSON-object as how it'll be put into the layerConfigJson directly
|
||||
*/
|
||||
const isShownIfAny : any[] = []
|
||||
const isShownIfAny: any[] = []
|
||||
const layer = new LayerConfig(layerJson, "while constructing a note-import layer")
|
||||
for (const preset of layer.presets) {
|
||||
const mustMatchAll = []
|
||||
for (const tag of preset.tags) {
|
||||
const key = tag.key
|
||||
const value = tag.value
|
||||
const condition = "_tags~(^|.*;)"+key+"\="+value+"($|;.*)"
|
||||
const condition = "_tags~(^|.*;)" + key + "\=" + value + "($|;.*)"
|
||||
mustMatchAll.push(condition)
|
||||
}
|
||||
isShownIfAny.push({and:mustMatchAll})
|
||||
isShownIfAny.push({and: mustMatchAll})
|
||||
}
|
||||
|
||||
const pointRenderings = (layerJson.mapRendering??[]).filter(r => r!== null && r["location"] !== undefined);
|
||||
const firstRender = <PointRenderingConfigJson>(pointRenderings [0])
|
||||
|
||||
const pointRenderings = (layerJson.mapRendering ?? []).filter(r => r !== null && r["location"] !== undefined);
|
||||
const firstRender = <PointRenderingConfigJson>(pointRenderings [0])
|
||||
const icon = firstRender.icon
|
||||
const iconBadges = []
|
||||
const title = layer.presets[0].title
|
||||
if(icon !== undefined){
|
||||
if (icon !== undefined) {
|
||||
iconBadges.push({
|
||||
if: {and:[]},
|
||||
then:icon
|
||||
if: {and: []},
|
||||
then: icon
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const importButton = {}
|
||||
{
|
||||
const translations = t.importButton.Subs({layerId: layer.id, title: layer.presets[0].title}).translations
|
||||
const translations = t.importButton.Subs({layerId: layer.id, title: layer.presets[0].title}).translations
|
||||
for (const key in translations) {
|
||||
importButton[key] = "{"+translations[key]+"}"
|
||||
}
|
||||
importButton[key] = "{" + translations[key] + "}"
|
||||
}
|
||||
}
|
||||
|
||||
function embed(prefix, translation: Translation, postfix){
|
||||
|
||||
function embed(prefix, translation: Translation, postfix) {
|
||||
const result = {}
|
||||
for (const language in translation.translations) {
|
||||
result[language] = prefix+translation.translations[language] + postfix
|
||||
result[language] = prefix + translation.translations[language] + postfix
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const result : LayerConfigJson = {
|
||||
"id": "note_import_"+layer.id,
|
||||
|
||||
const result: LayerConfigJson = {
|
||||
"id": "note_import_" + layer.id,
|
||||
// By disabling the name, the import-layers won't pollute the filter view "name": t.layerName.Subs({title: layer.title.render}).translations,
|
||||
"description": t.description.Subs({title: layer.title.render}).translations,
|
||||
"source": {
|
||||
|
@ -81,7 +81,7 @@ export default class CreateNoteImportLayer extends Conversion<LayerConfigJson, L
|
|||
"id~*"
|
||||
]
|
||||
},
|
||||
"geoJson": "https://api.openstreetmap.org/api/0.6/notes.json?limit=10000&closed="+this._includeClosedNotesDays+"&bbox={x_min},{y_min},{x_max},{y_max}",
|
||||
"geoJson": "https://api.openstreetmap.org/api/0.6/notes.json?limit=10000&closed=" + this._includeClosedNotesDays + "&bbox={x_min},{y_min},{x_max},{y_max}",
|
||||
"geoJsonZoomLevel": 10,
|
||||
"maxCacheAge": 0
|
||||
},
|
||||
|
@ -101,13 +101,15 @@ export default class CreateNoteImportLayer extends Conversion<LayerConfigJson, L
|
|||
"mappings": [
|
||||
{
|
||||
"if": "comments!~.*https://mapcomplete.osm.be.*",
|
||||
"then":"no"
|
||||
"then": "no"
|
||||
},
|
||||
{
|
||||
"if": {and:
|
||||
"if": {
|
||||
and:
|
||||
["_trigger_index~*",
|
||||
{or: isShownIfAny}
|
||||
]},
|
||||
]
|
||||
},
|
||||
"then": "yes"
|
||||
}
|
||||
]
|
||||
|
@ -135,12 +137,12 @@ export default class CreateNoteImportLayer extends Conversion<LayerConfigJson, L
|
|||
{
|
||||
"id": "close_note_",
|
||||
"render": embed(
|
||||
"{close_note(", t.notFound.Subs({title}),", ./assets/svg/close.svg, id, This feature does not exist)}" ),
|
||||
"{close_note(", t.notFound.Subs({title}), ", ./assets/svg/close.svg, id, This feature does not exist)}"),
|
||||
condition: "closed_at="
|
||||
},
|
||||
{
|
||||
"id": "close_note_mapped",
|
||||
"render": embed("{close_note(",t.alreadyMapped.Subs({title}), ", ./assets/svg/checkmark.svg, id, Already mapped)}"),
|
||||
"render": embed("{close_note(", t.alreadyMapped.Subs({title}), ", ./assets/svg/checkmark.svg, id, Already mapped)}"),
|
||||
condition: "closed_at="
|
||||
},
|
||||
{
|
||||
|
@ -164,9 +166,9 @@ export default class CreateNoteImportLayer extends Conversion<LayerConfigJson, L
|
|||
],
|
||||
"icon": {
|
||||
"render": "circle:white;help:black",
|
||||
mappings:[{
|
||||
if: {or:["closed_at~*","_imported=yes"]},
|
||||
then:"circle:white;checkmark:black"
|
||||
mappings: [{
|
||||
if: {or: ["closed_at~*", "_imported=yes"]},
|
||||
then: "circle:white;checkmark:black"
|
||||
}]
|
||||
},
|
||||
iconBadges,
|
||||
|
@ -174,8 +176,8 @@ export default class CreateNoteImportLayer extends Conversion<LayerConfigJson, L
|
|||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return {
|
||||
result,
|
||||
errors, warnings
|
||||
|
|
|
@ -8,7 +8,6 @@ import {AllKnownLayouts} from "../../../Customizations/AllKnownLayouts";
|
|||
import CreateNoteImportLayer from "./CreateNoteImportLayer";
|
||||
import LayerConfig from "../LayerConfig";
|
||||
import {TagRenderingConfigJson} from "../Json/TagRenderingConfigJson";
|
||||
import {Translation} from "../../../UI/i18n/Translation";
|
||||
import {SubstitutedTranslation} from "../../../UI/SubstitutedTranslation";
|
||||
import DependencyCalculator from "../DependencyCalculator";
|
||||
|
||||
|
@ -126,12 +125,12 @@ class AddImportLayers extends DesugaringStep<LayoutConfigJson> {
|
|||
convert(state: DesugaringContext, json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[]; warnings: string[] } {
|
||||
const errors = []
|
||||
const warnings = []
|
||||
|
||||
|
||||
json = {...json}
|
||||
const allLayers: LayerConfigJson[] = <LayerConfigJson[]>json.layers;
|
||||
json.layers = [...json.layers]
|
||||
|
||||
|
||||
|
||||
const creator = new CreateNoteImportLayer()
|
||||
for (let i1 = 0; i1 < allLayers.length; i1++) {
|
||||
const layer = allLayers[i1];
|
||||
|
@ -191,10 +190,10 @@ export class AddMiniMap extends DesugaringStep<LayerConfigJson> {
|
|||
static hasMinimap(renderingConfig: TagRenderingConfigJson): boolean {
|
||||
const translations: any[] = Utils.NoNull([renderingConfig.render, ...(renderingConfig.mappings ?? []).map(m => m.then)]);
|
||||
for (let translation of translations) {
|
||||
if(typeof translation == "string"){
|
||||
if (typeof translation == "string") {
|
||||
translation = {"*": translation}
|
||||
}
|
||||
|
||||
|
||||
for (const key in translation) {
|
||||
if (!translation.hasOwnProperty(key)) {
|
||||
continue
|
||||
|
@ -341,7 +340,7 @@ export class PrepareTheme extends Fuse<LayoutConfigJson> {
|
|||
constructor() {
|
||||
super(
|
||||
"Fully prepares and expands a theme",
|
||||
|
||||
|
||||
new OnEveryConcat("layers", new SubstituteLayer()),
|
||||
new SetDefault("socialImage", "assets/SocialImage.png", true),
|
||||
new OnEvery("layers", new PrepareLayer()),
|
||||
|
|
|
@ -8,7 +8,7 @@ export default class DependencyCalculator {
|
|||
|
||||
public static GetTagRenderingDependencies(tr: TagRenderingConfig): string[] {
|
||||
|
||||
if(tr === undefined){
|
||||
if (tr === undefined) {
|
||||
throw "Got undefined tag rendering in getTagRenderingDependencies"
|
||||
}
|
||||
const deps: string[] = []
|
||||
|
@ -74,11 +74,11 @@ export default class DependencyCalculator {
|
|||
getFeatureById: _ => undefined,
|
||||
getFeaturesWithin: (layerId, _) => {
|
||||
|
||||
if(layerId === '*'){
|
||||
if (layerId === '*') {
|
||||
// This is a wildcard
|
||||
return []
|
||||
}
|
||||
|
||||
|
||||
// The important line: steal the dependencies!
|
||||
deps.push({
|
||||
neededLayer: layerId, reason: "A calculated tag loads features from this layer",
|
||||
|
|
|
@ -12,8 +12,8 @@ export default interface FilterConfigJson {
|
|||
* Filtering is done based on the given osmTags that are compared to the objects in that layer.
|
||||
*/
|
||||
options: {
|
||||
question: string | any;
|
||||
osmTags?: AndOrTagConfigJson | string,
|
||||
question: string | any;
|
||||
osmTags?: AndOrTagConfigJson | string,
|
||||
fields?: {
|
||||
name: string,
|
||||
type?: string | "string"
|
||||
|
|
|
@ -84,13 +84,13 @@ export interface LayerConfigJson {
|
|||
*
|
||||
* The specified tags are evaluated lazily. E.g. if a calculated tag is only used in the popup (e.g. the number of nearby features),
|
||||
* the expensive calculation will only be performed then for that feature. This avoids clogging up the contributors PC when all features are loaded.
|
||||
*
|
||||
*
|
||||
* If a tag has to be evaluated strictly, use ':=' instead:
|
||||
*
|
||||
*
|
||||
* [
|
||||
* "_some_key:=some_javascript_expression"
|
||||
* ]
|
||||
*
|
||||
*
|
||||
*/
|
||||
calculatedTags?: string[];
|
||||
|
||||
|
@ -124,7 +124,7 @@ export interface LayerConfigJson {
|
|||
* can be used to hide a layer from start, or to load the layer but only to show it where appropriate (e.g. for snapping to it)
|
||||
*/
|
||||
shownByDefault?: true | boolean;
|
||||
|
||||
|
||||
/**
|
||||
* The zoom level at which point the data is hidden again
|
||||
* Default: 100 (thus: always visible
|
||||
|
|
|
@ -9,7 +9,7 @@ export interface TagRenderingConfigJson {
|
|||
/**
|
||||
* The id of the tagrendering, should be an unique string.
|
||||
* Used to keep the translations in sync. Only used in the tagRenderings-array of a layerConfig, not requered otherwise.
|
||||
*
|
||||
*
|
||||
* Use 'questions' to trigger the question box of this group (if a group is defined)
|
||||
*/
|
||||
id?: string,
|
||||
|
|
|
@ -67,9 +67,9 @@ export default class LayerConfig extends WithContextLoader {
|
|||
context = context + "." + json.id;
|
||||
super(json, context)
|
||||
this.id = json.id;
|
||||
|
||||
if(json.id === undefined){
|
||||
throw "Not a valid layer: id is undefined: "+JSON.stringify(json)
|
||||
|
||||
if (json.id === undefined) {
|
||||
throw "Not a valid layer: id is undefined: " + JSON.stringify(json)
|
||||
}
|
||||
|
||||
if (json.source === undefined) {
|
||||
|
@ -259,7 +259,7 @@ export default class LayerConfig extends WithContextLoader {
|
|||
}
|
||||
|
||||
|
||||
this.titleIcons = this.ParseTagRenderings((<TagRenderingConfigJson[]> json.titleIcons), {
|
||||
this.titleIcons = this.ParseTagRenderings((<TagRenderingConfigJson[]>json.titleIcons), {
|
||||
readOnlyMode: true
|
||||
});
|
||||
|
||||
|
@ -375,9 +375,9 @@ export default class LayerConfig extends WithContextLoader {
|
|||
return [
|
||||
new Combine([
|
||||
new Link(
|
||||
"<img src='https://mapcomplete.osm.be/assets/svg/statistics.svg' height='18px'>",
|
||||
"https://taginfo.openstreetmap.org/keys/"+values.key+"#values"
|
||||
),Link.OsmWiki(values.key)
|
||||
"<img src='https://mapcomplete.osm.be/assets/svg/statistics.svg' height='18px'>",
|
||||
"https://taginfo.openstreetmap.org/keys/" + values.key + "#values"
|
||||
), Link.OsmWiki(values.key)
|
||||
]),
|
||||
values.type === undefined ? "Multiple choice" : new Link(values.type, "../SpecialInputElements.md#" + values.type),
|
||||
new Combine(embedded)
|
||||
|
|
|
@ -54,12 +54,12 @@ export default class LayoutConfig {
|
|||
constructor(json: LayoutConfigJson, official = true, context?: string) {
|
||||
this.official = official;
|
||||
this.id = json.id;
|
||||
if(official){
|
||||
if(json.id.toLowerCase() !== json.id){
|
||||
throw "The id of a theme should be lowercase: "+json.id
|
||||
if (official) {
|
||||
if (json.id.toLowerCase() !== json.id) {
|
||||
throw "The id of a theme should be lowercase: " + json.id
|
||||
}
|
||||
if(json.id.match(/[a-z0-9-_]/) == null){
|
||||
throw "The id of a theme should match [a-z0-9-_]*: "+json.id
|
||||
if (json.id.match(/[a-z0-9-_]/) == null) {
|
||||
throw "The id of a theme should match [a-z0-9-_]*: " + json.id
|
||||
}
|
||||
}
|
||||
context = (context ?? "") + "." + this.id;
|
||||
|
@ -102,9 +102,9 @@ export default class LayoutConfig {
|
|||
this.descriptionTail = json.descriptionTail === undefined ? undefined : new Translation(json.descriptionTail, context + ".descriptionTail");
|
||||
this.icon = json.icon;
|
||||
this.socialImage = json.socialImage;
|
||||
if(this.socialImage === null || this.socialImage === "" || this.socialImage === undefined){
|
||||
if(official){
|
||||
throw "Theme "+json.id+" has no social image defined"
|
||||
if (this.socialImage === null || this.socialImage === "" || this.socialImage === undefined) {
|
||||
if (official) {
|
||||
throw "Theme " + json.id + " has no social image defined"
|
||||
}
|
||||
}
|
||||
this.startZoom = json.startZoom;
|
||||
|
|
|
@ -44,7 +44,9 @@ export default class LineRenderingConfig extends WithContextLoader {
|
|||
if (tags === undefined) {
|
||||
return deflt
|
||||
}
|
||||
if(tr === undefined){return deflt}
|
||||
if (tr === undefined) {
|
||||
return deflt
|
||||
}
|
||||
const str = tr?.GetRenderValue(tags)?.txt ?? deflt;
|
||||
if (str === "") {
|
||||
return deflt
|
||||
|
@ -59,7 +61,7 @@ export default class LineRenderingConfig extends WithContextLoader {
|
|||
"--catch-detail-color"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const style = {
|
||||
color,
|
||||
dashArray,
|
||||
|
@ -73,13 +75,13 @@ export default class LineRenderingConfig extends WithContextLoader {
|
|||
if (fillStr !== undefined && fillStr !== "") {
|
||||
style["fill"] = fillStr === "yes" || fillStr === "true"
|
||||
}
|
||||
|
||||
|
||||
const fillColorStr = render(this.fillColor, undefined)
|
||||
if(fillColorStr !== undefined){
|
||||
if (fillColorStr !== undefined) {
|
||||
style["fillColor"] = fillColorStr
|
||||
}
|
||||
return style
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -69,7 +69,7 @@ export default class PointRenderingConfig extends WithContextLoader {
|
|||
if (iconPath !== undefined && iconPath.startsWith(Utils.assets_path)) {
|
||||
const iconKey = iconPath.substr(Utils.assets_path.length);
|
||||
if (Svg.All[iconKey] === undefined) {
|
||||
throw context+": builtin SVG asset not found: " + iconPath;
|
||||
throw context + ": builtin SVG asset not found: " + iconPath;
|
||||
}
|
||||
}
|
||||
this.iconSize = this.tr("iconSize", "40,40,center");
|
||||
|
@ -209,16 +209,16 @@ export default class PointRenderingConfig extends WithContextLoader {
|
|||
} else {
|
||||
iconAndBadges.SetClass("w-full h-full")
|
||||
}
|
||||
|
||||
|
||||
let label = this.GetLabel(tags)
|
||||
let htmlEl : BaseUIElement;
|
||||
if(icon === undefined && label === undefined){
|
||||
let htmlEl: BaseUIElement;
|
||||
if (icon === undefined && label === undefined) {
|
||||
htmlEl = undefined
|
||||
}else if(icon === undefined){
|
||||
} else if (icon === undefined) {
|
||||
htmlEl = new Combine([label])
|
||||
}else if(label === undefined){
|
||||
htmlEl = new Combine([iconAndBadges])
|
||||
}else {
|
||||
} else if (label === undefined) {
|
||||
htmlEl = new Combine([iconAndBadges])
|
||||
} else {
|
||||
htmlEl = new Combine([iconAndBadges, label]).SetStyle("flex flex-col")
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ export default class TagRenderingConfig {
|
|||
readonly hideInAnswer: boolean | TagsFilter
|
||||
readonly addExtraTags: Tag[]
|
||||
}[]
|
||||
|
||||
constructor(json: string | TagRenderingConfigJson, context?: string) {
|
||||
if (json === undefined) {
|
||||
throw "Initing a TagRenderingConfig with undefined in " + context;
|
||||
|
@ -373,8 +374,8 @@ export default class TagRenderingConfig {
|
|||
return mapping.then;
|
||||
}
|
||||
if (mapping.if.matchesProperties(tags)) {
|
||||
if(this.id === "uk_addresses_placename"){
|
||||
console.log("Matched",mapping.if,"with ",tags["addr:place"])
|
||||
if (this.id === "uk_addresses_placename") {
|
||||
console.log("Matched", mapping.if, "with ", tags["addr:place"])
|
||||
}
|
||||
return mapping.then;
|
||||
}
|
||||
|
@ -487,12 +488,11 @@ export default class TagRenderingConfig {
|
|||
mappings = new List(
|
||||
this.mappings.map(m => {
|
||||
let txt = "**" + m.then.txt + "** corresponds with " + m.if.asHumanString(true, false, {});
|
||||
if(m.hideInAnswer === true)
|
||||
{
|
||||
if (m.hideInAnswer === true) {
|
||||
txt += "_This option cannot be chosen as answer_"
|
||||
}
|
||||
if(m.ifnot !== undefined){
|
||||
txt += "Unselecting this answer will add "+m.ifnot.asHumanString(true, false, {})
|
||||
if (m.ifnot !== undefined) {
|
||||
txt += "Unselecting this answer will add " + m.ifnot.asHumanString(true, false, {})
|
||||
}
|
||||
return txt;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue