From b9ddfca152eda0ea6a77e162b254a21b79de222a Mon Sep 17 00:00:00 2001 From: Max Grossman Date: Wed, 19 Dec 2018 11:06:41 -0500 Subject: [PATCH] implement point/puck geolocation ref #5587 --- modules/svg/geolocate.js | 88 ++++++++++++++++++++++++++++------------ modules/svg/index.js | 1 + modules/ui/geolocate.js | 35 ++++++++++------ 3 files changed, 85 insertions(+), 39 deletions(-) diff --git a/modules/svg/geolocate.js b/modules/svg/geolocate.js index 228efd7b55..22ad33de86 100644 --- a/modules/svg/geolocate.js +++ b/modules/svg/geolocate.js @@ -1,4 +1,13 @@ -import { svgPointTransform } from "./helpers"; +import { select as d3_select } from 'd3-selection'; + +import { svgPointTransform } from './helpers'; +import { geoMetersToLat } from '../geo'; +import { + geoIdentity as d3_geoIdentity, + geoPath as d3_geoPath, + geoStream as d3_geoStream +} from 'd3-geo'; +import _throttle from 'lodash-es/throttle'; export function svgGeolocate(projection, context, dispatch) { var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000); @@ -14,8 +23,7 @@ export function svgGeolocate(projection, context, dispatch) { } function showLayer() { - layer.attr('display', 'block'); - layerOn(); + layer.style('display', 'block'); } @@ -25,7 +33,7 @@ export function svgGeolocate(projection, context, dispatch) { .transition() .duration(250) .style('opacity', 0) - .on('end', function() {}); + .on('end', function () { }); } function layerOn() { @@ -33,81 +41,107 @@ export function svgGeolocate(projection, context, dispatch) { .style('opacity', 0) .transition() .duration(250) - .style('opacity', 1) - .on('end', function () { dispatch.call('change'); }); + .style('opacity', 1); } function layerOff() { - // layer.selectAll('.viewfield-group').remove(); 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]]); + + return Math.round(projectedLoc[1] - projectedTangent[1]).toString(); // more south point will have higher pixel value... + } + function update() { - var points = layer.selectAll('.geolocations').selectAll('.geolocation') - .data([_position]); - + var geolocation = { loc: [_position.coords.longitude, _position.coords.latitude] }, + pixelAccuracy = accuracy(_position.coords.accuracy, geolocation.loc); + + var groups = layer.selectAll('.geolocations').selectAll('.geolocation') + .data([geolocation]); + + groups.exit() + .remove(); - points.enter() + var pointsEnter = groups.enter() .append('g') - .attr('class', 'point') + .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', pixelAccuracy) } function drawLocation(selection) { var enabled = svgGeolocate.enabled; layer = selection.selectAll('.layer-geolocate') - .data([]); + .data([0]); layer.exit() .remove(); var layerEnter = layer.enter() .append('g') - .attr('class', 'layer-geolocation') + .attr('class', 'layer-geolocate') .style('display', enabled ? 'block' : 'none'); layerEnter .append('g') .attr('class', 'geolocations'); - layerEnter - .append('g') - .attr('class', 'radius'); - layer = layerEnter .merge(layer); if (enabled) { - layerOn(); update(); } else { layerOff(); } } - drawLocation.enabled = function(_) { + drawLocation.enabled = function (position, enabled) { if (!arguments.length) return svgGeolocate.enabled; - _position = _; - svgGeolocate.enabled = true; + _position = position; + svgGeolocate.enabled = enabled; if (svgGeolocate.enabled) { showLayer(); - update(); + layerOn(); } else { hideLayer(); } - dispatch.call('change'); + // dispatch.call('change'); return this; }; - - init(); return drawLocation; } diff --git a/modules/svg/index.js b/modules/svg/index.js index 310acc0587..b444bd7a05 100644 --- a/modules/svg/index.js +++ b/modules/svg/index.js @@ -3,6 +3,7 @@ export { svgData } from './data.js'; export { svgDebug } from './debug.js'; export { svgDefs } from './defs.js'; export { svgIcon } from './icon.js'; +export { svgGeolocate } from './geolocate'; export { svgLabels } from './labels.js'; export { svgLayers } from './layers.js'; export { svgLines } from './lines.js'; diff --git a/modules/ui/geolocate.js b/modules/ui/geolocate.js index fe1a0bcd01..651196d056 100644 --- a/modules/ui/geolocate.js +++ b/modules/ui/geolocate.js @@ -9,32 +9,43 @@ import { uiLoading } from './loading'; export function uiGeolocate(context) { var geoOptions = { enableHighAccuracy: false, timeout: 6000 /* 6sec */ }, locating = uiLoading(context).message(t('geolocate.locating')).blocking(true), + layer = context.layers().layer('geolocate'), + position, + extent, timeoutId; function click() { if (context.inIntro()) return; context.enter(modeBrowse(context)); - context.container().call(locating); - navigator.geolocation.getCurrentPosition(success, error, geoOptions); - + if (!layer.enabled()) { + if (!position) { + context.container().call(locating); + navigator.geolocation.getCurrentPosition(success, error, geoOptions); + } else { + zoomTo(); + } + } else { + layer.enabled(null, false); + } // This timeout ensures that we still call finish() even if // the user declines to share their location in Firefox timeoutId = setTimeout(finish, 10000 /* 10sec */ ); } + function zoomTo() { + var map = context.map(); + layer.enabled(position, true); + map.centerZoom(extent.center(), Math.min(20, map.extentZoom(extent))); + } - function success(position) { - var location = { loc: [position.coords.longitude, position.coords.latitude] }, - layer = context.layers().layer('geolocate'); - - layer.enabled(location); - var map = context.map(), - extent = geoExtent([position.coords.longitude, position.coords.latitude]) - .padByMeters(position.coords.accuracy); + function success(geolocation) { + position = geolocation; + extent = geoExtent([position.coords.longitude, position.coords.latitude]) + .padByMeters(position.coords.accuracy); - map.centerZoom(extent.center(), Math.min(20, map.extentZoom(extent))); + zoomTo(); finish(); }