diff --git a/css/65_data.css b/css/65_data.css index fcf5d3a66..6be709a5f 100644 --- a/css/65_data.css +++ b/css/65_data.css @@ -59,16 +59,33 @@ pointer-events: none; } -.layer-mapdata path { +.layer-mapdata path.shadow { + stroke: #f6634f; + stroke-width: 16; + stroke-opacity: 0; + fill: none; +} +.layer-mapdata path.shadow.related:not(.selected), +.layer-mapdata path.shadow.hover:not(.selected) { + stroke-opacity: 0.4; +} +.layer-mapdata path.shadow.selected { + stroke-opacity: 0.7; +} + +.layer-mapdata path.stroke { stroke: #ff26d4; stroke-width: 2; fill: none; } -.layer-mapdata path.MultiPolygon, -.layer-mapdata path.Polygon { - stroke-width: 1; + +.layer-mapdata path.fill { + stroke-width: 0; + stroke-opacity: 0.3; + stroke: #ff26d4; fill: #ff26d4; - fill-opacity: 0.2; + fill-opacity: 0.3; + fill-rule: evenodd; } .layer-mapdata text.label-halo, diff --git a/css/70_fills.css b/css/70_fills.css index 5b0996de8..fa6e58cf8 100644 --- a/css/70_fills.css +++ b/css/70_fills.css @@ -7,6 +7,11 @@ stroke-dasharray: none !important; fill: none !important; } +.low-zoom.fill-wireframe .layer-mapdata path.stroke, +.fill-wireframe .layer-mapdata path.stroke { + stroke-width: 2 !important; + stroke-opacity: 1 !important; +} .low-zoom.fill-wireframe path.shadow, .fill-wireframe path.shadow { diff --git a/modules/svg/areas.js b/modules/svg/areas.js index 01a12de7f..546919ecd 100644 --- a/modules/svg/areas.js +++ b/modules/svg/areas.js @@ -132,7 +132,7 @@ export function svgAreas(projection, context) { fill: areas }; - var clipPaths = context.surface().selectAll('defs').selectAll('.clipPath') + var clipPaths = context.surface().selectAll('defs').selectAll('.clipPath-osm') .filter(filter) .data(data.clip, osmEntity.key); @@ -141,7 +141,7 @@ export function svgAreas(projection, context) { var clipPathsEnter = clipPaths.enter() .append('clipPath') - .attr('class', 'clipPath') + .attr('class', 'clipPath-osm') .attr('id', function(entity) { return entity.id + '-clippath'; }); clipPathsEnter diff --git a/modules/svg/data.js b/modules/svg/data.js index 8ae1ab4fc..80f97b1d3 100644 --- a/modules/svg/data.js +++ b/modules/svg/data.js @@ -151,6 +151,21 @@ export function svgData(projection, context, dispatch) { } + function featureKey(d) { + return d.__featurehash__; + } + + + function isPolygon(d) { + return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon'; + } + + + function clipPathID(d) { + return 'data-' + d.__featurehash__ + '-clippath'; + } + + function featureClasses(d) { return [ 'data' + d.__featurehash__, @@ -176,8 +191,12 @@ export function svgData(projection, context, dispatch) { .attr('class', 'layer-mapdata') .merge(layer); + var surface = context.surface(); + if (!surface || surface.empty()) return; // not ready to draw yet, starting up - var geoData; + + // Gather data + var geoData, polygonData; if (_template && vtService) { // fetch data from vector tile service var sourceID = _template; vtService.loadTiles(sourceID, _template, projection); @@ -186,11 +205,50 @@ export function svgData(projection, context, dispatch) { geoData = getFeatures(_geojson); } geoData = geoData.filter(getPath); + polygonData = geoData.filter(isPolygon); - var paths = layer + // Draw clip paths for polygons + var clipPaths = surface.selectAll('defs').selectAll('.clipPath-data') + .data(polygonData, featureKey); + + clipPaths.exit() + .remove(); + + var clipPathsEnter = clipPaths.enter() + .append('clipPath') + .attr('class', 'clipPath-data') + .attr('id', clipPathID); + + clipPathsEnter + .append('path'); + + clipPaths.merge(clipPathsEnter) + .selectAll('path') + .attr('d', getPath); + + + // Draw fill, shadow, stroke layers + var datagroups = layer + .selectAll('g.datagroup') + .data(['fill', 'shadow', 'stroke']); + + datagroups = datagroups.enter() + .append('g') + .attr('class', function(d) { return 'datagroup datagroup-' + d; }) + .merge(datagroups); + + + // Draw paths + var pathData = { + shadow: geoData, + stroke: geoData, + fill: polygonData + }; + + var paths = datagroups .selectAll('path') - .data(geoData, function(d) { return d.__featurehash__; }); + .data(function(layer) { return pathData[layer]; }, featureKey); // exit paths.exit() @@ -199,11 +257,20 @@ export function svgData(projection, context, dispatch) { // enter/update paths = paths.enter() .append('path') - .attr('class', function(d) { return 'pathdata ' + featureClasses(d); }) + .attr('class', function(d) { + var datagroup = this.parentNode.__data__; + var area = (datagroup === 'fill' ? 'area ' : ''); + return 'pathdata ' + area + datagroup + ' ' + featureClasses(d); + }) + .attr('clip-path', function(d) { + var datagroup = this.parentNode.__data__; + return datagroup === 'fill' ? ('url(#' + clipPathID(d) + ')') : null; + }) .merge(paths) .attr('d', getPath); + // Draw labels layer .call(drawLabels, 'label-halo', geoData) .call(drawLabels, 'label', geoData); @@ -216,7 +283,7 @@ export function svgData(projection, context, dispatch) { }); var labels = selection.selectAll('text.' + textClass) - .data(labelData, function(d) { return d.__featurehash__; }); + .data(labelData, featureKey); // exit labels.exit() @@ -312,7 +379,7 @@ export function svgData(projection, context, dispatch) { drawData.hasData = function() { - return !!(_template || _geojson); + return !!(_template || !_isEmpty(_geojson)); };