Files
iD/modules/presets/index.js
Quincy Morgan ae7c099c8a Moved field inheritance resolution from the data build to the preset initialization
Removed the data build check for duplicate values between fields and moreFields
Renamed the shop field from Type to Shop Type
Renamed the beauty field from Shop Type to Beauty Specialty
Added the brand field to the shop preset under moreFields
2019-01-15 09:53:26 -05:00

252 lines
8.1 KiB
JavaScript

import _bind from 'lodash-es/bind';
import _forEach from 'lodash-es/forEach';
import _reject from 'lodash-es/reject';
import _uniq from 'lodash-es/uniq';
import { json as d3_json } from 'd3-request';
import { data } from '../../data/index';
import { presetCategory } from './category';
import { presetCollection } from './collection';
import { presetField } from './field';
import { presetPreset } from './preset';
export { presetCategory };
export { presetCollection };
export { presetField };
export { presetPreset };
export function presetIndex() {
// a presetCollection with methods for
// loading new data and returning defaults
var all = presetCollection([]);
var _defaults = { area: all, line: all, point: all, vertex: all, relation: all };
var _fields = {};
var _universal = [];
var _recent = presetCollection([]);
// Index of presets by (geometry, tag key).
var _index = {
point: {},
vertex: {},
line: {},
area: {},
relation: {}
};
all.match = function(entity, resolver) {
return resolver.transient(entity, 'presetMatch', function() {
var geometry = entity.geometry(resolver);
var address;
// Treat entities on addr:interpolation lines as points, not vertices - #3241
if (geometry === 'vertex' && entity.isOnAddressLine(resolver)) {
geometry = 'point';
}
var geometryMatches = _index[geometry];
var best = -1;
var match;
for (var k in entity.tags) {
// If any part of an address is present,
// allow fallback to "Address" preset - #4353
if (k.match(/^addr:/) !== null && geometryMatches['addr:*']) {
address = geometryMatches['addr:*'][0];
}
var keyMatches = geometryMatches[k];
if (!keyMatches) continue;
for (var i = 0; i < keyMatches.length; i++) {
var score = keyMatches[i].matchScore(entity);
if (score > best) {
best = score;
match = keyMatches[i];
}
}
}
if (address && (!match || match.isFallback())) {
match = address;
}
return match || all.item(geometry);
});
};
// Because of the open nature of tagging, iD will never have a complete
// list of tags used in OSM, so we want it to have logic like "assume
// that a closed way with an amenity tag is an area, unless the amenity
// is one of these specific types". This function computes a structure
// that allows testing of such conditions, based on the presets designated
// as as supporting (or not supporting) the area geometry.
//
// The returned object L is a whitelist/blacklist of tags. A closed way
// with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
// (see `Way#isArea()`). In other words, the keys of L form the whitelist,
// and the subkeys form the blacklist.
all.areaKeys = function() {
var areaKeys = {};
var ignore = ['barrier', 'highway', 'footway', 'railway', 'type']; // probably a line..
var presets = _reject(all.collection, 'suggestion');
// whitelist
presets.forEach(function(d) {
for (var key in d.tags) break;
if (!key) return;
if (ignore.indexOf(key) !== -1) return;
if (d.geometry.indexOf('area') !== -1) { // probably an area..
areaKeys[key] = areaKeys[key] || {};
}
});
// blacklist
presets.forEach(function(d) {
for (var key in d.tags) break;
if (!key) return;
if (ignore.indexOf(key) !== -1) return;
var value = d.tags[key];
if (key in areaKeys && // probably an area...
d.geometry.indexOf('line') !== -1 && // but sometimes a line
value !== '*') {
areaKeys[key][value] = true;
}
});
return areaKeys;
};
all.build = function(d, visible) {
if (d.fields) {
_forEach(d.fields, function(d, id) {
_fields[id] = presetField(id, d);
if (d.universal) {
_universal.push(_fields[id]);
}
});
}
// move the wikidata field to directly follow the wikipedia field
_universal.splice(_universal.indexOf(_fields.wikidata), 1);
_universal.splice(_universal.indexOf(_fields.wikipedia)+1, 0, _fields.wikidata);
if (d.presets) {
var rawPresets = d.presets;
_forEach(d.presets, function(d, id) {
var existing = all.index(id);
if (existing !== -1) {
all.collection[existing] = presetPreset(id, d, _fields, visible, rawPresets);
} else {
all.collection.push(presetPreset(id, d, _fields, visible, rawPresets));
}
});
}
if (d.categories) {
_forEach(d.categories, function(d, id) {
var existing = all.index(id);
if (existing !== -1) {
all.collection[existing] = presetCategory(id, d, all);
} else {
all.collection.push(presetCategory(id, d, all));
}
});
}
if (d.defaults) {
var getItem = _bind(all.item, all);
_defaults = {
area: presetCollection(d.defaults.area.map(getItem)),
line: presetCollection(d.defaults.line.map(getItem)),
point: presetCollection(d.defaults.point.map(getItem)),
vertex: presetCollection(d.defaults.vertex.map(getItem)),
relation: presetCollection(d.defaults.relation.map(getItem))
};
}
for (var i = 0; i < all.collection.length; i++) {
var preset = all.collection[i];
var geometry = preset.geometry;
for (var j = 0; j < geometry.length; j++) {
var g = _index[geometry[j]];
for (var k in preset.tags) {
(g[k] = g[k] || []).push(preset);
}
}
}
return all;
};
all.init = function() {
all.collection = [];
_recent.collection = [];
_fields = {};
_universal = [];
_index = { point: {}, vertex: {}, line: {}, area: {}, relation: {} };
return all.build(data.presets, true);
};
all.reset = function() {
all.collection = [];
_defaults = { area: all, line: all, point: all, vertex: all, relation: all };
_fields = {};
_universal = [];
_recent = presetCollection([]);
// Index of presets by (geometry, tag key).
_index = {
point: {},
vertex: {},
line: {},
area: {},
relation: {}
};
return all;
};
all.fromExternal = function(external, done) {
all.reset();
d3_json(external, function(err, externalPresets) {
if (err) {
all.init();
} else {
all.build(data.presets, false); // make default presets hidden to begin
all.build(externalPresets, true); // make the external visible
}
done(all);
});
};
all.field = function(id) {
return _fields[id];
};
all.universal = function() {
return _universal;
};
all.defaults = function(geometry, n) {
var rec = _recent.matchGeometry(geometry).collection.slice(0, 4);
var def = _uniq(rec.concat(_defaults[geometry].collection)).slice(0, n - 1);
return presetCollection(_uniq(rec.concat(def).concat(all.item(geometry))));
};
all.choose = function(preset) {
if (preset.searchable !== false) {
_recent = presetCollection(_uniq([preset].concat(_recent.collection)));
}
return all;
};
return all;
}