forked from MapComplete/MapComplete
		
	Studio: more finetuning, first version working
This commit is contained in:
		
							parent
							
								
									c4d4a57a08
								
							
						
					
					
						commit
						ac1e7c7f06
					
				
					 39 changed files with 2971 additions and 7187 deletions
				
			
		|  | @ -153,9 +153,6 @@ export class Pipe<TIn, TInter, TOut> extends Conversion<TIn, TOut> { | |||
| 
 | ||||
|     convert(json: TIn, context: ConversionContext): TOut { | ||||
|         const r0 = this._step0.convert(json, context.inOperation(this._step0.name)) | ||||
|         if (context.hasErrors()) { | ||||
|             return undefined | ||||
|         } | ||||
|         return this._step1.convert(r0, context.inOperation(this._step1.name)) | ||||
|     } | ||||
| } | ||||
|  | @ -308,9 +305,6 @@ export class Fuse<T> extends DesugaringStep<T> { | |||
|                 if (r === undefined || r === null) { | ||||
|                     break | ||||
|                 } | ||||
|                 if (context.hasErrors()) { | ||||
|                     break | ||||
|                 } | ||||
|                 json = r | ||||
|             } catch (e) { | ||||
|                 console.error("Step " + step.name + " failed due to ", e, e.stack) | ||||
|  |  | |||
|  | @ -731,6 +731,77 @@ export class ValidateLayer extends Conversion< | |||
|             return null | ||||
|         } | ||||
| 
 | ||||
|         if (typeof json === "string") { | ||||
|             context.err( | ||||
|                 `Not a valid layer: the layerConfig is a string. 'npm run generate:layeroverview' might be needed` | ||||
|             ) | ||||
|             return undefined | ||||
|         } | ||||
| 
 | ||||
|         if (json.id === undefined) { | ||||
|             context.err(`Not a valid layer: id is undefined: ${JSON.stringify(json)}`) | ||||
|         } | ||||
| 
 | ||||
|         if (json.source === undefined) { | ||||
|             context.enter("source").err("No source section is defined") | ||||
|         } else { | ||||
|             if (json.source === "special" || json.source === "special:library") { | ||||
|             } else if (json.source && json.source["osmTags"] === undefined) { | ||||
|                 context | ||||
|                     .enters("source", "osmTags") | ||||
|                     .err( | ||||
|                         "No osmTags defined in the source section - these should always be present, even for geojson layer" | ||||
|                     ) | ||||
|             } else { | ||||
|                 const osmTags = TagUtils.Tag(json.source["osmTags"], context + "source.osmTags") | ||||
|                 if (osmTags.isNegative()) { | ||||
|                     context | ||||
|                         .enters("source", "osmTags") | ||||
|                         .err( | ||||
|                             "The source states tags which give a very wide selection: it only uses negative expressions, which will result in too much and unexpected data. Add at least one required tag. The tags are:\n\t" + | ||||
|                                 osmTags.asHumanString(false, false, {}) | ||||
|                         ) | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (json.source["geoJsonSource"] !== undefined) { | ||||
|                 context | ||||
|                     .enters("source", "geoJsonSource") | ||||
|                     .err("Use 'geoJson' instead of 'geoJsonSource'") | ||||
|             } | ||||
| 
 | ||||
|             if (json.source["geojson"] !== undefined) { | ||||
|                 context | ||||
|                     .enters("source", "geojson") | ||||
|                     .err("Use 'geoJson' instead of 'geojson' (the J is a capital letter)") | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (json.id?.toLowerCase() !== json.id) { | ||||
|             context.enter("id").err(`The id of a layer should be lowercase: ${json.id}`) | ||||
|         } | ||||
|         if (json.id?.match(/[a-z0-9-_]/) == null) { | ||||
|             context.enter("id").err(`The id of a layer should match [a-z0-9-_]*: ${json.id}`) | ||||
|         } | ||||
| 
 | ||||
|         if ( | ||||
|             json.syncSelection !== undefined && | ||||
|             LayerConfig.syncSelectionAllowed.indexOf(json.syncSelection) < 0 | ||||
|         ) { | ||||
|             context | ||||
|                 .enter("syncSelection") | ||||
|                 .err( | ||||
|                     "Invalid sync-selection: must be one of " + | ||||
|                         LayerConfig.syncSelectionAllowed.map((v) => `'${v}'`).join(", ") + | ||||
|                         " but got '" + | ||||
|                         json.syncSelection + | ||||
|                         "'" | ||||
|                 ) | ||||
|         } | ||||
| 
 | ||||
|         if (context.hasErrors()) { | ||||
|             return undefined | ||||
|         } | ||||
|         let layerConfig: LayerConfig | ||||
|         try { | ||||
|             layerConfig = new LayerConfig(json, "validation", true) | ||||
|  |  | |||
|  | @ -136,7 +136,7 @@ export interface LayerConfigJson { | |||
|      * There are a few extra functions available. Refer to <a>Docs/CalculatedTags.md</a> for more information | ||||
|      * The functions will be run in order, e.g. | ||||
|      * [ | ||||
|      *  "_max_overlap_m2=Math.max(...feat.overlapsWith("someOtherLayer").map(o => o.overlap)) | ||||
|  Not found...    *  "_max_overlap_m2=Math.max(...feat.overlapsWith("someOtherLayer").map(o => o.overlap)) | ||||
|      *  "_max_overlap_ratio=Number(feat._max_overlap_m2)/feat.area | ||||
|      * ] | ||||
|      * | ||||
|  | @ -208,6 +208,7 @@ export interface LayerConfigJson { | |||
|      * types: use a fixed translation ; Use a dynamic tagRendering ; hidden | ||||
|      * typesdefault: 1 | ||||
|      * type: translation | ||||
|      * inline: {translated{value}} | ||||
|      */ | ||||
|     title?: TagRenderingConfigJson | Translatable | ||||
| 
 | ||||
|  | @ -309,7 +310,7 @@ export interface LayerConfigJson { | |||
|          * Do _not_ indicate 'new': 'add a new shop here' is incorrect, as the shop might have existed forever, it could just be unmapped! | ||||
|          * | ||||
|          * question: What is the word to describe this object? | ||||
|          * inline: Add <b>{value}</b> here | ||||
|          * inline: Add <b>{translated(value)}</b> here | ||||
|          */ | ||||
|         title: Translatable | ||||
|         /** | ||||
|  | @ -360,7 +361,7 @@ export interface LayerConfigJson { | |||
|          * If further away, it'll be placed in the center of the location input | ||||
|          * Distance in meter | ||||
|          * | ||||
|          * Default: 10 | ||||
|          * ifunset: Do not snap to a layer | ||||
|          */ | ||||
|         maxSnapDistance?: number | ||||
|     }[] | ||||
|  | @ -463,6 +464,7 @@ export interface LayerConfigJson { | |||
|      * types: Use an advanced delete configuration ; boolean | ||||
|      * iftrue: Allow deletion | ||||
|      * iffalse: Do not allow deletion | ||||
|      * ifunset: Do not allow deletion | ||||
|      * | ||||
|      **/ | ||||
|     deletion?: DeleteConfigJson | boolean | ||||
|  |  | |||
|  | @ -18,7 +18,6 @@ export default interface LineRenderingConfigJson { | |||
|      * types: dynamic value ; string | ||||
|      * title: Line Colour | ||||
|      * inline: The line colour always is <b>{value}</b> | ||||
|      * ifunset: Round ending | ||||
|      * type: color | ||||
|      * | ||||
|      */ | ||||
|  |  | |||
|  | @ -91,6 +91,7 @@ export default interface PointRenderingConfigJson { | |||
|      * If the icon is undefined, then the label is shown in the center of the feature. | ||||
|      * types: Dynamic value | string | ||||
|      * inline: Always show label <b>{value}</b> beneath the marker | ||||
|      * ifunset: Do not show a label beneath the marker | ||||
|      */ | ||||
|     label?: string | TagRenderingConfigJson | ||||
| 
 | ||||
|  | @ -100,6 +101,8 @@ export default interface PointRenderingConfigJson { | |||
|      * This will be applied to the _container_ containing both the marker and the label | ||||
|      * inline: Apply CSS-style <b>{value}</b> to the _entire marker_ | ||||
|      * types: Dynamic value ; string | ||||
|      * ifunset: Do not apply extra CSS element to the entire marker | ||||
|      * | ||||
|      */ | ||||
|     css?: string | TagRenderingConfigJson | ||||
| 
 | ||||
|  | @ -111,7 +114,9 @@ export default interface PointRenderingConfigJson { | |||
|      * You can use most Tailwind-css classes, see https://tailwindcss.com/ for more information
 | ||||
|      * For example: `center bg-gray-500 mx-2 my-1 rounded-full` | ||||
|      * inline: Apply CSS-classes <b>{value}</b> to the entire container | ||||
|      * ifunset: Do not apply extra CSS-classes to the label | ||||
|      * types: Dynamic value ; string | ||||
|      * ifunset: Do not apply extra CSS-classes to the entire marker | ||||
|      */ | ||||
|     cssClasses?: string | TagRenderingConfigJson | ||||
| 
 | ||||
|  | @ -120,6 +125,8 @@ export default interface PointRenderingConfigJson { | |||
|      * You can set the css-properties here, e.g. `background: red; font-size: 12px; ` | ||||
|      * inline: Apply CSS-style <b>{value}</b> to the label | ||||
|      * types: Dynamic value ; string | ||||
|      * ifunset: Do not apply extra CSS-labels to the label | ||||
|      * | ||||
|      */ | ||||
|     labelCss?: TagRenderingConfigJson | string | ||||
| 
 | ||||
|  | @ -131,6 +138,7 @@ export default interface PointRenderingConfigJson { | |||
|      * For example: `center bg-gray-500 mx-2 my-1 rounded-full` | ||||
|      * inline: Apply CSS-classes <b>{value}</b> to the label | ||||
|      * types: Dynamic value ; string | ||||
|      * ifunset: Do not apply extra CSS-classes to the label | ||||
|      */ | ||||
|     labelCssClasses?: string | TagRenderingConfigJson | ||||
| 
 | ||||
|  |  | |||
|  | @ -187,7 +187,7 @@ export interface QuestionableTagRenderingConfigJson extends TagRenderingConfigJs | |||
|      * | ||||
|      * question: Should a contributor be allowed to select multiple mappings? | ||||
|      * | ||||
|      * iftrue: allow to select multiple mappigns | ||||
|      * iftrue: allow to select multiple mappings | ||||
|      * iffalse: only allow to select a single mapping | ||||
|      * ifunset: only allow to select a single mapping | ||||
|      */ | ||||
|  | @ -250,7 +250,7 @@ export interface QuestionableTagRenderingConfigJson extends TagRenderingConfigJs | |||
|          * For example, if a feature has `shop=yes`, the question 'what type of shop is this?' should still asked | ||||
|          * ifunset: The question will be considered answered if any value is set for the key | ||||
|          */ | ||||
|         invalidValues?: string[] | ||||
|         invalidValues?: TagConfigJson | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  |  | |||
|  | @ -75,65 +75,17 @@ export default class LayerConfig extends WithContextLoader { | |||
|         const translationContext = "layers:" + json.id | ||||
|         super(json, context) | ||||
|         this.id = json.id | ||||
|         if (typeof json === "string") { | ||||
|             throw `Not a valid layer: the layerConfig is a string. 'npm run generate:layeroverview' might be needed (at ${context})` | ||||
|         } | ||||
| 
 | ||||
|         if (json.id === undefined) { | ||||
|             throw `Not a valid layer: id is undefined: ${JSON.stringify(json)} (At ${context})` | ||||
|         } | ||||
| 
 | ||||
|         if (json.source === undefined) { | ||||
|             throw "Layer " + this.id + " does not define a source section (" + context + ")" | ||||
|         } | ||||
| 
 | ||||
|         if (json.source === "special" || json.source === "special:library") { | ||||
|             this.source = null | ||||
|         } else if (json.source["osmTags"] === undefined) { | ||||
|             throw ( | ||||
|                 "Layer " + | ||||
|                 this.id + | ||||
|                 " does not define a osmTags in the source section - these should always be present, even for geojson layers (" + | ||||
|                 context + | ||||
|                 ")" | ||||
|             ) | ||||
|         } | ||||
| 
 | ||||
|         if (json.id.toLowerCase() !== json.id) { | ||||
|             throw `${context}: The id of a layer should be lowercase: ${json.id}` | ||||
|         } | ||||
|         if (json.id.match(/[a-z0-9-_]/) == null) { | ||||
|             throw `${context}: The id of a layer should match [a-z0-9-_]*: ${json.id}` | ||||
|         } | ||||
| 
 | ||||
|         if ( | ||||
|             json.syncSelection !== undefined && | ||||
|             LayerConfig.syncSelectionAllowed.indexOf(json.syncSelection) < 0 | ||||
|         ) { | ||||
|             throw ( | ||||
|                 context + | ||||
|                 " Invalid sync-selection: must be one of " + | ||||
|                 LayerConfig.syncSelectionAllowed.map((v) => `'${v}'`).join(", ") + | ||||
|                 " but got '" + | ||||
|                 json.syncSelection + | ||||
|                 "'" | ||||
|             ) | ||||
|         } | ||||
|         this.syncSelection = json.syncSelection ?? "no" | ||||
|         if (typeof json.source !== "string") { | ||||
|             this.maxAgeOfCache = json.source["maxCacheAge"] ?? 24 * 60 * 60 * 30 | ||||
|             const osmTags = TagUtils.Tag(json.source["osmTags"], context + "source.osmTags") | ||||
|             if (osmTags.isNegative()) { | ||||
|                 throw ( | ||||
|                     context + | ||||
|                     "The source states tags which give a very wide selection: it only uses negative expressions, which will result in too much and unexpected data. Add at least one required tag. The tags are:\n\t" + | ||||
|                     osmTags.asHumanString(false, false, {}) | ||||
|                 ) | ||||
|             } | ||||
| 
 | ||||
|             this.source = new SourceConfig( | ||||
|                 { | ||||
|                     osmTags: osmTags, | ||||
|                     osmTags: TagUtils.Tag(json.source["osmTags"], context + "source.osmTags"), | ||||
|                     geojsonSource: json.source["geoJson"], | ||||
|                     geojsonSourceLevel: json.source["geoJsonZoomLevel"], | ||||
|                     overpassScript: json.source["overpassScript"], | ||||
|  | @ -145,14 +97,6 @@ export default class LayerConfig extends WithContextLoader { | |||
|             ) | ||||
|         } | ||||
| 
 | ||||
|         if (json.source["geoJsonSource"] !== undefined) { | ||||
|             throw context + "Use 'geoJson' instead of 'geoJsonSource'" | ||||
|         } | ||||
| 
 | ||||
|         if (json.source["geojson"] !== undefined) { | ||||
|             throw context + "Use 'geoJson' instead of 'geojson' (the J is a capital letter)" | ||||
|         } | ||||
| 
 | ||||
|         this.allowSplit = json.allowSplit ?? false | ||||
|         this.name = Translations.T(json.name, translationContext + ".name") | ||||
|         if (json.units !== undefined && !Array.isArray(json.units)) { | ||||
|  |  | |||
|  | @ -53,6 +53,7 @@ export default class TagRenderingConfig { | |||
|     public readonly question?: TypedTranslation<object> | ||||
|     public readonly questionhint?: TypedTranslation<object> | ||||
|     public readonly condition?: TagsFilter | ||||
|     public readonly invalidValues?: TagsFilter | ||||
|     /** | ||||
|      * Evaluated against the current 'usersettings'-state | ||||
|      */ | ||||
|  | @ -133,6 +134,9 @@ export default class TagRenderingConfig { | |||
|         this.questionhint = Translations.T(json.questionHint, translationKey + ".questionHint") | ||||
|         this.description = Translations.T(json.description, translationKey + ".description") | ||||
|         this.condition = TagUtils.Tag(json.condition ?? { and: [] }, `${context}.condition`) | ||||
|         this.invalidValues = json["invalidValues"] | ||||
|             ? TagUtils.Tag(json["invalidValues"], `${context}.invalidValues`) | ||||
|             : undefined | ||||
|         if (typeof json.icon === "string") { | ||||
|             this.renderIcon = json.icon | ||||
|             this.renderIconClass = "small" | ||||
|  | @ -469,9 +473,12 @@ export default class TagRenderingConfig { | |||
|      */ | ||||
|     public IsKnown(tags: Record<string, string>): boolean { | ||||
|         if (this.condition && !this.condition.matchesProperties(tags)) { | ||||
|             // Filtered away by the condition, so it is kindof known
 | ||||
|             // Filtered away by the condition, so it is kind of known
 | ||||
|             return true | ||||
|         } | ||||
|         if (this.invalidValues && this.invalidValues.matchesProperties(tags)) { | ||||
|             return false | ||||
|         } | ||||
|         if (this.multiAnswer) { | ||||
|             for (const m of this.mappings ?? []) { | ||||
|                 if (TagUtils.MatchesMultiAnswer(m.if, tags)) { | ||||
|  | @ -482,6 +489,9 @@ export default class TagRenderingConfig { | |||
|             const free = this.freeform?.key | ||||
|             if (free !== undefined) { | ||||
|                 const value = tags[free] | ||||
|                 if (typeof value === "object") { | ||||
|                     return Object.keys(value).length > 0 | ||||
|                 } | ||||
|                 return value !== undefined && value !== "" | ||||
|             } | ||||
|             return false | ||||
|  | @ -679,7 +689,9 @@ export default class TagRenderingConfig { | |||
|         multiSelectedMapping: boolean[] | undefined, | ||||
|         currentProperties: Record<string, string> | ||||
|     ): UploadableTag { | ||||
|         freeformValue = freeformValue?.trim() | ||||
|         if (typeof freeformValue === "string") { | ||||
|             freeformValue = freeformValue?.trim() | ||||
|         } | ||||
|         const validator = Validators.get(<ValidatorType>this.freeform?.type) | ||||
|         if (validator && freeformValue) { | ||||
|             freeformValue = validator.reformat(freeformValue, () => currentProperties["_country"]) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue