modules/svg/geolocate.js (105 lines of code) (raw):

import { select as d3_select } from 'd3-selection'; import { svgPointTransform } from './helpers'; import { geoMetersToLat } from '../geo'; export function svgGeolocate(projection) { var layer = d3_select(null); var _position; function init() { if (svgGeolocate.initialized) return; // run once svgGeolocate.enabled = false; svgGeolocate.initialized = true; } function showLayer() { layer.style('display', 'block'); } function hideLayer() { layer .transition() .duration(250) .style('opacity', 0); } function layerOn() { layer .style('opacity', 0) .transition() .duration(250) .style('opacity', 1); } function layerOff() { layer.style('display', 'none'); } function transform(d) { return svgPointTransform(projection)(d); } function accuracy(accuracy, loc) { // converts accuracy to pixels... var degreesRadius = geoMetersToLat(accuracy), tangentLoc = [loc[0], loc[1] + degreesRadius], projectedTangent = projection(tangentLoc), projectedLoc = projection([loc[0], loc[1]]); // southern most point will have higher pixel value... return Math.round(projectedLoc[1] - projectedTangent[1]).toString(); } function update() { var geolocation = { loc: [_position.coords.longitude, _position.coords.latitude] }; var groups = layer.selectAll('.geolocations').selectAll('.geolocation') .data([geolocation]); groups.exit() .remove(); var pointsEnter = groups.enter() .append('g') .attr('class', 'geolocation'); pointsEnter .append('circle') .attr('id', 'geolocate-radius') .attr('dx', '0') .attr('dy', '0') .attr('fill', 'rgb(15,128,225)') .attr('fill-opacity', '0.3') .attr('r', '0'); pointsEnter .append('circle') .attr('dx', '0') .attr('dy', '0') .attr('fill', 'rgb(15,128,225)') .attr('stroke', 'white') .attr('stroke-width', '1.5') .attr('r', '6'); groups.merge(pointsEnter) .attr('transform', transform); d3_select('#geolocate-radius').attr('r', accuracy(_position.coords.accuracy, geolocation.loc)); } function drawLocation(selection) { var enabled = svgGeolocate.enabled; layer = selection.selectAll('.layer-geolocate') .data([0]); layer.exit() .remove(); var layerEnter = layer.enter() .append('g') .attr('class', 'layer-geolocate') .style('display', enabled ? 'block' : 'none'); layerEnter .append('g') .attr('class', 'geolocations'); layer = layerEnter .merge(layer); if (enabled) { update(); } else { layerOff(); } } drawLocation.enabled = function (position, enabled) { if (!arguments.length) return svgGeolocate.enabled; _position = position; svgGeolocate.enabled = enabled; if (svgGeolocate.enabled) { showLayer(); layerOn(); } else { hideLayer(); } return this; }; init(); return drawLocation; }