Skip to content

Commit

Permalink
Task Grid (#26)
Browse files Browse the repository at this point in the history
* Grid has been added if a GPX url is supplied- Grid options (2x2, 3x3, etc) can be found in the background controls if and only if the bounding area is a rectangle.
  • Loading branch information
Bonkles authored Oct 8, 2019
1 parent 8c1780a commit 37c6b93
Show file tree
Hide file tree
Showing 10 changed files with 284 additions and 6 deletions.
42 changes: 42 additions & 0 deletions css/20_map_fb.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,46 @@
/*In wireframe mode, restrain the stroke width to something barely wider than normal.*/
.fill-wireframe.highlight-edited g.lines > path.line.graphedited, .fill-wireframe.highlight-edited g.linegroup.line-stroke > path.way.line.stroke.tagedited {
stroke-width: 2 !important;
}

.bbox-svg {
top: 0;
left: 0;
overflow: hidden;
height: 100%;
width: 100%;
position: absolute;
pointer-events: none;
}

.map-bbox {
fill: none;
stroke: rgba(0, 255, 255, 0.70);
stroke-width: 1;
shape-rendering: crispEdges;
z-index: 1;
pointer-events: none;
}

.map-bbox.thick {
stroke-width: 5;
}

.grids-svg {
top: 0;
left: 0;
overflow: hidden;
height: 100%;
width: 100%;
position: absolute;
pointer-events: none;
}

.map-grids {
fill: none;
stroke: rgba(0, 255, 255, 0.40);
stroke-width: 2;
shape-rendering: crispEdges;
z-index: 1;
pointer-events: none;
}
4 changes: 4 additions & 0 deletions data/core.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,10 @@ en:
switch: Switch back to this background
custom: Custom
overlays: Overlays
grid:
grids: Grids
no_grid: No Grid
n_by_n: "{num} by {num} Grid"
imagery_source_faq: Imagery Info / Report a Problem
reset: reset
reset_all: Reset All
Expand Down
63 changes: 59 additions & 4 deletions modules/core/rapid_context.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,89 @@
import { geoExtent } from '../geo';

import toGeoJSON from '@mapbox/togeojson';
import { dispatch as d3_dispatch } from 'd3-dispatch';
import { utilRebind } from '../util';


export function coreRapidContext(context) {
export function coreRapidContext() {
var rapidContext = {};
rapidContext.version = '1.0.1';
var _isTaskBoundsRect = undefined;
var dispatch = d3_dispatch('task_extent_set');

function distinct (value, index, self) {
return self.indexOf(value) === index;
}

var taskExtent;
rapidContext.setTaskExtentByGpxData = function(gpxData) {
var dom = (new DOMParser()).parseFromString(gpxData, 'text/xml');
var gj = toGeoJSON.gpx(dom);

var lineStringCount = gj.features.reduce(function (accumulator, currentValue) {
return accumulator + (currentValue.geometry.type === 'LineString' ? 1 : 0);
}, 0);

if (gj.type === 'FeatureCollection') {
var minlat, minlon, maxlat, maxlon;
// Calculate task extent.
gj.features.forEach(function(f) {
if (f.geometry.type === 'Point') {
var lon = f.geometry.coordinates[0];
var lat = f.geometry.coordinates[1];
if (minlat === undefined || lat < minlat) minlat = lat;
if (minlon === undefined || lon < minlon) minlon = lon;
if (maxlat === undefined || lat > maxlat) maxlat = lat;
if (maxlon === undefined || lon > maxlon) maxlon = lon;
if (maxlon === undefined || lon > maxlon) maxlon = lon;
}

if (f.geometry.type === 'LineString' && lineStringCount === 1) {
var lats = f.geometry.coordinates.map(function(f) {return f[0];});
var lngs = f.geometry.coordinates.map(function(f) {return f[1];});
var uniqueLats = lats.filter(distinct);
var uniqueLngs = lngs.filter(distinct);

var eachLatHas2Lngs = true;
uniqueLats.forEach(function (lat) {
var lngsForThisLat = f.geometry.coordinates
// Filter the coords to the ones with this lat
.filter(function(coord){ return coord[0] === lat; })
// Make an array of lngs that associate with that lat
.map(function(coord){ return coord[1]; })
// Finally, filter for uniqueness
.filter(distinct);

if (lngsForThisLat.length !== 2) {
eachLatHas2Lngs = false;
}
});
// Check for exactly two unique latitudes, two unique longitudes,
//and that each latitude was associated with exactly 2 longitudes,
//
if (uniqueLats.length === 2 && uniqueLngs.length === 2 && eachLatHas2Lngs) {
_isTaskBoundsRect = true;
} else {
_isTaskBoundsRect = false;
}
}
});
taskExtent = new geoExtent([minlon, minlat], [maxlon, maxlat]);
dispatch.call('task_extent_set');
}
};


rapidContext.getTaskExtent = function() {
return taskExtent;
};

return rapidContext;

rapidContext.isTaskRectangular = function() {
if (!taskExtent) {
return false;
}

return _isTaskBoundsRect;
};

return utilRebind(rapidContext, dispatch, 'on');
}
9 changes: 9 additions & 0 deletions modules/geo/extent.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ export function geoExtent(min, max) {
}
}


export function geoExtentFromBounds(mapBounds) {
return geoExtent([
[mapBounds.minlon, mapBounds.minlat],
[mapBounds.maxlon, mapBounds.maxlat]
]);
}


geoExtent.prototype = new Array(2);

Object.assign(geoExtent.prototype, {
Expand Down
1 change: 1 addition & 0 deletions modules/geo/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { geoExtent } from './extent.js';
export { geoExtentFromBounds } from './extent.js';

export { geoLatToMeters } from './geo.js';
export { geoLonToMeters } from './geo.js';
Expand Down
8 changes: 8 additions & 0 deletions modules/renderer/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export function rendererBackground(context) {
var _contrast = 1;
var _saturation = 1;
var _sharpness = 1;
var _numGridSplits = 0; // No grid by default.


function background(selection) {
Expand Down Expand Up @@ -132,6 +133,13 @@ export function rendererBackground(context) {
}


background.numGridSplits = function(_) {
if (!arguments.length) return _numGridSplits;
_numGridSplits = _;
dispatch.call('change');
return background;
};

background.updateImagery = function() {
var b = baseLayer.source();
if (context.inIntro() || !b) return;
Expand Down
57 changes: 57 additions & 0 deletions modules/renderer/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { interpolate as d3_interpolate } from 'd3-interpolate';
import { scaleLinear as d3_scaleLinear } from 'd3-scale';
import { event as d3_event, select as d3_select } from 'd3-selection';
import { zoom as d3_zoom, zoomIdentity as d3_zoomIdentity } from 'd3-zoom';
import { geoPath as d3_geoPath } from 'd3-geo';

import { t } from '../util/locale';
import { geoExtent, geoRawMercator, geoScaleToZoom, geoZoomToScale } from '../geo';
Expand Down Expand Up @@ -539,6 +540,59 @@ export function rendererMap(context) {
}


function drawMapGrid() {
// Add bounding box to imported OSM file layer
var d3Path = d3_geoPath(projection),
mapBoundsExtent = context.rapidContext().getTaskExtent();
var minlat = mapBoundsExtent[0][1],
minlon = mapBoundsExtent[0][0],
maxlat = mapBoundsExtent[1][1],
maxlon = mapBoundsExtent[1][0],
numGridSplits = context.background().numGridSplits();

var gridsSvg = surface.selectAll('.grids-svg')
.data([0]);

// Since there is no z-order within an svg,
// and we want the grid to appear on top of everything else,
// insert(), not append(), it at the start of the data layer.
gridsSvg.enter()
.insert('svg', ':first-child')
.attr('class', 'grids-svg');

gridsSvg.exit()
.remove();

var gridsData = [];

for (var i = 1; i < numGridSplits; i++) {
var midlon = minlon + (maxlon - minlon) * i / numGridSplits,
midlat = minlat + (maxlat - minlat) * i / numGridSplits;
gridsData.push({
type: 'LineString',
coordinates:[[midlon, minlat], [midlon, maxlat]]
});
gridsData.push({
type: 'LineString',
coordinates:[[minlon, midlat], [maxlon, midlat]]
});
}

var gridsPath = gridsSvg.selectAll('.map-grids')
.data(gridsData);

gridsPath.attr('d', d3Path);

gridsPath.enter()
.append('path')
.attr('class', 'map-grids')
.attr('d', d3Path);

gridsPath.exit()
.remove();
}


function redraw(difference, extent) {
if (surface.empty() || !_redrawEnabled) return;

Expand Down Expand Up @@ -566,6 +620,9 @@ export function rendererMap(context) {
surface
.classed('low-zoom', zoom <= lowzoom(lat));

if (context.rapidContext().isTaskRectangular()) {
drawMapGrid();
}

if (!difference) {
supersurface.call(context.background());
Expand Down
4 changes: 4 additions & 0 deletions modules/svg/layers.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ export function svgLayers(projection, context) {
.append('defs')
.attr('class', 'surface-defs');

defs.enter()
.append('svg')
.attr('class', 'grids-svg');

var groups = svg.selectAll('.data-layer')
.data(_layers);

Expand Down
21 changes: 19 additions & 2 deletions modules/ui/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { event as d3_event, select as d3_select } from 'd3-selection';
import { t, textDirection } from '../util/locale';
import { svgIcon } from '../svg/icon';
import { uiBackgroundDisplayOptions } from './background_display_options';
import { uiGridDisplayOptions } from './grid_display_options';
import { uiBackgroundOffset } from './background_offset';
import { uiCmd } from './cmd';
import { uiDisclosure } from './disclosure';
Expand All @@ -14,7 +15,6 @@ import { uiSettingsCustomBackground } from './settings/custom_background';
import { uiTooltipHtml } from './tooltipHtml';
import { tooltip } from '../util/tooltip';


export function uiBackground(context) {
var key = t('background.key');

Expand All @@ -26,8 +26,10 @@ export function uiBackground(context) {
var _backgroundList = d3_select(null);
var _overlayList = d3_select(null);
var _displayOptionsContainer = d3_select(null);
var _gridOptionsContainer = d3_select(null);
var _offsetContainer = d3_select(null);

var gridDisplayOptions = uiGridDisplayOptions(context);
var backgroundDisplayOptions = uiBackgroundDisplayOptions(context);
var backgroundOffset = uiBackgroundOffset(context);

Expand Down Expand Up @@ -92,7 +94,6 @@ export function uiBackground(context) {
document.activeElement.blur();
}


function customChanged(d) {
if (d && d.template) {
_customSource.template(d.template);
Expand Down Expand Up @@ -257,6 +258,7 @@ export function uiBackground(context) {
updateOverlayList();
}


function updateBackgroundList() {
_backgroundList
.call(drawListItems, 'radio', chooseBackground, function(d) { return !d.isHidden() && !d.overlay; });
Expand All @@ -277,6 +279,9 @@ export function uiBackground(context) {
updateOverlayList();
}

_gridOptionsContainer
.call(gridDisplayOptions);

_displayOptionsContainer
.call(backgroundDisplayOptions);

Expand Down Expand Up @@ -363,6 +368,18 @@ export function uiBackground(context) {
.content(renderOverlayList)
);

// grid list
_gridOptionsContainer = content
.append('div')
.attr('class', 'grid-overlay-list-container');

context.rapidContext().on('task_extent_set.background', function() {
// Show grid options only if the task bbox is rectangular
if (!context.rapidContext().isTaskRectangular()) {
_gridOptionsContainer.remove();
}
});

// display options
_displayOptionsContainer = content
.append('div')
Expand Down
Loading

0 comments on commit 37c6b93

Please sign in to comment.