Merge branch 'develop' into add_mapilio_data

This commit is contained in:
sezerbozbiyik
2023-05-26 16:17:07 +03:00
21 changed files with 916 additions and 748 deletions
+16 -3
View File
@@ -37,39 +37,52 @@ _Breaking developer changes, which may affect downstream projects or sites that
# Unreleased (2.26.0-dev)
#### :tada: New Features
* Combo fields for tags with `yes/no` values now also display the `no` state and allow to toggle between the two states ([#7427])
#### :sparkles: Usability & Accessibility
* Make it easier to search for OSM objects by id ([#9520], thanks [@k-yle])
#### :scissors: Operations
#### :camera: Street-Level
#### :white_check_mark: Validation
* Offer to connect sidewalk to service road without tagging the connection as a crossing ([#9650], thanks [@1ec5])
#### :bug: Bugfixes
* Fix `multi/many/semiCombo` options for not being selectable immediately after removing them for fields with predefined options
* Fix a bug where the _Add_ input element on comboboxes with a fixed set of allowed options is still hidden after an option of a previously "fully saturated" field is removed
* Fix wrongly flagged "incorrect geometry type" warnings for features with lifecycle-prefixed tags ([#9483], thanks [@biswajit-k])
* Fix corruption of tag values of fields with referenced strings, but restricted `options`, when an unavailable option is entered manually into the field.
* Properly handle case sensitive tag values in taginfo suggestions in raw tag editor ([#9640])
* Fix dysfunctional autocomplete of wikidata fields for some languages with country-codes ([#9638])
* Prevent certain tag values from corrupting css classes when they contain whitespaces ([#9637], thanks [@k-yle])
#### :earth_asia: Localization
* Send `Accept-Language` header on Nominatim API calls ([#9501], thanks [@k-yle])
* Add Address and Phone Format for India ([#9482], thanks [@biswajit-k])
* Add Address and Phone format for India ([#9482], thanks [@biswajit-k])
* Add Address format for the Philippines ([#9482], thanks [@bryceco])
#### :hourglass: Performance
#### :mortar_board: Walkthrough / Help
#### :rocket: Presets
* Render "oneway" arrows for features with `waterway=pressurized`, `waterway=spillway`, `seamark:type=two-way_route` or `seamark:type=recommended_traffic_lane` ([#9492], thanks [@k-yle])
* Render "right-side" arrows for features with lifecycle prefixes ([#9493], thanks [@k-yle])
* Take regional variants of parent presets into account when resolving preset fields ([#9524])
* Render "right-side" arrows for `man_made=quay` features
#### :hammer: Development
* Upgrade dependencies: `fortawesome` to v6.4, `which-polygon` to v2.2.1, `glob` to v9.2, `temaki` to v5.3, `marked` to v4.3, `core-js-bundle` to v3.30
* Upgrade dependencies: `fortawesome` to v6.4, `which-polygon` to v2.2.1, `glob` to v9.2, `temaki` to v5.4, `marked` to v4.3, `core-js-bundle` to v3.30, `osm-auth` to v2.1
* Bundle `package-lock.json` file in repository for faster `clean-install` builds
* Build icons from configured presets source and also process field value `icons` in `npm run build:data`
[#7427]: https://github.com/openstreetmap/iD/issues/7427
[#9482]: https://github.com/openstreetmap/iD/pull/9482
[#9483]: https://github.com/openstreetmap/iD/pull/9483
[#9492]: https://github.com/openstreetmap/iD/pull/9492
[#9493]: https://github.com/openstreetmap/iD/pull/9493
[#9520]: https://github.com/openstreetmap/iD/pull/9520
[#9501]: https://github.com/openstreetmap/iD/pull/9501
[#9520]: https://github.com/openstreetmap/iD/pull/9520
[#9524]: https://github.com/openstreetmap/iD/issues/9524
[#9630]: https://github.com/openstreetmap/iD/pull/9630
[#9637]: https://github.com/openstreetmap/iD/pull/9637
[#9638]: https://github.com/openstreetmap/iD/pull/9638
[#9640]: https://github.com/openstreetmap/iD/issues/9640
[#9650]: https://github.com/openstreetmap/iD/pull/9650
[@biswajit-k]: https://github.com/biswajit-k
[@bryceco]: https://github.com/bryceco
# 2.25.2
+6
View File
@@ -1676,6 +1676,12 @@ input.date-selector {
max-width: 100%;
color: #7092ff;
}
.form-field-input-multicombo li.chip.negated span {
text-decoration: line-through;
}
.form-field-input-multicombo li.chip input {
width: 1em;
}
.ideditor[dir='ltr'] .form-field-input-multicombo li.chip {
padding: 2px 0px 2px 5px;
}
+8
View File
@@ -186,5 +186,13 @@
["city", "postcode"],
["district"]
]
},
{
"countryCodes": ["ph"],
"format": [
["unit", "housenumber", "street"],
["district", "city"],
["postcode", "province"]
]
}
]
+2
View File
@@ -1931,6 +1931,8 @@ en:
title: Connect this feature
connect_features:
title: Connect the features
connect_using_crossing:
title: Connect using a crossing
connect_using_ford:
title: Connect using a ford
continue_from_start:
+4 -3
View File
@@ -3,6 +3,7 @@ export function actionChangePreset(entityID, oldPreset, newPreset, skipFieldDefa
var entity = graph.entity(entityID);
var geometry = entity.geometry(graph);
var tags = entity.tags;
const loc = entity.extent(graph).center();
// preserve tags that the new preset might care about, if any
var preserveKeys;
@@ -15,14 +16,14 @@ export function actionChangePreset(entityID, oldPreset, newPreset, skipFieldDefa
// only if old preset is not a sub-preset of the new one:
// preserve tags for which the new preset has a field
// https://github.com/openstreetmap/iD/issues/9372
newPreset.fields().concat(newPreset.moreFields())
newPreset.fields(loc).concat(newPreset.moreFields(loc))
.filter(f => f.matchGeometry(geometry))
.map(f => f.key).filter(Boolean)
.forEach(key => preserveKeys.push(key));
}
}
if (oldPreset) tags = oldPreset.unsetTags(tags, geometry, preserveKeys);
if (newPreset) tags = newPreset.setTags(tags, geometry, skipFieldDefaults);
if (oldPreset) tags = oldPreset.unsetTags(tags, geometry, preserveKeys, loc);
if (newPreset) tags = newPreset.setTags(tags, geometry, skipFieldDefaults, loc);
return graph.replace(entity.update({tags: tags}));
};
+8 -6
View File
@@ -15,9 +15,11 @@ export function modeAddArea(context, mode) {
.on('startFromWay', startFromWay)
.on('startFromNode', startFromNode);
var defaultTags = { area: 'yes' };
if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'area');
function defaultTags(loc) {
var defaultTags = { area: 'yes' };
if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'area', false, loc);
return defaultTags;
}
function actionClose(wayId) {
return function (graph) {
@@ -29,7 +31,7 @@ export function modeAddArea(context, mode) {
function start(loc) {
var startGraph = context.graph();
var node = osmNode({ loc: loc });
var way = osmWay({ tags: defaultTags });
var way = osmWay({ tags: defaultTags(loc) });
context.perform(
actionAddEntity(node),
@@ -45,7 +47,7 @@ export function modeAddArea(context, mode) {
function startFromWay(loc, edge) {
var startGraph = context.graph();
var node = osmNode({ loc: loc });
var way = osmWay({ tags: defaultTags });
var way = osmWay({ tags: defaultTags(loc) });
context.perform(
actionAddEntity(node),
@@ -61,7 +63,7 @@ export function modeAddArea(context, mode) {
function startFromNode(node) {
var startGraph = context.graph();
var way = osmWay({ tags: defaultTags });
var way = osmWay({ tags: defaultTags(node.loc) });
context.perform(
actionAddEntity(way),
+8 -5
View File
@@ -15,14 +15,17 @@ export function modeAddLine(context, mode) {
.on('startFromWay', startFromWay)
.on('startFromNode', startFromNode);
var defaultTags = {};
if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'line');
function defaultTags(loc) {
var defaultTags = {};
if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'line', false, loc);
return defaultTags;
}
function start(loc) {
var startGraph = context.graph();
var node = osmNode({ loc: loc });
var way = osmWay({ tags: defaultTags });
var way = osmWay({ tags: defaultTags(loc) });
context.perform(
actionAddEntity(node),
@@ -37,7 +40,7 @@ export function modeAddLine(context, mode) {
function startFromWay(loc, edge) {
var startGraph = context.graph();
var node = osmNode({ loc: loc });
var way = osmWay({ tags: defaultTags });
var way = osmWay({ tags: defaultTags(loc) });
context.perform(
actionAddEntity(node),
@@ -52,7 +55,7 @@ export function modeAddLine(context, mode) {
function startFromNode(node) {
var startGraph = context.graph();
var way = osmWay({ tags: defaultTags });
var way = osmWay({ tags: defaultTags(node.loc) });
context.perform(
actionAddEntity(way),
+11 -7
View File
@@ -19,12 +19,15 @@ export function modeAddPoint(context, mode) {
.on('cancel', cancel)
.on('finish', cancel);
var defaultTags = {};
if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'point');
function defaultTags(loc) {
var defaultTags = {};
if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'point', false, loc);
return defaultTags;
}
function add(loc) {
var node = osmNode({ loc: loc, tags: defaultTags });
var node = osmNode({ loc: loc, tags: defaultTags(loc) });
context.perform(
actionAddEntity(node),
@@ -36,7 +39,7 @@ export function modeAddPoint(context, mode) {
function addWay(loc, edge) {
var node = osmNode({ tags: defaultTags });
var node = osmNode({ tags: defaultTags(loc) });
context.perform(
actionAddMidpoint({loc: loc, edge: edge}, node),
@@ -54,14 +57,15 @@ export function modeAddPoint(context, mode) {
function addNode(node) {
if (Object.keys(defaultTags).length === 0) {
const _defaultTags = defaultTags(node.loc);
if (Object.keys(_defaultTags).length === 0) {
enterSelectMode(node);
return;
}
var tags = Object.assign({}, node.tags); // shallow copy
for (var key in defaultTags) {
tags[key] = defaultTags[key];
for (var key in _defaultTags) {
tags[key] = _defaultTags[key];
}
context.perform(
-3
View File
@@ -163,9 +163,6 @@ export function presetIndex() {
// Rebuild universal fields array
_universal = Object.values(_fields).filter(field => field.universal);
// Reset all the preset fields - they'll need to be resolved again
Object.values(_presets).forEach(preset => preset.resetFields());
// Rebuild geometry index
_geometryIndex = { point: {}, vertex: {}, line: {}, area: {}, relation: {} };
_this.collection.forEach(preset => {
+25 -15
View File
@@ -1,7 +1,10 @@
import { isEqual } from 'lodash';
import { t } from '../core/localizer';
import { osmAreaKeys, osmAreaKeysExceptions } from '../osm/tags';
import { utilArrayUniq, utilObjectOmit } from '../util';
import { utilSafeClassName } from '../util/util';
import { locationManager } from '../core/LocationManager';
//
@@ -13,8 +16,6 @@ export function presetPreset(presetID, preset, addable, allFields, allPresets) {
allPresets = allPresets || {};
let _this = Object.assign({}, preset); // shallow copy
let _addable = addable || false;
let _resolvedFields; // cache
let _resolvedMoreFields; // cache
let _searchName; // cache
let _searchNameStripped; // cache
let _searchAliases; // cache
@@ -40,11 +41,9 @@ export function presetPreset(presetID, preset, addable, allFields, allPresets) {
_this.originalMoreFields = (_this.moreFields || []);
_this.fields = () => _resolvedFields || (_resolvedFields = resolveFields('fields'));
_this.fields = loc => resolveFields('fields', loc);
_this.moreFields = () => _resolvedMoreFields || (_resolvedMoreFields = resolveFields('moreFields'));
_this.resetFields = () => _resolvedFields = _resolvedMoreFields = null;
_this.moreFields = loc => resolveFields('moreFields', loc);
_this.tags = _this.tags || {};
@@ -219,13 +218,13 @@ export function presetPreset(presetID, preset, addable, allFields, allPresets) {
};
_this.unsetTags = (tags, geometry, ignoringKeys, skipFieldDefaults) => {
_this.unsetTags = (tags, geometry, ignoringKeys, skipFieldDefaults, loc) => {
// allow manually keeping some tags
let removeTags = ignoringKeys ? utilObjectOmit(_this.removeTags, ignoringKeys) : _this.removeTags;
tags = utilObjectOmit(tags, Object.keys(removeTags));
if (geometry && !skipFieldDefaults) {
_this.fields().forEach(field => {
_this.fields(loc).forEach(field => {
if (field.matchGeometry(geometry) && field.key &&
field.default === tags[field.key] &&
(!ignoringKeys || ignoringKeys.indexOf(field.key) === -1)) {
@@ -239,7 +238,7 @@ export function presetPreset(presetID, preset, addable, allFields, allPresets) {
};
_this.setTags = (tags, geometry, skipFieldDefaults) => {
_this.setTags = (tags, geometry, skipFieldDefaults, loc) => {
const addTags = _this.addTags;
tags = Object.assign({}, tags); // shallow copy
@@ -277,7 +276,7 @@ export function presetPreset(presetID, preset, addable, allFields, allPresets) {
}
if (geometry && !skipFieldDefaults) {
_this.fields().forEach(field => {
_this.fields(loc).forEach(field => {
if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field.default) {
tags[field.key] = field.default;
}
@@ -290,14 +289,14 @@ export function presetPreset(presetID, preset, addable, allFields, allPresets) {
// For a preset without fields, use the fields of the parent preset.
// Replace {preset} placeholders with the fields of the specified presets.
function resolveFields(which) {
function resolveFields(which, loc) {
const fieldIDs = (which === 'fields' ? _this.originalFields : _this.originalMoreFields);
let resolved = [];
fieldIDs.forEach(fieldID => {
const match = fieldID.match(referenceRegex);
if (match !== null) { // a presetID wrapped in braces {}
resolved = resolved.concat(inheritFields(match[1], which));
resolved = resolved.concat(inheritFields(allPresets[match[1]], which));
} else if (allFields[fieldID]) { // a normal fieldID
resolved.push(allFields[fieldID]);
} else {
@@ -310,7 +309,19 @@ export function presetPreset(presetID, preset, addable, allFields, allPresets) {
const endIndex = _this.id.lastIndexOf('/');
const parentID = endIndex && _this.id.substring(0, endIndex);
if (parentID) {
resolved = inheritFields(parentID, which);
let parent = allPresets[parentID];
if (loc) {
const validHere = locationManager.locationSetsAt(loc);
if (!validHere[parent.locationSetID]) {
// this is a preset for which a regional variant of the main preset exists
const candidateIDs = Object.keys(allPresets).filter(k => k.startsWith(parentID));
parent = allPresets[candidateIDs.find(candidateID => {
const candidate = allPresets[candidateID];
return validHere[candidate.locationSetID] && isEqual(candidate.tags, parent.tags);
})];
}
}
resolved = inheritFields(parent, which);
}
}
@@ -318,8 +329,7 @@ export function presetPreset(presetID, preset, addable, allFields, allPresets) {
// returns an array of fields to inherit from the given presetID, if found
function inheritFields(presetID, which) {
const parent = allPresets[presetID];
function inheritFields(parent, which) {
if (!parent) return [];
if (which === 'fields') {
+10 -3
View File
@@ -17,7 +17,7 @@ export default {
// Search for Wikidata items matching the query
itemsForSearchQuery: function(query, callback) {
itemsForSearchQuery: function _itemsForSearchQuery(query, callback, language) {
if (!query) {
if (callback) callback('No query', {});
return;
@@ -32,7 +32,7 @@ export default {
search: query,
type: 'item',
// the language to search
language: lang,
language: language || lang,
// the language for the label and description in the result
uselang: lang,
limit: 10,
@@ -42,7 +42,14 @@ export default {
d3_json(url)
.then(function(result) {
if (result && result.error) {
throw new Error(result.error);
if (result.error.code === 'badvalue' &&
result.error.info.includes(lang) &&
!language && lang.includes('-')) {
// retry without "country suffix" region subtag
_itemsForSearchQuery(query, callback, lang.split('-')[0]);
} else {
throw new Error(result.error);
}
}
if (callback) callback(null, result.search || {});
})
+6 -1
View File
@@ -159,7 +159,12 @@ export function svgTagClasses() {
classes.push('tag-wikidata');
}
return classes.join(' ').trim();
// ensure that classes for tags keys/values with special characters like spaces
// are not added to the DOM, because it can cause bizarre issues (#9448)
return classes
.filter(klass => /^[-_a-z0-9]+$/.test(klass))
.join(' ')
.trim();
};
+37 -5
View File
@@ -151,7 +151,7 @@ export function uiFieldCombo(field, context) {
function objectDifference(a, b) {
return a.filter(function(d1) {
return !b.some(function(d2) {
return !d2.isMixed && d1.value === d2.value;
return d1.value === d2.value;
});
});
}
@@ -416,6 +416,17 @@ export function uiFieldCombo(field, context) {
}
function invertMultikey(d3_event, d) {
d3_event.preventDefault();
d3_event.stopPropagation();
var t = {};
if (_isMulti) {
t[d.key] = _tags[d.key] === 'yes' ? 'no' : 'yes';
}
dispatch.call('change', this, t);
}
function combo(selection) {
_container = selection.selectAll('.form-field-input-wrap')
.data([0]);
@@ -455,6 +466,11 @@ export function uiFieldCombo(field, context) {
.attr('class', 'input-wrap')
.merge(_inputWrap);
// Hide 'Add' button if this field uses fixed set of
// options and they're all currently used
var hideAdd = (!_allowCustomValues && !_comboData.length);
_inputWrap.style('display', hideAdd ? 'none' : null);
_input = _inputWrap.selectAll('input')
.data([0]);
} else {
@@ -557,13 +573,13 @@ export function uiFieldCombo(field, context) {
if (!field.key && field.keys.indexOf(k) === -1) continue;
var v = tags[k];
if (!v || (typeof v === 'string' && v.toLowerCase() === 'no')) continue;
var suffix = field.key ? k.slice(field.key.length) : k;
_multiData.push({
key: k,
value: displayValue(suffix),
display: renderValue(suffix),
state: typeof v === 'string' ? v.toLowerCase() : '',
isMixed: Array.isArray(v)
});
}
@@ -623,7 +639,7 @@ export function uiFieldCombo(field, context) {
maxLength = Math.max(0, maxLength);
// Hide 'Add' button if this field is already at its character limit
var hideAdd = maxLength <= 0;
var hideAdd = maxLength <= 0 || (!_allowCustomValues && !_comboData.length);
_container.selectAll('.chiplist .input-wrap')
.style('display', hideAdd ? 'none' : null);
@@ -656,8 +672,24 @@ export function uiFieldCombo(field, context) {
return d.isMixed;
})
.attr('title', function(d) {
return d.isMixed ? t('inspector.unshared_value_tooltip') : null;
});
if (d.isMixed) {
return t('inspector.unshared_value_tooltip');
}
if (!['yes', 'no'].includes(d.state)) {
return d.state;
}
return null;
})
.classed('negated', d => d.state === 'no');
if (!_isSemi) {
chips.selectAll('input[type=checkbox]').remove();
chips.insert('input', 'span')
.attr('type', 'checkbox')
.property('checked', d => d.state === 'yes')
.property('indeterminate', d => d.isMixed || !['yes', 'no'].includes(d.state))
.on('click', invertMultikey);
}
if (allowDragAndDrop) {
registerDragAndDrop(chips);
+35 -7
View File
@@ -384,6 +384,17 @@ export function uiFieldText(field, context) {
}
// returns all values of a (potential) multiselection and/or multi-key field
function getVals(tags) {
if (field.keys) {
return new Set(field.keys.reduce((acc, key) => acc.concat(tags[key]), [])
.filter(Boolean));
} else {
return new Set([].concat(tags[field.key]).filter(Boolean));
}
}
function change(onInput) {
return function() {
var t = {};
@@ -391,7 +402,7 @@ export function uiFieldText(field, context) {
if (!onInput) val = context.cleanTagValue(val);
// don't override multiple values with blank string
if (!val && Array.isArray(_tags[field.key])) return;
if (!val && getVals(_tags).size > 1) return;
if (!onInput) {
if (field.type === 'number' && val) {
@@ -405,7 +416,24 @@ export function uiFieldText(field, context) {
utilGetSetValue(input, val);
}
t[field.key] = val || undefined;
dispatch.call('change', this, t, onInput);
if (field.keys) {
// for multi-key fields with: handle alternative tag keys gracefully
// https://github.com/openstreetmap/id-tagging-schema/issues/905
dispatch.call('change', this, tags => {
if (field.keys.some(key => tags[key])) {
// use exiting key(s)
field.keys.filter(key => tags[key]).forEach(key => {
tags[key] = val || undefined;
});
} else {
// fall back to default key if none of the `keys` is preset
tags[field.key] = val || undefined;
}
return tags;
}, onInput);
} else {
dispatch.call('change', this, t, onInput);
}
};
}
@@ -416,14 +444,14 @@ export function uiFieldText(field, context) {
return i;
};
i.tags = function(tags) {
_tags = tags;
var isMixed = Array.isArray(tags[field.key]);
utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : '')
.attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined)
const vals = getVals(tags);
const isMixed = vals.size > 1;
const val = vals.size === 1 ? [...vals][0] : '';
utilGetSetValue(input, val)
.attr('title', isMixed ? [...vals].join('\n') : undefined)
.attr('placeholder', isMixed ? t('inspector.multiple_values') : (field.placeholder() || t('inspector.unknown')))
.classed('mixed', isMixed);
+1 -1
View File
@@ -96,7 +96,7 @@ export function uiFieldLocalized(field, context) {
var preset = presetManager.match(entity, context.graph());
if (preset) {
var isSuggestion = preset.suggestion;
var fields = preset.fields();
var fields = preset.fields(entity.extent(context.graph()).center());
var showsBrandField = fields.some(function(d) { return d.id === 'brand'; });
var showsOperatorField = fields.some(function(d) { return d.id === 'operator'; });
var setsName = preset.addTags.name;
+8 -2
View File
@@ -4,6 +4,7 @@ import { presetManager } from '../../presets';
import { t, localizer } from '../../core/localizer';
import { utilArrayIdentical } from '../../util/array';
import { utilArrayUnion, utilRebind } from '../../util';
import { geoExtent } from '../../geo/extent';
import { uiField } from '../field';
import { uiFormFields } from '../form_fields';
import { uiSection } from '../section';
@@ -32,6 +33,11 @@ export function uiSectionPresetFields(context) {
return geoms;
}, {}));
const loc = _entityIDs.reduce(function(extent, entityID) {
var entity = context.graph().entity(entityID);
return extent.extend(entity.extent(context.graph()));
}, geoExtent()).center();
var presetsManager = presetManager;
var allFields = [];
@@ -39,8 +45,8 @@ export function uiSectionPresetFields(context) {
var sharedTotalFields;
_presets.forEach(function(preset) {
var fields = preset.fields();
var moreFields = preset.moreFields();
var fields = preset.fields(loc);
var moreFields = preset.moreFields(loc);
allFields = utilArrayUnion(allFields, fields);
allMoreFields = utilArrayUnion(allMoreFields, moreFields);
+29 -8
View File
@@ -1,3 +1,5 @@
import { isEqual } from 'lodash';
import { actionAddMidpoint } from '../actions/add_midpoint';
import { actionChangeTags } from '../actions/change_tags';
import { actionMergeNodes } from '../actions/merge_nodes';
@@ -123,9 +125,8 @@ export function validationCrossingWays(context) {
motorway: true, motorway_link: true, trunk: true, trunk_link: true,
primary: true, primary_link: true, secondary: true, secondary_link: true
};
var nonCrossingHighways = { track: true };
function tagsForConnectionNodeIfAllowed(entity1, entity2, graph) {
function tagsForConnectionNodeIfAllowed(entity1, entity2, graph, lessLikelyTags) {
var featureType1 = getFeatureType(entity1, graph);
var featureType2 = getFeatureType(entity2, graph);
@@ -141,11 +142,18 @@ export function validationCrossingWays(context) {
// one feature is a path but not both
var roadFeature = entity1IsPath ? entity2 : entity1;
if (nonCrossingHighways[roadFeature.tags.highway]) {
// don't mark path connections with certain roads as crossings
var pathFeature = entity1IsPath ? entity1 : entity2;
// don't mark path connections with tracks as crossings
if (roadFeature.tags.highway === 'track') {
return {};
}
// a sidewalk crossing a driveway is unremarkable and unlikely to be interrupted by the driveway
// a sidewalk crossing another kind of service road may be similarly unremarkable
if (!lessLikelyTags &&
roadFeature.tags.highway === 'service' &&
pathFeature.tags.highway === 'footway' && pathFeature.tags.footway === 'sidewalk') {
return {};
}
var pathFeature = entity1IsPath ? entity1 : entity2;
if (['marked', 'unmarked', 'traffic_signals', 'uncontrolled'].indexOf(pathFeature.tags.crossing) !== -1) {
// if the path is a crossing, match the crossing type
return bothLines ? { highway: 'crossing', crossing: pathFeature.tags.crossing } : {};
@@ -435,6 +443,10 @@ export function validationCrossingWays(context) {
if (connectionTags) {
fixes.push(makeConnectWaysFix(this.data.connectionTags));
let lessLikelyConnectionTags = tagsForConnectionNodeIfAllowed(entities[0], entities[1], graph, true);
if (lessLikelyConnectionTags && !isEqual(connectionTags, lessLikelyConnectionTags)) {
fixes.push(makeConnectWaysFix(lessLikelyConnectionTags));
}
}
if (isCrossingIndoors) {
@@ -692,16 +704,23 @@ export function validationCrossingWays(context) {
function makeConnectWaysFix(connectionTags) {
var fixTitleID = 'connect_features';
var fixIcon = 'iD-icon-crossing';
if (connectionTags.highway === 'crossing') {
fixTitleID = 'connect_using_crossing';
fixIcon = 'temaki-pedestrian';
}
if (connectionTags.ford) {
fixTitleID = 'connect_using_ford';
if (connectionTags.highway) {
fixIcon = 'temaki-pedestrian';
}
}
return new validationIssueFix({
icon: 'iD-icon-crossing',
const fix = new validationIssueFix({
icon: fixIcon,
title: t.append('issues.fix.' + fixTitleID + '.title'),
onClick: function(context) {
var loc = this.issue.loc;
var connectionTags = this.issue.data.connectionTags;
var edges = this.issue.data.edges;
context.perform(
@@ -737,6 +756,8 @@ export function validationCrossingWays(context) {
);
}
});
fix._connectionTags = connectionTags;
return fix;
}
function makeChangeLayerFix(higherOrLower) {
+669 -662
View File
File diff suppressed because it is too large Load Diff
+15 -15
View File
@@ -55,14 +55,14 @@
"abortcontroller-polyfill": "^1.7.5",
"aes-js": "^3.1.2",
"alif-toolkit": "^1.2.9",
"core-js-bundle": "^3.30.1",
"core-js-bundle": "^3.30.2",
"diacritics": "1.3.0",
"fast-deep-equal": "~3.1.1",
"fast-json-stable-stringify": "2.1.0",
"lodash-es": "~4.17.15",
"marked": "~4.3.0",
"marked": "~5.0.2",
"node-diff3": "~3.1.0",
"osm-auth": "~2.0.1",
"osm-auth": "~2.1.0",
"pannellum": "2.5.6",
"pbf": "^3.2.1",
"polygon-clipping": "~0.15.1",
@@ -75,22 +75,22 @@
"@fortawesome/free-brands-svg-icons": "~6.4.0",
"@fortawesome/free-regular-svg-icons": "~6.4.0",
"@fortawesome/free-solid-svg-icons": "~6.4.0",
"@rapideditor/temaki": "~5.3.0",
"@rapideditor/temaki": "~5.4.0",
"@mapbox/maki": "^8.0.1",
"@openstreetmap/id-tagging-schema": "^6.0.0",
"@openstreetmap/id-tagging-schema": "^6.2.0",
"@transifex/api": "^5.2.0",
"autoprefixer": "^10.4.14",
"chai": "^4.3.4",
"chai": "^4.3.7",
"chalk": "^4.1.2",
"cldr-core": "^41.0.0",
"cldr-localenames-full": "^41.0.0",
"cldr-core": "^43.0.0",
"cldr-localenames-full": "^43.0.0",
"concat-files": "^0.1.1",
"d3": "~7.8.1",
"d3": "~7.8.4",
"dotenv": "^16.0.3",
"editor-layer-index": "github:osmlab/editor-layer-index#gh-pages",
"esbuild": "^0.17.18",
"esbuild": "^0.17.19",
"esbuild-visualizer": "^0.4.0",
"eslint": "^8.8.0",
"eslint": "^8.41.0",
"fetch-mock": "^9.11.0",
"gaze": "^1.1.3",
"glob": "^9.2.1",
@@ -98,19 +98,19 @@
"js-yaml": "^4.0.0",
"json-stringify-pretty-compact": "^3.0.0",
"karma": "^6.4.2",
"karma-chrome-launcher": "^3.1.0",
"karma-chrome-launcher": "^3.2.0",
"karma-coverage": "2.1.1",
"karma-mocha": "^2.0.1",
"karma-remap-istanbul": "^0.6.0",
"mapillary_sprite_source": "^1.8.0",
"mapillary-js": "4.1.1",
"minimist": "^1.2.8",
"mocha": "^10.0.0",
"mocha": "^10.2.0",
"name-suggestion-index": "~6.0",
"node-fetch": "^2.6.9",
"node-fetch": "^2.6.11",
"npm-run-all": "^4.0.0",
"osm-community-index": "~5.5.2",
"postcss": "^8.4.21",
"postcss": "^8.4.23",
"postcss-selector-prepend": "^0.5.0",
"shelljs": "^0.8.0",
"shx": "^0.3.0",
+1 -1
View File
@@ -67,7 +67,7 @@ function getLangNamesInNativeLang() {
let langDirectoryPaths = fs.readdirSync(cldrMainDir);
langDirectoryPaths.forEach(code => {
let languagesPath = `${cldrMainDir}${code}/languages.json`;
//if (!fs.existsSync(languagesPath)) return;
if (!fs.existsSync(languagesPath)) return;
let languageObj = JSON.parse(fs.readFileSync(languagesPath, 'utf8')).main[code];
let identity = languageObj.identity;
+17 -1
View File
@@ -2,7 +2,12 @@ describe('iD.validations.crossing_ways', function () {
var context;
beforeEach(function() {
context = iD.coreContext().assetPath('../dist/').init();
const container = d3.select('body').append('div');
context = iD.coreContext().assetPath('../dist/').init().container(container);
container
.append('div')
.attr('class', 'main-map')
.call(context.map());
});
function createWaysWithOneCrossingPoint(tags1, tags2) {
@@ -253,6 +258,17 @@ describe('iD.validations.crossing_ways', function () {
verifySingleCrossingIssue(validate(), {});
});
it('flags sidewalk crossing service road', function() {
createWaysWithOneCrossingPoint({ highway: 'service' }, { highway: 'footway', footway: 'sidewalk' });
const issues = validate();
verifySingleCrossingIssue(issues, {});
context.enter(iD.modeSelect(context, ['w-1']));
const dynamicFixes = issues[0].dynamicFixes(context);
expect(dynamicFixes).to.have.lengthOf(5);
expect(dynamicFixes[0]._connectionTags).to.eql({});
expect(dynamicFixes[1]._connectionTags).to.eql({ highway: 'crossing' });
});
it('flags road crossing railway', function() {
createWaysWithOneCrossingPoint({ highway: 'residential' }, { railway: 'rail' });
verifySingleCrossingIssue(validate(), { railway: 'level_crossing' });