Skip to content

Commit 0f71747

Browse files
committed
define a schema for relation members
1 parent 54c2a7d commit 0f71747

File tree

3 files changed

+111
-6
lines changed

3 files changed

+111
-6
lines changed

lib/build.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import chalk from 'chalk';
22
import fs from 'fs';
33
import { globSync } from 'glob';
4-
import jsonschema from 'jsonschema';
4+
import { Validator } from 'jsonschema';
55
import path from 'path';
66
import shell from 'shelljs';
77
import YAML from 'js-yaml';
@@ -21,6 +21,8 @@ const discardedSchema = require('../schemas/discarded.json');
2121

2222
let _currBuild = null;
2323

24+
const jsonschema = new Validator();
25+
2426
function validateData(options) {
2527
const START = '🔬 ' + chalk.yellow('Validating schema...');
2628
const END = '👍 ' + chalk.green('schema okay');
@@ -203,6 +205,9 @@ function read(f) {
203205

204206

205207
function validateSchema(file, instance, schema) {
208+
// add this schema to the cache, so $ref can be resolved faster
209+
jsonschema.addSchema(schema);
210+
206211
let validationErrors = jsonschema.validate(instance, schema).errors;
207212

208213
if (validationErrors.length) {
@@ -362,6 +367,15 @@ function generatePresets(dataDir, tstrings, searchableFieldIDs, listReusedIcons)
362367
if (!icons[icon]) icons[icon] = [];
363368
icons[icon].push(id);
364369
}
370+
371+
if (preset.relation) {
372+
tstrings.presets[id].relation_roles = {};
373+
for (const member of preset.relation.members) {
374+
for (const role in member.roles) {
375+
tstrings.presets[id].relation_roles[role] = member.roles[role];
376+
}
377+
}
378+
}
365379
});
366380

367381
if (listReusedIcons) {
@@ -454,8 +468,10 @@ function generateTranslations(fields, presets, tstrings, searchableFieldIDs) {
454468
let tags = preset.tags || {};
455469
let keys = Object.keys(tags);
456470

471+
const tagsString = keys.map(k => `${k}=${tags[k]}`).join(' + ');
472+
457473
if (keys.length) {
458-
yamlPreset['#name'] = keys.map(k => `${k}=${tags[k]}`).join(' + ');
474+
yamlPreset['#name'] = tagsString;
459475
if (yamlPreset.aliases) {
460476
yamlPreset['#name'] += ' | ' + yamlPreset.aliases.split('\n').join(', ');
461477
}
@@ -466,6 +482,12 @@ function generateTranslations(fields, presets, tstrings, searchableFieldIDs) {
466482
yamlPreset['#name'] += ` | Local preset for countries ${preset.locationSet.include.map(country => `"${country.toUpperCase()}"`).join(', ')}`;
467483
}
468484

485+
if (yamlPreset.relation_roles) {
486+
for (const role in yamlPreset.relation_roles) {
487+
yamlPreset.relation_roles[`#${role}`] = `Relation role “${role}” when used with ${tagsString}`;
488+
}
489+
}
490+
469491
if (preset.searchable !== false) {
470492
if (yamlPreset.terms) {
471493
yamlPreset['#terms'] = 'terms: ' + yamlPreset.terms;

schemas/field.json

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,7 @@
9292
"minItems": 1,
9393
"uniqueItems": true,
9494
"items": {
95-
"type": "string",
96-
"enum": ["point", "vertex", "line", "area", "relation"]
95+
"$ref": "#/$defs/Geometry"
9796
}
9897
},
9998
"default": {
@@ -294,5 +293,11 @@
294293
{ "required": ["keys"] }
295294
]}
296295
]}
297-
]
296+
],
297+
"$defs": {
298+
"Geometry": {
299+
"type": "string",
300+
"enum": ["point", "vertex", "line", "area", "relation"]
301+
}
302+
}
298303
}

schemas/preset.json

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,86 @@
126126
}
127127
},
128128
"additionalProperties": false
129+
},
130+
"relation": {
131+
"$ref": "#/$defs/RelationSchema"
132+
},
133+
"relationCrossReference": {
134+
"description": "A preset can reference the relation schema from another preset",
135+
"type": "string",
136+
"pattern": "^\\{.+\\}$"
129137
}
130138
},
131139
"additionalProperties": false,
132-
"required": ["name", "geometry", "tags"]
140+
"required": ["name", "geometry", "tags"],
141+
"$defs": {
142+
"RelationSchema": {
143+
"type": "object",
144+
"properties": {
145+
"optionalTags": {
146+
"type": "object",
147+
"description": "Only useful for specifying placeholders which are referenced in members.*.matchTags",
148+
"examples": [{ "route": "$1" }],
149+
"additionalProperties": {
150+
"type": "string"
151+
}
152+
},
153+
"id": {
154+
"type": "string",
155+
"description": "The “permanent relation type ID”, this should match the value of https://osm.wiki/Property:P41 in the OSM wiki’s wikibase system."
156+
},
157+
"allowDuplicateMembers": {
158+
"type": "boolean",
159+
"default": true
160+
},
161+
"members": {
162+
"type": "array",
163+
"items": {
164+
"type": "object",
165+
"properties": {
166+
"roles": {
167+
"type": "object",
168+
"additionalProperties": {
169+
"type": "string"
170+
},
171+
"description": "Map of roles to their label in the default language. An empty string is allowed as key."
172+
},
173+
"geometry": {
174+
"type": "array",
175+
"items": {
176+
"$ref": "field.json#/$defs/Geometry"
177+
},
178+
"description": "If not specified, any geometry is allowed"
179+
},
180+
"matchTags": {
181+
"type": "array",
182+
"items": {
183+
"type": "object",
184+
"additionalProperties": {
185+
"type": "string"
186+
}
187+
},
188+
"examples": [
189+
[{ "a": 1, "b": 2 }],
190+
[{ "a": 1 }, { "b": 2 }]
191+
],
192+
"description": "`*` can be used as a tag value. If multiple array items are specified, only 1 needs to match."
193+
},
194+
"min": {
195+
"type": "integer",
196+
"description": "If unspecified, there is no minimum"
197+
},
198+
"max": {
199+
"type": "integer",
200+
"description": "If unspecified, there is no maximum"
201+
}
202+
},
203+
"additionalProperties": false
204+
}
205+
}
206+
},
207+
"required": ["id", "allowDuplicateMembers", "members"],
208+
"additionalProperties": false
209+
}
210+
}
133211
}

0 commit comments

Comments
 (0)