Skip to content

Commit

Permalink
Add warning for features without start date (#193)
Browse files Browse the repository at this point in the history
* Add empty start date validation

* Add tests

* Fix reference

* Review fixes

* Update text

* Apply suggestions from code review

---------

Co-authored-by: Minh Nguyễn <[email protected]>
  • Loading branch information
pedromml and 1ec5 authored Mar 22, 2024
1 parent 148c32a commit 7e31e4f
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 1 deletion.
7 changes: 7 additions & 0 deletions data/core.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1894,6 +1894,11 @@ en:
tip: "Find relations with missing or incorrect member roles"
multipolygon:
reference: "Multipolygon members must have an inner or outer role."
missing_start_date:
title: Missing Start Dates
feature:
message: "Find features that are missing a start date"
reference: "Every feature should have a start date to prevent it from appearing since the beginning of time. If you are not sure about the start date, you can add an approximate date or a date range using the EDTF date field."
missing_tag:
title: Missing Tags
tip: "Find features that are missing descriptive tags"
Expand Down Expand Up @@ -1977,6 +1982,8 @@ en:
add_a_tunnel:
title: Add a tunnel
annotation: Added a tunnel.
add_start_date:
title: Add a start date
address_the_concern:
title: Address the concern
change_date_range:
Expand Down
3 changes: 2 additions & 1 deletion modules/osm/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@ export {
osmRemoveLifecyclePrefix,
osmRoutableHighwayTagValues,
osmFlowingWaterwayTagValues,
osmRailwayTrackTagValues
osmRailwayTrackTagValues,
osmTimelessFeatureTagValues
} from './tags';
5 changes: 5 additions & 0 deletions modules/osm/tags.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,3 +255,8 @@ export function isColourValid(value) {
}
return true;
}

export var osmTimelessFeatureTagValues = {
wood: true, wetland: true, beach: true, cave_entrance: true, peak: true, cliff: true, coastline: true, tree_row: true,
water: true, scrub: true, grassland: true, heath: true, bare_rock: true, glacier: true, stream: true, river: true, pond: true, basin: true, lake: true
};
1 change: 1 addition & 0 deletions modules/validations/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export { validationAlmostJunction } from './almost_junction';
export { validationCloseNodes } from './close_nodes';
export { validationCrossingWays } from './crossing_ways';
export { validationDisconnectedWay } from './disconnected_way';
export { validationMissingStartDate } from './missing_start_date';
export { validationFormatting } from './invalid_format';
export { validationHelpRequest } from './help_request';
export { validationImpossibleOneway } from './impossible_oneway';
Expand Down
64 changes: 64 additions & 0 deletions modules/validations/missing_start_date.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { t } from '../core/localizer';
import { utilDisplayLabel } from '../util';
import { validationIssue, validationIssueFix } from '../core/validation';
import { osmTimelessFeatureTagValues } from '../osm/tags';

export function validationMissingStartDate(context) {
const type = 'missing_start_date';

const validation = function checkMissingStartDate(entity, graph) {
// If start_date is not empty, return nothing
if (entity.tags && (entity.tags.start_date || entity.tags['start_date:edtf'])) return [];
// If entity has no tags, return nothing
if (Object.keys(entity.tags).length === 0) return [];
// Rule should be ignored for natural entities and waterways
if (entity.tags && (
(entity.tags.natural && osmTimelessFeatureTagValues[entity.tags.natural]) ||
(entity.tags.waterway && osmTimelessFeatureTagValues[entity.tags.waterway]) ||
(entity.tags.water && osmTimelessFeatureTagValues[entity.tags.water]))) return [];

// If entity is a vertex node
var osm = context.connection();
var isUnloadedNode = entity.type === 'node' && osm && !osm.isDataLoaded(entity.loc);

// Should skip this validation if node is unloaded, is a vertex or has parent relations
if (isUnloadedNode ||
// allow untagged nodes that are part of ways
entity.geometry(graph) === 'vertex' ||
// allow untagged entities that are part of relations
entity.hasParentRelations(graph)) return [];

const entityID = entity.id;

function showReference(selection) {
selection.selectAll('.issue-reference')
.data([0])
.enter()
.append('div')
.attr('class', 'issue-reference')
.call(t.append('issues.missing_start_date.reference'));
}

return [new validationIssue({
type: type,
severity: 'warning',
message: (context) => {
const entity = context.hasEntity(entityID);
return entity ? t.append('issues.missing_start_date.feature.message', {
feature: utilDisplayLabel(entity, context.graph())
}) : '';
},
reference: showReference,
entityIds: [entityID],
dynamicFixes: () => {
return [
new validationIssueFix({ title: t.append('issues.fix.add_start_date.title')})
];
}
})];
};

validation.type = type;

return validation;
}
80 changes: 80 additions & 0 deletions test/spec/validations/missing_start_date.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
describe('iD.validations.missing_start_date', function () {
var context;

beforeEach(function() {
context = iD.coreContext().assetPath('../dist/').init();
});

function createWay(tags) {
var n1 = iD.osmNode({id: 'n-1', loc: [4,4]});
var n2 = iD.osmNode({id: 'n-2', loc: [4,5]});
var n3 = iD.osmNode({id: 'n-3', loc: [5,5]});
var w = iD.osmWay({id: 'w-1', nodes: ['n-1', 'n-2', 'n-3'], tags: tags});

context.perform(
iD.actionAddEntity(n1),
iD.actionAddEntity(n2),
iD.actionAddEntity(n3),
iD.actionAddEntity(w)
);
}

function validate() {
var validator = iD.validationMissingStartDate(context);
var changes = context.history().changes();
var entities = changes.modified.concat(changes.created);
var issues = [];
entities.forEach(function(entity) {
issues = issues.concat(validator(entity, context.graph()));
});
return issues;
}

it('has only missing tag errors on init', function() {
var issues = validate();
expect(issues).to.have.lengthOf(0);
});

it('ignores way with no tags', function() {
createWay({});
var issues = validate();
expect(issues).to.have.lengthOf(0);
});

it('ignores way with start_date tag', function() {
createWay({ start_date: '1950' });
var issues = validate();
expect(issues).to.have.lengthOf(0);
});

it('ignores way with start_date:edtf tag', function() {
createWay({ 'start_date:edtf': '1950' });
var issues = validate();
expect(issues).to.have.lengthOf(0);
});

it('ignores way with natural tag', function() {
createWay({ natural: 'wood' });
var issues = validate();
expect(issues).to.have.lengthOf(0);
});

it('ignores way with waterway tag', function() {
createWay({ waterway: 'river' });
var issues = validate();
expect(issues).to.have.lengthOf(0);
});

it('ignores way with water tag', function() {
createWay({ water: 'pond' });
var issues = validate();
expect(issues).to.have.lengthOf(0);
});

it('flags way without start_date tag', function() {
createWay({ amenity: 'cafe' });
var issues = validate();
expect(issues).to.have.lengthOf(1);
});

});

0 comments on commit 7e31e4f

Please sign in to comment.