Add CSS classes for relation memberships

An entity that is a member of a relation will have the classes
`member`, `member-role-<member role>`, and `member-type-<relation type>`.

The first use of these classes is to avoid filling multipolygon
member areas.
This commit is contained in:
John Firebaugh
2013-01-17 15:12:48 -08:00
parent 7df1a00f38
commit 3eaf4a46e0
12 changed files with 135 additions and 23 deletions
+4
View File
@@ -154,6 +154,10 @@ path.multipolygon {
fill-rule: evenodd;
}
path.area.member-type-multipolygon {
fill: none;
}
path.area.selected {
stroke-width:4 !important;
}
+1
View File
@@ -43,6 +43,7 @@
<script src="js/id/svg.js"></script>
<script src="js/id/svg/areas.js"></script>
<script src="js/id/svg/lines.js"></script>
<script src="js/id/svg/member_classes.js"></script>
<script src="js/id/svg/midpoints.js"></script>
<script src="js/id/svg/multipolygons.js"></script>
<script src="js/id/svg/points.js"></script>
+2 -1
View File
@@ -52,7 +52,8 @@ iD.svg.Areas = function(projection) {
paths
.order()
.attr('d', lineString)
.call(iD.svg.TagClasses());
.call(iD.svg.TagClasses())
.call(iD.svg.MemberClasses(graph));
paths.exit()
.remove();
+20 -20
View File
@@ -33,27 +33,27 @@ iD.svg.Lines = function(projection) {
return as - bs;
}
function drawPaths(group, lines, filter, classes, lineString) {
var paths = group.selectAll('path')
.filter(filter)
.data(lines, iD.Entity.key);
paths.enter()
.append('path')
.attr('class', classes);
paths
.order()
.attr('d', lineString)
.call(iD.svg.TagClasses());
paths.exit()
.remove();
return paths;
}
return function drawLines(surface, graph, entities, filter) {
function drawPaths(group, lines, filter, classes, lineString) {
var paths = group.selectAll('path')
.filter(filter)
.data(lines, iD.Entity.key);
paths.enter()
.append('path')
.attr('class', classes);
paths
.order()
.attr('d', lineString)
.call(iD.svg.TagClasses())
.call(iD.svg.MemberClasses(graph));
paths.exit()
.remove();
return paths;
}
if (!alength) {
var arrow = surface.append('text').text(arrowtext);
+32
View File
@@ -0,0 +1,32 @@
iD.svg.MemberClasses = function(graph) {
var tagClassRe = /^member-?/;
return function memberClassesSelection(selection) {
selection.each(function memberClassesEach(d, i) {
var classes, value = this.className;
if (value.baseVal !== undefined) value = value.baseVal;
classes = value.trim().split(/\s+/).filter(function(name) {
return name.length && !tagClassRe.test(name);
}).join(' ');
var relations = graph.parentRelations(d);
if (relations.length) {
classes += ' member';
}
relations.forEach(function (relation) {
classes += ' member-type-' + relation.tags.type;
classes += ' member-role-' + _.find(relation.members, function (member) { return member.id == d.id; }).role;
});
classes = classes.trim();
if (classes !== value) {
d3.select(this).attr('class', classes);
}
});
};
};
+2 -1
View File
@@ -40,7 +40,8 @@ iD.svg.Multipolygons = function(projection) {
paths
.order()
.attr('d', lineString)
.call(iD.svg.TagClasses());
.call(iD.svg.TagClasses())
.call(iD.svg.MemberClasses(graph));
paths.exit()
.remove();
+2 -1
View File
@@ -45,7 +45,8 @@ iD.svg.Points = function(projection) {
.attr('transform', 'translate(-8, -8)');
groups.attr('transform', iD.svg.PointTransform(projection))
.call(iD.svg.TagClasses());
.call(iD.svg.TagClasses())
.call(iD.svg.MemberClasses(graph));
// Selecting the following implicitly
// sets the data (point entity) on the element
+1
View File
@@ -31,6 +31,7 @@ iD.svg.Vertices = function(projection) {
groups.attr('transform', iD.svg.PointTransform(projection))
.call(iD.svg.TagClasses())
.call(iD.svg.MemberClasses(graph))
.classed('shared', function(entity) { return graph.parentWays(entity).length > 1; });
// Selecting the following implicitly
+3
View File
@@ -45,6 +45,7 @@
<script src="../js/id/svg.js"></script>
<script src="../js/id/svg/areas.js"></script>
<script src="../js/id/svg/lines.js"></script>
<script src="../js/id/svg/member_classes.js"></script>
<script src="../js/id/svg/midpoints.js"></script>
<script src="../js/id/svg/multipolygons.js"></script>
<script src="../js/id/svg/points.js"></script>
@@ -159,6 +160,8 @@
<script src="spec/svg.js"></script>
<script src="spec/svg/areas.js"></script>
<script src="spec/svg/lines.js"></script>
<script src="spec/svg/member_classes.js"></script>
<script src="spec/svg/multipolygons.js"></script>
<script src="spec/svg/points.js"></script>
<script src="spec/svg/vertices.js"></script>
+2
View File
@@ -64,6 +64,8 @@
<script src="spec/svg.js"></script>
<script src="spec/svg/areas.js"></script>
<script src="spec/svg/lines.js"></script>
<script src="spec/svg/member_classes.js"></script>
<script src="spec/svg/multipolygons.js"></script>
<script src="spec/svg/points.js"></script>
<script src="spec/svg/vertices.js"></script>
+12
View File
@@ -28,6 +28,18 @@ describe("iD.svg.Areas", function () {
expect(surface.select('.area')).to.be.classed('tag-building-yes');
});
it("adds member classes", function () {
var area = iD.Way({tags: {area: 'yes'}}),
relation = iD.Relation({members: [{id: area.id, role: 'outer'}], tags: {type: 'multipolygon'}}),
graph = iD.Graph([area, relation]);
surface.call(iD.svg.Areas(projection), graph, [area], filter);
expect(surface.select('.area')).to.be.classed('member');
expect(surface.select('.area')).to.be.classed('member-role-outer');
expect(surface.select('.area')).to.be.classed('member-type-multipolygon');
});
it("preserves non-area paths", function () {
var area = iD.Way({tags: {area: 'yes'}}),
graph = iD.Graph([area]);
+54
View File
@@ -0,0 +1,54 @@
describe("iD.svg.MemberClasses", function () {
var selection;
beforeEach(function () {
selection = d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'g'));
});
it("adds no classes to elements that aren't a member of any relations", function() {
var node = iD.Node(),
graph = iD.Graph([node]);
selection
.datum(node)
.call(iD.svg.MemberClasses(graph));
expect(selection.attr('class')).to.equal(null);
});
it("adds tags for member, role, and type", function() {
var node = iD.Node(),
relation = iD.Relation({members: [{id: node.id, role: 'r'}], tags: {type: 't'}}),
graph = iD.Graph([node, relation]);
selection
.datum(node)
.call(iD.svg.MemberClasses(graph));
expect(selection.attr('class')).to.equal('member member-type-t member-role-r');
});
it('removes classes for tags that are no longer present', function() {
var node = iD.Entity(),
graph = iD.Graph([node]);
selection
.attr('class', 'member member-type-t member-role-r')
.datum(node)
.call(iD.svg.MemberClasses(graph));
expect(selection.attr('class')).to.equal('');
});
it("preserves existing non-'member-'-prefixed classes", function() {
var node = iD.Entity(),
graph = iD.Graph([node]);
selection
.attr('class', 'selected')
.datum(node)
.call(iD.svg.MemberClasses(graph));
expect(selection.attr('class')).to.equal('selected');
});
});