Adjustments to multipolygon preset

* It shouldn't appear as a search result for simple areas.
* Changing to another area preset shouldn't remove the
  type=multipolygon tag.
* It shouldn't be preferred to the Building preset, which
  matches with score 0.5.

Fixes #1546.
This commit is contained in:
John Firebaugh
2013-05-29 15:35:38 -07:00
parent 40ad05e1e9
commit 4bdd49b5fc
5 changed files with 105 additions and 81 deletions
+4 -1
View File
@@ -4509,8 +4509,11 @@
"tags": {
"type": "multipolygon"
},
"removeTags": {},
"name": "Multipolygon",
"icon": "multipolygon"
"icon": "multipolygon",
"searchable": false,
"matchScore": 0.1
},
"type/restriction": {
"geometry": [
+4 -1
View File
@@ -6,6 +6,9 @@
"tags": {
"type": "multipolygon"
},
"removeTags": {},
"name": "Multipolygon",
"icon": "multipolygon"
"icon": "multipolygon",
"searchable": false,
"matchScore": 0.1
}
+19
View File
@@ -27,6 +27,20 @@
},
"required": true
},
"addTags": {
"description": "Tags that are added when changing to the preset (default is the same value as 'tags')",
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"removeTags": {
"description": "Tags that are removed when changing to another preset (default is the same value as 'tags')",
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"fields": {
"description": "Form fields that are displayed for the preset",
"type": "array",
@@ -49,6 +63,11 @@
"description": "Whether or not the preset will be suggested via search",
"type": "boolean",
"default": true
},
"matchScore": {
"description": "The quality score this preset will receive when being compared with other matches (higher is better)",
"type": "number",
"default": 1.0
}
},
"additionalProperties": false
+16 -17
View File
@@ -12,24 +12,21 @@ iD.presets.Preset = function(id, preset, fields) {
return preset.geometry.indexOf(geometry) >= 0;
};
var matchScore = preset.matchScore || 1;
preset.matchScore = function(entity) {
var tags = preset.tags,
score = 0;
for (var t in tags) {
if (entity.tags[t] === tags[t]) {
if (t === 'area') {
// score area tag lower to prevent other/area preset
// from being chosen over something more specific
score += 0.5;
} else {
score += 1;
}
score += matchScore;
} else if (tags[t] === '*' && t in entity.tags) {
score += 0.5;
score += matchScore / 2;
} else {
return -1;
}
}
return score;
};
@@ -59,30 +56,32 @@ iD.presets.Preset = function(id, preset, fields) {
return reference;
};
var removeTags = preset.removeTags || preset.tags;
preset.removeTags = function(tags, geometry) {
tags = _.omit(tags, _.keys(preset.tags));
tags = _.omit(tags, _.keys(removeTags));
for (var i in preset.fields) {
var field = preset.fields[i];
for (var f in preset.fields) {
var field = preset.fields[f];
if (field.matchGeometry(geometry) && field['default'] === tags[field.key]) {
delete tags[field.key];
}
}
return tags;
return tags;
};
var applyTags = preset.applyTags || preset.tags;
preset.applyTags = function(tags, geometry) {
tags = _.clone(tags);
for (var k in preset.tags) {
if (preset.tags[k] !== '*') tags[k] = preset.tags[k];
for (var k in applyTags) {
if (applyTags[k] !== '*') tags[k] = applyTags[k];
}
for (var f in preset.fields) {
f = preset.fields[f];
if (f.matchGeometry(geometry) && f.key && !tags[f.key] && f['default']) {
tags[f.key] = f['default'];
var field = preset.fields[f];
if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field['default']) {
tags[field.key] = field['default'];
}
}
+62 -62
View File
@@ -1,108 +1,108 @@
describe('iD.presets.Preset', function() {
var fields, p;
beforeEach(function() {
if (!p) {
fields = {};
var i = 0;
for (i in iD.data.presets.fields) {
fields[i] = iD.presets.Field(i, iD.data.presets.fields[i]);
}
p = {};
for (i in iD.data.presets.presets) {
p[i] = iD.presets.Preset(i, iD.data.presets.presets[i], fields);
}
}
});
var w1 = iD.Way({ tags: {
highway: 'motorway'
}}),
w2 = iD.Way({ tags: {
leisure: 'pitch',
sport: 'tennis'
}}),
w3 = iD.Way({ tags: {
highway: 'residential'
}}),
w4 = iD.Way({ tags: {
building: 'yep'
}}),
w5 = iD.Way(),
g = iD.Graph().replace(w1).replace(w2);
it("has optional fields", function() {
expect(p.point.fields).to.eql([]);
var preset = iD.presets.Preset('test', {});
expect(preset.fields).to.eql([]);
});
describe('#matchGeometry', function() {
var n = iD.Node();
var g = iD.Graph().replace(n);
it("returns false if it doesn't match", function() {
expect(p['highway/residential'].matchGeometry('point')).to.equal(false);
var preset = iD.presets.Preset('test', {geometry: ['line']});
expect(preset.matchGeometry('point')).to.equal(false);
});
it("returns true if it does match", function() {
expect(p.point.matchGeometry('point')).to.equal(true);
var preset = iD.presets.Preset('test', {geometry: ['point', 'line']});
expect(preset.matchGeometry('point')).to.equal(true);
});
});
describe('#matchScore', function() {
it("returns -1 if preset does not match tags", function() {
expect(p['highway/residential'].matchScore(w1)).to.equal(-1);
var preset = iD.presets.Preset('test', {tags: {foo: 'bar'}}),
entity = iD.Way({tags: {highway: 'motorway'}});
expect(preset.matchScore(entity)).to.equal(-1);
});
it("returns 0 for fallback presets", function() {
expect(p.point.matchScore(w1)).to.equal(0);
it("returns the value of the matchScore property when matched", function() {
var preset = iD.presets.Preset('test', {tags: {highway: 'motorway'}, matchScore: 0.2}),
entity = iD.Way({tags: {highway: 'motorway'}});
expect(preset.matchScore(entity)).to.equal(0.2);
});
it("returns the number of matched tags", function() {
expect(p['highway/residential'].matchScore(w3)).to.equal(1);
expect(p['leisure/pitch/tennis'].matchScore(w2)).to.equal(2);
it("defaults to the number of matched tags", function() {
var preset = iD.presets.Preset('test', {tags: {highway: 'residential'}}),
entity = iD.Way({tags: {highway: 'residential'}});
expect(preset.matchScore(entity)).to.equal(1);
var preset = iD.presets.Preset('test', {tags: {highway: 'service', service: 'alley'}}),
entity = iD.Way({tags: {highway: 'service', service: 'alley'}});
expect(preset.matchScore(entity)).to.equal(2);
});
it("counts * as a match for any value", function() {
expect(p.building.matchScore(w4)).to.equal(0.5);
expect(p.building.matchScore(w5)).to.equal(-1);
it("counts * as a match for any value with score 0.5", function() {
var preset = iD.presets.Preset('test', {tags: {building: '*'}}),
entity = iD.Way({tags: {building: 'yep'}});
expect(preset.matchScore(entity)).to.equal(0.5);
});
});
describe("isFallback", function() {
it("returns true if preset has no tags", function() {
expect(iD.presets.Preset("area", {name: "Area", tags: {}}).isFallback()).to.equal(true);
var preset = iD.presets.Preset("area", {tags: {}});
expect(preset.isFallback()).to.equal(true);
});
it("returns false if preset has no tags", function() {
expect(p.building.isFallback()).to.equal(false);
it("returns false if preset has tags", function() {
var preset = iD.presets.Preset("area", {tags: {building: 'yes'}});
expect(preset.isFallback()).to.equal(false);
});
});
describe('#applyTags', function() {
it("adds match tags", function() {
expect(p['highway/residential'].applyTags({}, 'area')).to.eql({ highway: 'residential' });
var preset = iD.presets.Preset('test', {tags: {highway: 'residential'}});
expect(preset.applyTags({}, 'area')).to.eql({highway: 'residential'});
});
it("does not add wildcard tags", function() {
expect(p.amenity.applyTags({}, 'area')).to.eql({});
var preset = iD.presets.Preset('test', {tags: {building: '*'}});
expect(preset.applyTags({}, 'area')).to.eql({});
});
it("adds default tags", function() {
expect(p['amenity/cafe'].applyTags({}, 'area')).to.eql({ amenity: 'cafe', building: 'yes'});
expect(p['amenity/cafe'].applyTags({}, 'point')).to.eql({ amenity: 'cafe' });
it("adds default tags of fields with matching geometry", function() {
var field = iD.presets.Field('field', {key: 'building', geometry: 'area', default: 'yes'}),
preset = iD.presets.Preset('test', {fields: ['field']}, {field: field});
expect(preset.applyTags({}, 'area')).to.eql({building: 'yes'});
});
it("adds no default tags of fields with non-matching geometry", function() {
var field = iD.presets.Field('field', {key: 'building', geometry: 'area', default: 'yes'}),
preset = iD.presets.Preset('test', {fields: ['field']}, {field: field});
expect(preset.applyTags({}, 'point')).to.eql({});
});
});
describe('#removeTags', function() {
it('removes match tags', function() {
expect(p['highway/residential'].removeTags({ highway: 'residential' }, 'area')).to.eql({});
it('removes tags that match preset tags', function() {
var preset = iD.presets.Preset('test', {tags: {highway: 'residential'}});
expect(preset.removeTags({highway: 'residential'}, 'area')).to.eql({});
});
it('removes default tags', function() {
expect(p['amenity/cafe'].removeTags({ amenity: 'cafe', building: 'yes'}, 'area')).to.eql({});
expect(p['amenity/cafe'].removeTags({ amenity: 'cafe', building: 'yep'}, 'area')).to.eql({ building: 'yep'});
it('removes tags that match field default tags', function() {
var field = iD.presets.Field('field', {key: 'building', geometry: 'area', default: 'yes'}),
preset = iD.presets.Preset('test', {fields: ['field']}, {field: field});
expect(preset.removeTags({building: 'yes'}, 'area')).to.eql({});
});
it('preserves tags that do not match field default tags', function() {
var field = iD.presets.Field('field', {key: 'building', geometry: 'area', default: 'yes'}),
preset = iD.presets.Preset('test', {fields: ['field']}, {field: field});
expect(preset.removeTags({building: 'yep'}, 'area')).to.eql({ building: 'yep'});
});
it('preserves tags that are not listed in removeTags', function() {
var preset = iD.presets.Preset('test', {tags: {a: 'b'}, removeTags: {}});
expect(preset.removeTags({a: 'b'}, 'area')).to.eql({a: 'b'});
});
});
});