Files
iD/js/id/svg/areas.js
T
John Firebaugh b4fa085663 Do not fully fill certain landuse values, e.g. landuse=residential
residential, retail, commercial and industrial landuse tags
typically cover large, dense areas with many overlapping
features. The large click targets of filled areas have proven
to be very good for usability in the general case, but are
a common source of accidental mistagging in this case.

We may add additional tags to this list, but these are the
obvious first step — especially landuse=residential.

Fixes #542.
2014-07-29 10:34:27 -07:00

142 lines
4.4 KiB
JavaScript

iD.svg.Areas = function(projection) {
// Patterns only work in Firefox when set directly on element.
// (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
var patterns = {
wetland: 'wetland',
beach: 'beach',
scrub: 'scrub',
construction: 'construction',
military: 'construction',
cemetery: 'cemetery',
grave_yard: 'cemetery',
meadow: 'meadow',
farm: 'farmland',
farmland: 'farmland',
orchard: 'orchard'
};
var patternKeys = ['landuse', 'natural', 'amenity'];
var clipped = ['residential', 'commercial', 'retail', 'industrial'];
function clip(entity) {
return clipped.indexOf(entity.tags.landuse) !== -1;
}
function setPattern(d) {
for (var i = 0; i < patternKeys.length; i++) {
if (patterns.hasOwnProperty(d.tags[patternKeys[i]])) {
this.style.fill = 'url("#pattern-' + patterns[d.tags[patternKeys[i]]] + '")';
return;
}
}
this.style.fill = '';
}
return function drawAreas(surface, graph, entities, filter) {
var path = iD.svg.Path(projection, graph, true),
areas = {},
multipolygon;
for (var i = 0; i < entities.length; i++) {
var entity = entities[i];
if (entity.geometry(graph) !== 'area') continue;
multipolygon = iD.geo.isSimpleMultipolygonOuterMember(entity, graph);
if (multipolygon) {
areas[multipolygon.id] = {
entity: multipolygon.mergeTags(entity.tags),
area: Math.abs(entity.area(graph))
};
} else if (!areas[entity.id]) {
areas[entity.id] = {
entity: entity,
area: Math.abs(entity.area(graph))
};
}
}
areas = d3.values(areas).filter(function hasPath(a) { return path(a.entity); });
areas.sort(function areaSort(a, b) { return b.area - a.area; });
areas = _.pluck(areas, 'entity');
var strokes = areas.filter(function(area) {
return area.type === 'way';
});
var data = {
clip: areas.filter(clip),
shadow: strokes,
stroke: strokes,
fill: areas
};
var clipPaths = surface.selectAll('defs').selectAll('.clipPath')
.filter(filter)
.data(data.clip, iD.Entity.key);
clipPaths.enter()
.append('clipPath')
.attr('class', 'clipPath')
.attr('id', function(entity) { return entity.id + '-clippath'; })
.append('path');
clipPaths.selectAll('path')
.attr('d', path);
clipPaths.exit()
.remove();
var areagroup = surface
.select('.layer-areas')
.selectAll('g.areagroup')
.data(['fill', 'shadow', 'stroke']);
areagroup.enter()
.append('g')
.attr('class', function(d) { return 'layer areagroup area-' + d; });
var paths = areagroup
.selectAll('path')
.filter(filter)
.data(function(layer) { return data[layer]; }, iD.Entity.key);
// Remove exiting areas first, so they aren't included in the `fills`
// array used for sorting below (https://github.com/openstreetmap/iD/issues/1903).
paths.exit()
.remove();
var fills = surface.selectAll('.area-fill path.area')[0];
var bisect = d3.bisector(function(node) {
return -node.__data__.area(graph);
}).left;
function sortedByArea(entity) {
if (this.__data__ === 'fill') {
return fills[bisect(fills, -entity.area(graph))];
}
}
paths.enter()
.insert('path', sortedByArea)
.each(function(entity) {
var layer = this.parentNode.__data__;
this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
if (layer === 'fill' && clip(entity)) {
this.setAttribute('clip-path', 'url(#' + entity.id + '-clippath)');
}
if (layer === 'fill') {
setPattern.apply(this, arguments);
}
})
.call(iD.svg.TagClasses());
paths
.attr('d', path);
};
};