presetIndex.build -> presetIndex.merge, and make it merge data

This commit is contained in:
Bryan Housel
2020-02-05 21:13:15 -05:00
parent 426565d6de
commit 3c6ba9703a
7 changed files with 103 additions and 127 deletions
+2 -3
View File
@@ -43,9 +43,8 @@ of iD (e.g. `http://preview.ideditor.com/release/`), the following parameters ar
* __`photo_overlay`__ - The street-level photo overlay layers to enable.<br/>
_Example:_ `photo_overlay=streetside,mapillary,openstreetcam`<br/>
_Available values:_ `streetside` (Microsoft Bing), `mapillary`, `mapillary-signs`, `mapillary-map-features`, `openstreetcam`
* __`presets`__ - A path to an external presets file or a comma-separated list of preset IDs. These will be the only presets the user may select.<br/>
_Example:_ `presets=https://path/to/presets.json`
_Example 2:_ `presets=building,highway/residential,highway/unclassified`
* __`presets`__ - A comma-separated list of preset IDs. These will be the only presets the user may select.<br/>
_Example:_ `presets=building,highway/residential,highway/unclassified`
* __`rtl=true`__ - Force iD into right-to-left mode (useful for testing).
* __`source`__ - Prefills the changeset source. Pass a url encoded string.<br/>
_Example:_ `source=Bing%3BMapillary`
+7 -1
View File
@@ -2,12 +2,18 @@ import { t } from '../util/locale';
import { presetCollection } from './collection';
//
// `presetCategory` builds a `presetCollection` of member presets,
// decorated with some extra methods for searching and matching geometry
//
export function presetCategory(categoryID, category, all) {
let _this = Object.assign({}, category); // shallow copy
_this.id = categoryID;
_this.members = presetCollection(_this.members.map(presetID => all.item(presetID)));
_this.members = presetCollection(
category.members.map(presetID => all.item(presetID)).filter(Boolean)
);
_this.geometry = _this.members.collection
.reduce((acc, preset) => {
+4
View File
@@ -2,6 +2,10 @@ import { t } from '../util/locale';
import { utilSafeClassName } from '../util/util';
//
// `presetField` decorates a given `field` Object
// with some extra methods for searching and matching geometry
//
export function presetField(fieldID, field) {
let _this = Object.assign({}, field); // shallow copy
+62 -93
View File
@@ -1,5 +1,4 @@
import { dispatch as d3_dispatch } from 'd3-dispatch';
// import { json as d3_json } from 'd3-fetch';
import { osmNodeGeometriesForTags } from '../osm/tags';
import { presetCategory } from './category';
@@ -29,9 +28,10 @@ export function presetIndex(context) {
const LINE = presetPreset('line', { name: 'Line', tags: {}, geometry: ['line'], matchScore: 0.1 } );
const AREA = presetPreset('area', { name: 'Area', tags: { area: 'yes' }, geometry: ['area'], matchScore: 0.1 } );
const RELATION = presetPreset('relation', { name: 'Relation', tags: {}, geometry: ['relation'], matchScore: 0.1 } );
const FALLBACKS = [POINT, VERTEX, LINE, AREA, RELATION];
let _this = presetCollection(FALLBACKS);
let _this = presetCollection([POINT, VERTEX, LINE, AREA, RELATION]);
let _presets = { point: POINT, vertex: VERTEX, line: LINE, area: AREA, relation: RELATION };
let _defaults = {
point: presetCollection([POINT]),
vertex: presetCollection([VERTEX]),
@@ -41,9 +41,10 @@ export function presetIndex(context) {
};
let _fields = {};
let _categories = {};
let _universal = [];
let _recents;
// let _addablePresetIDs; // presets that the user can add
let _addablePresetIDs; // presets that the user can add
// Index of presets by (geometry, tag key).
let _geometryIndex = { point: {}, vertex: {}, line: {}, area: {}, relation: {} };
@@ -70,120 +71,87 @@ export function presetIndex(context) {
}
// _this.init = (addablePresetIDs) => {
_this.init = () => {
// _addablePresetIDs = addablePresetIDs;
// let addable = true;
// if (addablePresetIDs) {
// addable = (presetID) => addablePresetIDs.indexOf(presetID) !== -1;
// }
// return _this.build(d, addable);
return ensurePresetData()
.then(d => _this.build(d));
.then(_this.merge);
};
// _this.reset = () => {
// _defaults = {
// point: presetCollection([]),
// vertex: presetCollection([]),
// line: presetCollection([]),
// area: presetCollection([]),
// relation: presetCollection([])
// };
// _this.collection = [];
// _recents = null;
// _fields = {};
// _universal = [];
// _geometryIndex = { point: {}, vertex: {}, line: {}, area: {}, relation: {} };
// return _this;
// };
// _this.fromExternal = (external, done) => {
// _this.reset();
// d3_json(external)
// .then(externalPresets => {
// _this.build(data.presets, false); // load the default presets as non-addable to start
// _addablePresetIDs = externalPresets.presets && Object.keys(externalPresets.presets);
// _this.build(externalPresets, true); // then load the external presets as addable
// })
// .catch(() => _this.init())
// .finally(() => done(_this));
// };
// _this.build = (d, addable) => {
_this.build = (d) => {
if (d.fields && Object.keys(d.fields).length) {
_this.merge = (d) => {
// Merge Fields
if (d.fields) {
Object.keys(d.fields).forEach(fieldID => {
const f = d.fields[fieldID];
_fields[fieldID] = presetField(fieldID, f);
if (f.universal) {
_universal.push(_fields[fieldID]);
if (f) { // add or replace
_fields[fieldID] = presetField(fieldID, f);
} else { // remove
delete _fields[fieldID];
}
});
}
if (d.presets && Object.keys(d.presets).length) {
const rawPresets = d.presets;
// Merge Presets
if (d.presets) {
Object.keys(d.presets).forEach(presetID => {
const p = d.presets[presetID];
const existing = _this.index(presetID);
// const isAddable = typeof addable === 'function' ? addable(presetID, p) : addable;
const isAddable = true;
if (existing !== -1) {
_this.collection[existing] = presetPreset(presetID, p, _fields, isAddable, rawPresets);
} else {
_this.collection.push(presetPreset(presetID, p, _fields, isAddable, rawPresets));
if (p) { // add or replace
// _presets[presetID] = presetPreset(presetID, p, _fields, isAddable, _presets);
_presets[presetID] = presetPreset(presetID, p, _fields, true);
} else { // remove (but not if it's a fallback)
const existing = _presets[presetID];
if (existing && !existing.isFallback()) {
delete _presets[presetID];
}
}
});
}
if (d.categories && Object.keys(d.categories).length) {
// Merge Categories
if (d.categories) {
Object.keys(d.categories).forEach(categoryID => {
const c = d.categories[categoryID];
const existing = _this.index(categoryID);
if (existing !== -1) {
_this.collection[existing] = presetCategory(categoryID, c, _this);
} else {
_this.collection.push(presetCategory(categoryID, c, _this));
if (c) { // add or replace
_categories[categoryID] = presetCategory(categoryID, c, _this);
} else { // remove
delete _categories[categoryID];
}
});
}
const getItem = (_this.item).bind(_this);
// if (_addablePresetIDs) {
// ['area', 'line', 'point', 'vertex', 'relation'].forEach(geometry => {
// _defaults[geometry] = presetCollection(
// _addablePresetIDs.map(getItem).filter(preset => preset.geometry.indexOf(geometry) !== -1)
// );
// });
// } else if (d.defaults) {
if (d.defaults && Object.keys(d.defaults).length) {
_defaults = {
point: presetCollection(d.defaults.point.map(getItem)),
vertex: presetCollection(d.defaults.vertex.map(getItem)),
line: presetCollection(d.defaults.line.map(getItem)),
area: presetCollection(d.defaults.area.map(getItem)),
relation: presetCollection(d.defaults.relation.map(getItem))
};
}
for (let i = 0; i < _this.collection.length; i++) {
const preset = _this.collection[i];
const geometry = preset.geometry;
for (let j = 0; j < geometry.length; j++) {
let g = _geometryIndex[geometry[j]];
for (let k in preset.tags) {
(g[k] = g[k] || []).push(preset);
// Merge Defaults
if (d.defaults) {
Object.keys(d.defaults).forEach(geometry => {
const def = d.defaults[geometry];
if (Array.isArray(def)) { // add or replace
_defaults[geometry] = presetCollection(
def.map(presetID => _presets[presetID]).filter(Boolean)
);
} else { // remove
delete _defaults[geometry];
}
}
});
}
// Rebuild universal fields
_universal = Object.values(_fields).reduce((acc, field) => {
if (field.universal) acc.push(field);
return acc;
}, []);
// Rebuild _this.collection
_this.collection = Object.values(_presets).concat(Object.values(_categories));
// Rebuild geometry index
_geometryIndex = { point: {}, vertex: {}, line: {}, area: {}, relation: {} };
_this.collection.forEach(preset => {
(preset.geometry || []).forEach(geometry => {
let g = _geometryIndex[geometry];
for (let key in preset.tags) {
(g[key] = g[key] || []).push(preset);
}
});
});
return _this;
};
@@ -482,5 +450,6 @@ export function presetIndex(context) {
setRecents(items);
};
return utilRebind(_this, dispatch, 'on');
}
+4
View File
@@ -4,6 +4,10 @@ import { utilArrayUniq, utilObjectOmit } from '../util';
import { utilSafeClassName } from '../util/util';
//
// `presetPreset` decorates a given `preset` Object
// with some extra methods for searching and matching geometry
//
export function presetPreset(presetID, preset, fields, addable, rawPresets) {
let _this = Object.assign({}, preset); // shallow copy
+13 -19
View File
@@ -1,31 +1,25 @@
describe('iD.presetCategory', function() {
var category, residential;
var category = {
'geometry': 'line',
'icon': 'highway',
'name': 'roads',
'members': [ 'highway/residential' ]
};
var residential = iD.presetPreset('highway/residential',
{ tags: { highway: 'residential' }, geometry: ['line'] }
);
var all = iD.presetCollection([residential]);
beforeEach(function() {
category = {
'geometry': 'line',
'icon': 'highway',
'name': 'roads',
'members': [
'highway/residential'
]
};
residential = iD.presetPreset('highway/residential', {
tags: {
highway: 'residential'
},
geometry: ['line']
});
});
it('maps members names to preset instances', function() {
var c = iD.presetCategory('road', category, iD.presetCollection([residential]));
var c = iD.presetCategory('road', category, all);
expect(c.members.collection[0]).to.eql(residential);
});
describe('#matchGeometry', function() {
it('matches the type of an entity', function() {
var c = iD.presetCategory('road', category, iD.presetCollection([residential]));
var c = iD.presetCategory('road', category, all);
expect(c.matchGeometry('line')).to.eql(true);
expect(c.matchGeometry('point')).to.eql(false);
});
+11 -11
View File
@@ -15,56 +15,56 @@ describe('iD.presetCollection', function() {
tags: {},
geometry: ['area']
}),
grill: iD.presetPreset('__test/amenity/bbq', {
grill: iD.presetPreset('amenity/bbq', {
name: 'Grill',
tags: { amenity: 'bbq' },
geometry: ['point'],
terms: []
}),
sandpit: iD.presetPreset('__test/amenity/grit_bin', {
sandpit: iD.presetPreset('amenity/grit_bin', {
name: 'Sandpit',
tags: { amenity: 'grit_bin' },
geometry: ['point'],
terms: []
}),
residential: iD.presetPreset('__test/highway/residential', {
residential: iD.presetPreset('highway/residential', {
name: 'Residential Area',
tags: { highway: 'residential' },
geometry: ['point', 'area'],
terms: []
}),
grass1: iD.presetPreset('__test/landuse/grass1', {
grass1: iD.presetPreset('landuse/grass1', {
name: 'Grass',
tags: { landuse: 'grass' },
geometry: ['point', 'area'],
terms: []
}),
grass2: iD.presetPreset('__test/landuse/grass2', {
grass2: iD.presetPreset('landuse/grass2', {
name: 'Ğṝȁß',
tags: { landuse: 'ğṝȁß' },
geometry: ['point', 'area'],
terms: []
}),
park: iD.presetPreset('__test/leisure/park', {
park: iD.presetPreset('leisure/park', {
name: 'Park',
tags: { leisure: 'park' },
geometry: ['point', 'area'],
terms: [ 'grass' ],
matchScore: 0.5
}),
parking: iD.presetPreset('__test/amenity/parking', {
parking: iD.presetPreset('amenity/parking', {
name: 'Parking',
tags: { amenity: 'parking' },
geometry: ['point', 'area'],
terms: [ 'cars' ]
}),
soccer: iD.presetPreset('__test/leisure/pitch/soccer', {
soccer: iD.presetPreset('leisure/pitch/soccer', {
name: 'Soccer Field',
tags: { leisure: 'pitch', sport: 'soccer' },
geometry: ['point', 'area'],
terms: ['fußball']
}),
football: iD.presetPreset('__test/leisure/pitch/american_football', {
football: iD.presetPreset('leisure/pitch/american_football', {
name: 'Football Field',
tags: { leisure: 'pitch', sport: 'american_football' },
geometry: ['point', 'area'],
@@ -80,7 +80,7 @@ describe('iD.presetCollection', function() {
describe('#item', function() {
it('fetches a preset by id', function() {
expect(c.item('__test/highway/residential')).to.equal(p.residential);
expect(c.item('highway/residential')).to.equal(p.residential);
});
});
@@ -148,7 +148,7 @@ describe('iD.presetCollection', function() {
});
it('excludes presets with searchable: false', function() {
var excluded = iD.presetPreset('__test/excluded', {
var excluded = iD.presetPreset('excluded', {
name: 'excluded',
tags: { amenity: 'excluded' },
geometry: ['point'],