Skip to content

Commit 9d16588

Browse files
authored
move deprecatedTags logic out of osmEntity and into a helper function (openstreetmap#10842)
1 parent 16f1187 commit 9d16588

File tree

6 files changed

+183
-132
lines changed

6 files changed

+183
-132
lines changed

modules/osm/deprecated.js

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/** @typedef {{ old: Tags; replace?: Tags }[]} DataDeprecated */
2+
3+
/** @param {Tags} tags @param {DataDeprecated} dataDeprecated */
4+
export function getDeprecatedTags(tags, dataDeprecated) {
5+
// if there are no tags, none can be deprecated
6+
if (Object.keys(tags).length === 0) return [];
7+
8+
/** @type {DataDeprecated} */
9+
var deprecated = [];
10+
dataDeprecated.forEach((d) => {
11+
var oldKeys = Object.keys(d.old);
12+
if (d.replace) {
13+
var hasExistingValues = Object.keys(d.replace).some((replaceKey) => {
14+
if (!tags[replaceKey] || d.old[replaceKey]) return false;
15+
var replaceValue = d.replace[replaceKey];
16+
if (replaceValue === '*') return false;
17+
if (replaceValue === tags[replaceKey]) return false;
18+
return true;
19+
});
20+
// don't flag deprecated tags if the upgrade path would overwrite existing data - #7843
21+
if (hasExistingValues) return;
22+
}
23+
24+
var matchesDeprecatedTags = oldKeys.every((oldKey) => {
25+
if (!tags[oldKey]) return false;
26+
if (d.old[oldKey] === '*') return true;
27+
if (d.old[oldKey] === tags[oldKey]) return true;
28+
29+
var vals = tags[oldKey].split(';').filter(Boolean);
30+
if (vals.length === 0) {
31+
return false;
32+
} else if (vals.length > 1) {
33+
return vals.indexOf(d.old[oldKey]) !== -1;
34+
} else {
35+
if (tags[oldKey] === d.old[oldKey]) {
36+
if (d.replace && d.old[oldKey] === d.replace[oldKey]) {
37+
var replaceKeys = Object.keys(d.replace);
38+
return !replaceKeys.every((replaceKey) => {
39+
return tags[replaceKey] === d.replace[replaceKey];
40+
});
41+
} else {
42+
return true;
43+
}
44+
}
45+
}
46+
47+
return false;
48+
});
49+
50+
if (matchesDeprecatedTags) {
51+
deprecated.push(d);
52+
}
53+
});
54+
55+
return deprecated;
56+
}
57+
58+
/** @type {{ [key: string]: string[] }} */
59+
var _deprecatedTagValuesByKey;
60+
61+
/** @param {DataDeprecated} dataDeprecated */
62+
export function deprecatedTagValuesByKey(dataDeprecated) {
63+
if (!_deprecatedTagValuesByKey) {
64+
_deprecatedTagValuesByKey = {};
65+
dataDeprecated.forEach((d) => {
66+
var oldKeys = Object.keys(d.old);
67+
if (oldKeys.length === 1) {
68+
var oldKey = oldKeys[0];
69+
var oldValue = d.old[oldKey];
70+
if (oldValue !== '*') {
71+
if (!_deprecatedTagValuesByKey[oldKey]) {
72+
_deprecatedTagValuesByKey[oldKey] = [oldValue];
73+
} else {
74+
_deprecatedTagValuesByKey[oldKey].push(oldValue);
75+
}
76+
}
77+
}
78+
});
79+
}
80+
return _deprecatedTagValuesByKey;
81+
};

modules/osm/entity.js

-75
Original file line numberDiff line numberDiff line change
@@ -54,29 +54,6 @@ osmEntity.key = function(entity) {
5454
return entity.id + 'v' + (entity.v || 0);
5555
};
5656

57-
var _deprecatedTagValuesByKey;
58-
59-
osmEntity.deprecatedTagValuesByKey = function(dataDeprecated) {
60-
if (!_deprecatedTagValuesByKey) {
61-
_deprecatedTagValuesByKey = {};
62-
dataDeprecated.forEach(function(d) {
63-
var oldKeys = Object.keys(d.old);
64-
if (oldKeys.length === 1) {
65-
var oldKey = oldKeys[0];
66-
var oldValue = d.old[oldKey];
67-
if (oldValue !== '*') {
68-
if (!_deprecatedTagValuesByKey[oldKey]) {
69-
_deprecatedTagValuesByKey[oldKey] = [oldValue];
70-
} else {
71-
_deprecatedTagValuesByKey[oldKey].push(oldValue);
72-
}
73-
}
74-
}
75-
});
76-
}
77-
return _deprecatedTagValuesByKey;
78-
};
79-
8057

8158
osmEntity.prototype = {
8259

@@ -185,56 +162,4 @@ osmEntity.prototype = {
185162
isDegenerate: function() {
186163
return true;
187164
},
188-
189-
deprecatedTags: function(dataDeprecated) {
190-
var tags = this.tags;
191-
192-
// if there are no tags, none can be deprecated
193-
if (Object.keys(tags).length === 0) return [];
194-
195-
var deprecated = [];
196-
dataDeprecated.forEach(function(d) {
197-
var oldKeys = Object.keys(d.old);
198-
if (d.replace) {
199-
var hasExistingValues = Object.keys(d.replace).some(function(replaceKey) {
200-
if (!tags[replaceKey] || d.old[replaceKey]) return false;
201-
var replaceValue = d.replace[replaceKey];
202-
if (replaceValue === '*') return false;
203-
if (replaceValue === tags[replaceKey]) return false;
204-
return true;
205-
});
206-
// don't flag deprecated tags if the upgrade path would overwrite existing data - #7843
207-
if (hasExistingValues) return;
208-
}
209-
var matchesDeprecatedTags = oldKeys.every(function(oldKey) {
210-
if (!tags[oldKey]) return false;
211-
if (d.old[oldKey] === '*') return true;
212-
if (d.old[oldKey] === tags[oldKey]) return true;
213-
214-
var vals = tags[oldKey].split(';').filter(Boolean);
215-
if (vals.length === 0) {
216-
return false;
217-
} else if (vals.length > 1) {
218-
return vals.indexOf(d.old[oldKey]) !== -1;
219-
} else {
220-
if (tags[oldKey] === d.old[oldKey]) {
221-
if (d.replace && d.old[oldKey] === d.replace[oldKey]) {
222-
var replaceKeys = Object.keys(d.replace);
223-
return !replaceKeys.every(function(replaceKey) {
224-
return tags[replaceKey] === d.replace[replaceKey];
225-
});
226-
} else {
227-
return true;
228-
}
229-
}
230-
}
231-
return false;
232-
});
233-
if (matchesDeprecatedTags) {
234-
deprecated.push(d);
235-
}
236-
});
237-
238-
return deprecated;
239-
}
240165
};

modules/ui/fields/combo.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { drag as d3_drag } from 'd3-drag';
44
import * as countryCoder from '@rapideditor/country-coder';
55

66
import { fileFetcher } from '../../core/file_fetcher';
7-
import { osmEntity } from '../../osm/entity';
87
import { t } from '../../core/localizer';
98
import { services } from '../../services';
109
import { uiCombobox } from '../combobox';
@@ -13,6 +12,7 @@ import { svgIcon } from '../../svg/icon';
1312
import { utilKeybinding } from '../../util/keybinding';
1413
import { utilArrayUniq, utilGetSetValue, utilNoAuto, utilRebind, utilTotalExtent, utilUnicodeCharsCount } from '../../util';
1514
import { uiLengthIndicator } from '../length_indicator';
15+
import { deprecatedTagValuesByKey } from '../../osm/deprecated';
1616

1717
export {
1818
uiFieldCombo as uiFieldManyCombo,
@@ -258,7 +258,7 @@ export function uiFieldCombo(field, context) {
258258
return value === restrictTagValueSpelling(value);
259259
});
260260

261-
var deprecatedValues = osmEntity.deprecatedTagValuesByKey(_dataDeprecated)[field.key];
261+
var deprecatedValues = deprecatedTagValuesByKey(_dataDeprecated)[field.key];
262262
if (deprecatedValues) {
263263
// don't suggest deprecated tag values
264264
data = data.filter(d =>

modules/validations/outdated_tags.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { services } from '../services';
99
import { utilHashcode, utilTagDiff } from '../util';
1010
import { utilDisplayLabel } from '../util/utilDisplayLabel';
1111
import { validationIssue, validationIssueFix } from '../core/validation';
12+
import { getDeprecatedTags } from '../osm/deprecated';
1213

1314
/** @import { TagDiff } from '../util/util'. */
1415

@@ -43,7 +44,7 @@ export function validationOutdatedTags() {
4344

4445
// Upgrade deprecated tags..
4546
if (_dataDeprecated) {
46-
const deprecatedTags = entity.deprecatedTags(_dataDeprecated);
47+
const deprecatedTags = getDeprecatedTags(entity.tags, _dataDeprecated);
4748
if (entity.type === 'way' && entity.isClosed() &&
4849
entity.tags.traffic_calming === 'island' && !entity.tags.highway) {
4950
// https://github.com/openstreetmap/id-tagging-schema/issues/1162#issuecomment-2000356902

test/spec/osm/deprecated.ts

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import {
2+
deprecatedTagValuesByKey,
3+
getDeprecatedTags,
4+
type DataDeprecated,
5+
} from '../../../modules/osm/deprecated';
6+
7+
var deprecated: DataDeprecated = [
8+
{ old: { highway: 'no' } },
9+
{ old: { amenity: 'toilet' }, replace: { amenity: 'toilets' } },
10+
{ old: { speedlimit: '*' }, replace: { maxspeed: '$1' } },
11+
{
12+
old: { man_made: 'water_tank' },
13+
replace: { man_made: 'storage_tank', content: 'water' },
14+
},
15+
{
16+
old: { amenity: 'gambling', gambling: 'casino' },
17+
replace: { amenity: 'casino' },
18+
},
19+
];
20+
21+
describe('getDeprecatedTags', () => {
22+
it('returns none if entity has no tags', () => {
23+
expect(getDeprecatedTags({}, deprecated)).toStrictEqual([]);
24+
});
25+
26+
it('returns none when no tags are deprecated', () => {
27+
expect(getDeprecatedTags({ amenity: 'toilets' }, deprecated)).toStrictEqual(
28+
[],
29+
);
30+
});
31+
32+
it('returns 1:0 replacement', () => {
33+
expect(getDeprecatedTags({ highway: 'no' }, deprecated)).toStrictEqual([
34+
{ old: { highway: 'no' } },
35+
]);
36+
});
37+
38+
it('returns 1:1 replacement', () => {
39+
expect(getDeprecatedTags({ amenity: 'toilet' }, deprecated)).toStrictEqual([
40+
{ old: { amenity: 'toilet' }, replace: { amenity: 'toilets' } },
41+
]);
42+
});
43+
44+
it('returns 1:1 wildcard', () => {
45+
expect(getDeprecatedTags({ speedlimit: '50' }, deprecated)).toStrictEqual([
46+
{ old: { speedlimit: '*' }, replace: { maxspeed: '$1' } },
47+
]);
48+
});
49+
50+
it('returns 1:2 total replacement', () => {
51+
expect(
52+
getDeprecatedTags({ man_made: 'water_tank' }, deprecated),
53+
).toStrictEqual([
54+
{
55+
old: { man_made: 'water_tank' },
56+
replace: { man_made: 'storage_tank', content: 'water' },
57+
},
58+
]);
59+
});
60+
61+
it('returns 1:2 partial replacement', () => {
62+
expect(
63+
getDeprecatedTags(
64+
{ man_made: 'water_tank', content: 'water' },
65+
deprecated,
66+
),
67+
).toStrictEqual([
68+
{
69+
old: { man_made: 'water_tank' },
70+
replace: { man_made: 'storage_tank', content: 'water' },
71+
},
72+
]);
73+
});
74+
75+
it('returns 2:1 replacement', () => {
76+
expect(
77+
getDeprecatedTags(
78+
{ amenity: 'gambling', gambling: 'casino' },
79+
deprecated,
80+
),
81+
).toStrictEqual([
82+
{
83+
old: { amenity: 'gambling', gambling: 'casino' },
84+
replace: { amenity: 'casino' },
85+
},
86+
]);
87+
});
88+
});
89+
90+
describe('deprecatedTagValuesByKey', () => {
91+
it('groups simple deprecations by key', () => {
92+
expect(deprecatedTagValuesByKey(deprecated)).toStrictEqual({
93+
amenity: ['toilet'], // `gambling` not included
94+
highway: ['no'],
95+
man_made: ['water_tank'],
96+
});
97+
});
98+
});

test/spec/osm/entity.js

-54
Original file line numberDiff line numberDiff line change
@@ -226,60 +226,6 @@ describe('iD.osmEntity', function () {
226226
});
227227
});
228228

229-
describe('#deprecatedTags', function () {
230-
var deprecated = [
231-
{ old: { highway: 'no' } },
232-
{ old: { amenity: 'toilet' }, replace: { amenity: 'toilets' } },
233-
{ old: { speedlimit: '*' }, replace: { maxspeed: '$1' } },
234-
{ old: { man_made: 'water_tank' }, replace: { man_made: 'storage_tank', content: 'water' } },
235-
{ old: { amenity: 'gambling', gambling: 'casino' }, replace: { amenity: 'casino' } }
236-
];
237-
238-
it('returns none if entity has no tags', function () {
239-
expect(iD.osmEntity().deprecatedTags(deprecated)).to.eql([]);
240-
});
241-
242-
it('returns none when no tags are deprecated', function () {
243-
expect(iD.osmEntity({ tags: { amenity: 'toilets' } }).deprecatedTags(deprecated)).to.eql([]);
244-
});
245-
246-
it('returns 1:0 replacement', function () {
247-
expect(iD.osmEntity({ tags: { highway: 'no' } }).deprecatedTags(deprecated)).to.eql(
248-
[{ old: { highway: 'no' } }]
249-
);
250-
});
251-
252-
it('returns 1:1 replacement', function () {
253-
expect(iD.osmEntity({ tags: { amenity: 'toilet' } }).deprecatedTags(deprecated)).to.eql(
254-
[{ old: { amenity: 'toilet' }, replace: { amenity: 'toilets' } }]
255-
);
256-
});
257-
258-
it('returns 1:1 wildcard', function () {
259-
expect(iD.osmEntity({ tags: { speedlimit: '50' } }).deprecatedTags(deprecated)).to.eql(
260-
[{ old: { speedlimit: '*' }, replace: { maxspeed: '$1' } }]
261-
);
262-
});
263-
264-
it('returns 1:2 total replacement', function () {
265-
expect(iD.osmEntity({ tags: { man_made: 'water_tank' } }).deprecatedTags(deprecated)).to.eql(
266-
[{ old: { man_made: 'water_tank' }, replace: { man_made: 'storage_tank', content: 'water' } }]
267-
);
268-
});
269-
270-
it('returns 1:2 partial replacement', function () {
271-
expect(iD.osmEntity({ tags: { man_made: 'water_tank', content: 'water' } }).deprecatedTags(deprecated)).to.eql(
272-
[{ old: { man_made: 'water_tank' }, replace: { man_made: 'storage_tank', content: 'water' } }]
273-
);
274-
});
275-
276-
it('returns 2:1 replacement', function () {
277-
expect(iD.osmEntity({ tags: { amenity: 'gambling', gambling: 'casino' } }).deprecatedTags(deprecated)).to.eql(
278-
[{ old: { amenity: 'gambling', gambling: 'casino' }, replace: { amenity: 'casino' } }]
279-
);
280-
});
281-
});
282-
283229
describe('#hasInterestingTags', function () {
284230
it('returns false if the entity has no tags', function () {
285231
expect(iD.osmEntity().hasInterestingTags()).to.equal(false);

0 commit comments

Comments
 (0)