Files
iD/modules/ui/map_data.js

547 lines
17 KiB
JavaScript

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 += '<div>' + msg + '</div>';
}
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;
}