Skip to content

Commit 478a4e7

Browse files
committed
Fix unsolvable validator error for presets with locationSet
re: #1626 re: openstreetmap/iD#10459
1 parent a0ed2ca commit 478a4e7

File tree

5 files changed

+51
-48
lines changed

5 files changed

+51
-48
lines changed

modules/core/PresetSystem.js

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,14 @@ export class PresetSystem extends AbstractSystem {
7373
}
7474
}
7575

76-
const assets = this.context.systems.assets;
77-
const urlhash = this.context.systems.urlhash;
76+
const context = this.context;
77+
const assets = context.systems.assets;
78+
const locations = context.systems.locations;
79+
const urlhash = context.systems.urlhash;
80+
7881
const prerequisites = Promise.all([
7982
assets.initAsync(),
83+
locations.initAsync(),
8084
urlhash.initAsync()
8185
]);
8286

@@ -143,7 +147,7 @@ export class PresetSystem extends AbstractSystem {
143147
merge(src) {
144148
let newLocationSets = [];
145149
const context = this.context;
146-
const locationSystem = context.systems.locations;
150+
const locations = context.systems.locations;
147151

148152
// Merge Fields
149153
if (src.fields) {
@@ -246,12 +250,12 @@ if (c.icon) c.icon = c.icon.replace(/^iD-/, 'rapid-');
246250

247251
// Merge Custom Features
248252
if (src.featureCollection && Array.isArray(src.featureCollection.features)) {
249-
locationSystem.mergeCustomGeoJSON(src.featureCollection);
253+
locations.mergeCustomGeoJSON(src.featureCollection);
250254
}
251255

252256
// Resolve all locationSet features.
253257
if (newLocationSets.length) {
254-
locationSystem.mergeLocationSets(newLocationSets);
258+
locations.mergeLocationSets(newLocationSets);
255259
}
256260

257261
return this;
@@ -294,29 +298,36 @@ if (c.icon) c.icon = c.icon.replace(/^iD-/, 'rapid-');
294298
*/
295299
matchTags(tags, geometry, loc) {
296300
const keyIndex = this._geometryIndex[geometry];
301+
const context = this.context;
302+
const locations = context.systems.locations;
303+
304+
// If we care about location, gather the locationSets allowed at this location
305+
const validHere = Array.isArray(loc) ? locations.locationSetsAt(loc) : null;
306+
297307
let bestScore = -1;
298-
let bestMatch;
308+
let bestMatch = null;
299309
let matchCandidates = [];
300310

301311
for (let k in tags) {
302-
let indexMatches = [];
303-
304-
let valueIndex = keyIndex[k];
312+
const valueIndex = keyIndex[k];
305313
if (!valueIndex) continue;
306314

307-
let keyValueMatches = valueIndex[tags[k]];
315+
const indexMatches = [];
316+
const keyValueMatches = valueIndex[tags[k]];
308317
if (keyValueMatches) indexMatches.push(...keyValueMatches);
309-
let keyStarMatches = valueIndex['*'];
318+
const keyStarMatches = valueIndex['*'];
310319
if (keyStarMatches) indexMatches.push(...keyStarMatches);
311320

312321
if (indexMatches.length === 0) continue;
313322

314-
for (let i = 0; i < indexMatches.length; i++) {
315-
const candidate = indexMatches[i];
323+
for (const candidate of indexMatches) {
316324
const score = candidate.matchScore(tags);
317325
if (score === -1) continue;
318326

319-
matchCandidates.push({score, candidate});
327+
// Exclude candidate if it is scoped to a location not valid here
328+
if (validHere && candidate.locationSetID && !validHere[candidate.locationSetID]) continue;
329+
330+
matchCandidates.push({ score, candidate });
320331

321332
if (score > bestScore) {
322333
bestScore = score;
@@ -325,22 +336,6 @@ if (c.icon) c.icon = c.icon.replace(/^iD-/, 'rapid-');
325336
}
326337
}
327338

328-
const locationSystem = this.context.systems.locations;
329-
if (bestMatch && bestMatch.locationSetID && bestMatch.locationSetID !== '+[Q2]' && Array.isArray(loc)) {
330-
const validHere = locationSystem.locationSetsAt(loc);
331-
if (!validHere[bestMatch.locationSetID]) {
332-
matchCandidates.sort((a, b) => (a.score < b.score) ? 1 : -1);
333-
for (let i = 0; i < matchCandidates.length; i++){
334-
const candidateScore = matchCandidates[i];
335-
if (!candidateScore.candidate.locationSetID || validHere[candidateScore.candidate.locationSetID]) {
336-
bestMatch = candidateScore.candidate;
337-
bestScore = candidateScore.score;
338-
break;
339-
}
340-
}
341-
}
342-
}
343-
344339
// If any part of an address is present, allow fallback to "Address" preset - iD#4353
345340
if (!bestMatch || bestMatch.isFallback()) {
346341
for (let k in tags) {
@@ -526,8 +521,8 @@ if (c.icon) c.icon = c.icon.replace(/^iD-/, 'rapid-');
526521
// If a location was provided, filter results to only those valid here.
527522
let arr = [...results.values()];
528523
if (Array.isArray(loc)) {
529-
const locationSystem = this.context.systems.locations;
530-
const validHere = locationSystem.locationSetsAt(loc);
524+
const locations = this.context.systems.locations;
525+
const validHere = locations.locationSetsAt(loc);
531526
arr = arr.filter(item => !item.locationSetID || validHere[item.locationSetID]);
532527
}
533528

modules/validations/mismatched_geometry.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ export function validationMismatchedGeometry(context) {
236236
if (entity.type === 'node' && entity.isOnAddressLine(graph)) return null;
237237

238238
var sourceGeom = entity.geometry(graph);
239+
var loc = entity.extent(graph).center();
239240

240241
var targetGeoms = entity.type === 'way' ? ['point', 'vertex'] : ['line', 'area'];
241242

@@ -244,7 +245,7 @@ export function validationMismatchedGeometry(context) {
244245
var asSource = presets.match(entity, graph);
245246

246247
var targetGeom = targetGeoms.find(nodeGeom => {
247-
var asTarget = presets.matchTags(entity.tags, nodeGeom);
248+
var asTarget = presets.matchTags(entity.tags, nodeGeom, loc);
248249
// sometimes there are two presets with the same tags for different geometries
249250
if (!asSource || !asTarget || asSource === asTarget || deepEqual(asSource.tags, asTarget.tags)) return false;
250251

test/browser/validations/ambiguous_crossing_tags.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ describe('validationAmbiguousCrossingTags', () => {
1616
constructor() {
1717
this.services = {};
1818
this.systems = {
19-
editor: new MockEditSystem(),
20-
l10n: new MockLocalizationSystem(),
21-
presets: new Rapid.PresetSystem(this)
19+
editor: new MockEditSystem(this),
20+
l10n: new MockLocalizationSystem(this),
21+
locations: new Rapid.LocationSystem(this),
22+
presets: new Rapid.PresetSystem(this)
2223
};
2324
}
2425
}

test/browser/validations/mismatched_geometry.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@ describe('validationMismatchedGeometry', () => {
88
t(id) { return id; }
99
}
1010

11-
class MockLocationSystem {
12-
constructor() {}
13-
}
14-
1511
class MockStorageSystem {
1612
constructor() { }
1713
getItem() { return ''; }
@@ -30,7 +26,7 @@ describe('validationMismatchedGeometry', () => {
3026
this.systems = {
3127
assets: new Rapid.AssetSystem(this),
3228
l10n: new MockLocalizationSystem(),
33-
locations: new MockLocationSystem(),
29+
locations: new Rapid.LocationSystem(this),
3430
presets: new Rapid.PresetSystem(this),
3531
storage: new MockStorageSystem(),
3632
urlhash: new MockUrlSystem()
@@ -45,11 +41,15 @@ describe('validationMismatchedGeometry', () => {
4541
graph = new Rapid.Graph(); // reset
4642
_savedAreaKeys = Rapid.osmAreaKeys;
4743

48-
// For the tests that need a building preset
4944
const testPresets = {
5045
building: {
5146
tags: { building: '*' },
5247
geometry: ['area']
48+
},
49+
desert_library: {
50+
tags: { amenity: 'library' },
51+
geometry: ['point'],
52+
locationSet: { include: ['Q620634'] }
5353
}
5454
};
5555
context.systems.assets._cache.tagging_preset_presets = testPresets;
@@ -171,6 +171,16 @@ describe('validationMismatchedGeometry', () => {
171171
});
172172
});
173173

174+
it(`does not flag open way if the preset location doesn't match the entity location` , () => {
175+
// In this test case, there is an area preset for `amenity=library` but we won't match it because of the location
176+
const presets = context.systems.presets;
177+
return presets.initAsync().then(() => {
178+
createOpenWay({ amenity: 'library' });
179+
const issues = validate();
180+
expect(issues).to.have.lengthOf(0);
181+
});
182+
});
183+
174184
it('flags open way with both area and line tags', () => {
175185
createOpenWay({ area: 'yes', barrier: 'fence' });
176186
const issues = validate();

test/browser/validations/outdated_tags.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,13 @@ describe('validationOutdatedTags', () => {
66
t(id) { return id; }
77
}
88

9-
class MockLocationSystem {
10-
constructor() {}
11-
}
12-
139
class MockContext {
1410
constructor() {
1511
this.services = {};
1612
this.systems = {
1713
assets: new Rapid.AssetSystem(this),
18-
l10n: new MockLocalizationSystem(),
19-
locations: new MockLocationSystem(),
14+
l10n: new MockLocalizationSystem(this),
15+
locations: new Rapid.LocationSystem(this),
2016
presets: new Rapid.PresetSystem(this)
2117
};
2218
}

0 commit comments

Comments
 (0)