import * as d3 from 'd3'; import _ from 'lodash'; import { d3keybinding } from '../lib/d3.keybinding.js'; import { t, textDirection } from '../util/locale'; import { svgIcon } from '../svg/index'; import { uiTooltipHtml } from './tooltipHtml'; import { tooltip } from '../util/tooltip'; export function uiMapData(context) { var key = t('map_data.key'), features = context.features().keys(), layers = context.layers(), fills = ['wireframe', 'partial', 'full'], fillDefault = context.storage('area-fill') || 'partial', fillSelected = fillDefault; function map_data(selection) { function showsFeature(d) { return context.features().enabled(d); } function autoHiddenFeature(d) { return context.features().autoHidden(d); } function clickFeature(d) { context.features().toggle(d); update(); } function showsFill(d) { return fillSelected === d; } function setFill(d) { _.each(fills, function(opt) { context.surface().classed('fill-' + opt, Boolean(opt === d)); }); fillSelected = d; if (d !== 'wireframe') { fillDefault = d; context.storage('area-fill', d); } update(); } function showsLayer(which) { var layer = layers.layer(which); if (layer) { return layer.enabled(); } return false; } function setLayer(which, enabled) { var layer = layers.layer(which); if (layer) { layer.enabled(enabled); update(); } } function toggleLayer(which) { setLayer(which, !showsLayer(which)); } function clickMapillaryImages() { toggleLayer('mapillary-images'); if (!showsLayer('mapillary-images')) { setLayer('mapillary-signs', false); } } function clickMapillarySigns() { toggleLayer('mapillary-signs'); } function drawMapillaryItems(selection) { var mapillaryImages = layers.layer('mapillary-images'), mapillarySigns = layers.layer('mapillary-signs'), supportsMapillaryImages = mapillaryImages && mapillaryImages.supported(), supportsMapillarySigns = mapillarySigns && mapillarySigns.supported(), showsMapillaryImages = supportsMapillaryImages && mapillaryImages.enabled(), showsMapillarySigns = supportsMapillarySigns && mapillarySigns.enabled(); var mapillaryList = selection .selectAll('.layer-list-mapillary') .data([0]); mapillaryList = mapillaryList.enter() .append('ul') .attr('class', 'layer-list layer-list-mapillary') .merge(mapillaryList); var mapillaryImageLayerItem = mapillaryList .selectAll('.list-item-mapillary-images') .data(supportsMapillaryImages ? [0] : []); mapillaryImageLayerItem.exit() .remove(); var enterImages = mapillaryImageLayerItem.enter() .append('li') .attr('class', 'list-item-mapillary-images'); var labelImages = enterImages .append('label') .call(tooltip() .title(t('mapillary_images.tooltip')) .placement('top')); labelImages .append('input') .attr('type', 'checkbox') .on('change', clickMapillaryImages); labelImages .append('span') .text(t('mapillary_images.title')); var mapillarySignLayerItem = mapillaryList .selectAll('.list-item-mapillary-signs') .data(supportsMapillarySigns ? [0] : []); mapillarySignLayerItem.exit() .remove(); var enterSigns = mapillarySignLayerItem.enter() .append('li') .attr('class', 'list-item-mapillary-signs'); var labelSigns = enterSigns .append('label') .call(tooltip() .title(t('mapillary_signs.tooltip')) .placement('top')); labelSigns .append('input') .attr('type', 'checkbox') .on('change', clickMapillarySigns); labelSigns .append('span') .text(t('mapillary_signs.title')); // Updates mapillaryImageLayerItem = mapillaryImageLayerItem .merge(enterImages); mapillaryImageLayerItem .classed('active', showsMapillaryImages) .selectAll('input') .property('checked', showsMapillaryImages); mapillarySignLayerItem = mapillarySignLayerItem .merge(enterSigns); mapillarySignLayerItem .classed('active', showsMapillarySigns) .selectAll('input') .property('disabled', !showsMapillaryImages) .property('checked', showsMapillarySigns); mapillarySignLayerItem .selectAll('label') .classed('deemphasize', !showsMapillaryImages); } function drawOsmItem(selection) { var osm = layers.layer('osm'), showsOsm = osm.enabled(); var ul = selection .selectAll('.layer-list-osm') .data(osm ? [0] : []); // Exit ul.exit() .remove(); // Enter var ulEnter = ul.enter() .append('ul') .attr('class', 'layer-list layer-list-osm'); var liEnter = ulEnter .append('li') .attr('class', 'list-item-osm'); var labelEnter = liEnter .append('label') .call(tooltip() .title(t('map_data.layers.osm.tooltip')) .placement('top') ); labelEnter .append('input') .attr('type', 'checkbox') .on('change', function() { toggleLayer('osm'); }); labelEnter .append('span') .text(t('map_data.layers.osm.title')); // Update ul = ul .merge(ulEnter); ul.selectAll('.list-item-osm') .classed('active', showsOsm) .selectAll('input') .property('checked', showsOsm); } function drawGpxItem(selection) { var gpx = layers.layer('gpx'), hasGpx = gpx && gpx.hasGpx(), showsGpx = hasGpx && gpx.enabled(); var ul = selection .selectAll('.layer-list-gpx') .data(gpx ? [0] : []); // Exit ul.exit() .remove(); // Enter var ulEnter = ul.enter() .append('ul') .attr('class', 'layer-list layer-list-gpx'); var liEnter = ulEnter .append('li') .attr('class', 'list-item-gpx'); liEnter .append('button') .attr('class', 'list-item-gpx-extent') .call(tooltip() .title(t('gpx.zoom')) .placement((textDirection === 'rtl') ? 'right' : 'left')) .on('click', function() { d3.event.preventDefault(); d3.event.stopPropagation(); gpx.fitZoom(); }) .call(svgIcon('#icon-search')); liEnter .append('button') .attr('class', 'list-item-gpx-browse') .call(tooltip() .title(t('gpx.browse')) .placement((textDirection === 'rtl') ? 'right' : 'left') ) .on('click', function() { d3.select(document.createElement('input')) .attr('type', 'file') .on('change', function() { gpx.files(d3.event.target.files); }) .node().click(); }) .call(svgIcon('#icon-geolocate')); var labelEnter = liEnter .append('label') .call(tooltip() .title(t('gpx.drag_drop')) .placement('top') ); labelEnter .append('input') .attr('type', 'checkbox') .on('change', function() { toggleLayer('gpx'); }); labelEnter .append('span') .text(t('gpx.local_layer')); // Update ul = ul .merge(ulEnter); ul.selectAll('.list-item-gpx') .classed('active', showsGpx) .selectAll('label') .classed('deemphasize', !hasGpx) .selectAll('input') .property('disabled', !hasGpx) .property('checked', showsGpx); } function drawList(selection, data, type, name, change, active) { var items = selection.selectAll('li') .data(data); // Exit items.exit() .remove(); // Enter var enter = items.enter() .append('li') .attr('class', 'layer') .call(tooltip() .html(true) .title(function(d) { var tip = t(name + '.' + d + '.tooltip'), key = (d === 'wireframe' ? t('area_fill.wireframe.key') : null); if (name === 'feature' && autoHiddenFeature(d)) { var msg = showsLayer('osm') ? t('map_data.autohidden') : t('map_data.osmhidden'); tip += '
' + msg + '
'; } return uiTooltipHtml(tip, key); }) .placement('top') ); var label = enter .append('label'); label .append('input') .attr('type', type) .attr('name', name) .on('change', change); label .append('span') .text(function(d) { return t(name + '.' + d + '.description'); }); // Update items = items .merge(enter); items .classed('active', active) .selectAll('input') .property('checked', active) .property('indeterminate', function(d) { return (name === 'feature' && autoHiddenFeature(d)); }); } function update() { dataLayerContainer .call(drawOsmItem) .call(drawMapillaryItems) .call(drawGpxItem); fillList .call(drawList, fills, 'radio', 'area_fill', setFill, showsFill); featureList .call(drawList, features, 'checkbox', 'feature', clickFeature, showsFeature); } function hidePanel() { setVisible(false); } function togglePanel() { if (d3.event) d3.event.preventDefault(); tooltipBehavior.hide(button); setVisible(!button.classed('active')); } function toggleWireframe() { if (d3.event) { d3.event.preventDefault(); d3.event.stopPropagation(); } setFill((fillSelected === 'wireframe' ? fillDefault : 'wireframe')); context.map().pan([0,0]); // trigger a redraw } function setVisible(show) { if (show !== shown) { button.classed('active', show); shown = show; if (show) { update(); selection.on('mousedown.map_data-inside', function() { return d3.event.stopPropagation(); }); content.style('display', 'block') .style('right', '-300px') .transition() .duration(200) .style('right', '0px'); } else { content.style('display', 'block') .style('right', '0px') .transition() .duration(200) .style('right', '-300px') .on('end', function() { d3.select(this).style('display', 'none'); }); selection.on('mousedown.map_data-inside', null); } } } var content = selection .append('div') .attr('class', 'fillL map-overlay col3 content hide'), tooltipBehavior = tooltip() .placement((textDirection === 'rtl') ? 'right' : 'left') .html(true) .title(uiTooltipHtml(t('map_data.description'), key)), button = selection .append('button') .attr('tabindex', -1) .on('click', togglePanel) .call(svgIcon('#icon-data', 'light')) .call(tooltipBehavior), shown = false; content .append('h4') .text(t('map_data.title')); // data layers content .append('a') .text(t('map_data.data_layers')) .attr('href', '#') .classed('hide-toggle', true) .classed('expanded', true) .on('click', function() { var exp = d3.select(this).classed('expanded'); dataLayerContainer.style('display', exp ? 'none' : 'block'); d3.select(this).classed('expanded', !exp); d3.event.preventDefault(); }); var dataLayerContainer = content .append('div') .attr('class', 'data-data-layers') .style('display', 'block'); // area fills content .append('a') .text(t('map_data.fill_area')) .attr('href', '#') .classed('hide-toggle', true) .classed('expanded', false) .on('click', function() { var exp = d3.select(this).classed('expanded'); fillContainer.style('display', exp ? 'none' : 'block'); d3.select(this).classed('expanded', !exp); d3.event.preventDefault(); }); var fillContainer = content .append('div') .attr('class', 'data-area-fills') .style('display', 'none'); var fillList = fillContainer .append('ul') .attr('class', 'layer-list layer-fill-list'); // feature filters content .append('a') .text(t('map_data.map_features')) .attr('href', '#') .classed('hide-toggle', true) .classed('expanded', false) .on('click', function() { var exp = d3.select(this).classed('expanded'); featureContainer.style('display', exp ? 'none' : 'block'); d3.select(this).classed('expanded', !exp); d3.event.preventDefault(); }); var featureContainer = content .append('div') .attr('class', 'data-feature-filters') .style('display', 'none'); var featureList = featureContainer .append('ul') .attr('class', 'layer-list layer-feature-list'); context.features() .on('change.map_data-update', update); setFill(fillDefault); var keybinding = d3keybinding('features') .on(key, togglePanel) .on(t('area_fill.wireframe.key'), toggleWireframe) .on([t('background.key'), t('help.key')], hidePanel); d3.select(document) .call(keybinding); context.surface().on('mousedown.map_data-outside', hidePanel); context.container().on('mousedown.map_data-outside', hidePanel); } return map_data; }