mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-15 21:48:20 +02:00
Add Osmose QA layer and service
Initial implementation - need to add UI for the errors and correctly set up support for the desired error types provided by osmose.
This commit is contained in:
+8
-2
@@ -3,7 +3,8 @@
|
||||
|
||||
.error-header-icon .qa_error-fill,
|
||||
.layer-keepRight .qa_error .qa_error-fill,
|
||||
.layer-improveOSM .qa_error .qa_error-fill {
|
||||
.layer-improveOSM .qa_error .qa_error-fill,
|
||||
.layer-osmose .qa_error .qa_error-fill {
|
||||
stroke: #333;
|
||||
stroke-width: 1.3px; /* NOTE: likely a better way to scale the icon stroke */
|
||||
}
|
||||
@@ -152,6 +153,11 @@
|
||||
color: #EC1C24;
|
||||
}
|
||||
|
||||
/* Osmose Errors
|
||||
------------------------------------------------------- */
|
||||
.osmose {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
/* Custom Map Data (geojson, gpx, kml, vector tile) */
|
||||
.layer-mapdata {
|
||||
@@ -211,4 +217,4 @@
|
||||
stroke: #000;
|
||||
stroke-width: 5px;
|
||||
stroke-miterlimit: 1;
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -630,6 +630,9 @@ en:
|
||||
improveOSM:
|
||||
tooltip: Missing data automatically detected by improveosm.org
|
||||
title: ImproveOSM Issues
|
||||
osmose:
|
||||
tooltip: Automatically detected map issues from osmose.openstreetmap.fr
|
||||
title: Osmose Issues
|
||||
custom:
|
||||
tooltip: "Drag and drop a data file onto the page, or click the button to setup"
|
||||
title: Custom Map Data
|
||||
@@ -1343,7 +1346,7 @@ en:
|
||||
title: Quality Assurance
|
||||
intro: "*Quality Assurance* (Q/A) tools can find improper tags, disconnected roads, and other issues with OpenStreetMap, which mappers can then fix. To view existing Q/A issues, click the {data} **Map data** panel to enable a specific Q/A layer."
|
||||
tools_h: "Tools"
|
||||
tools: "The following tools are currently supported: [KeepRight](https://www.keepright.at/) and [ImproveOSM](https://improveosm.org/en/). Expect iD to support [Osmose](https://osmose.openstreetmap.fr/) and more Q/A tools in the future."
|
||||
tools: "The following tools are currently supported: [KeepRight](https://www.keepright.at/), [ImproveOSM](https://improveosm.org/en/) and [Osmose](https://osmose.openstreetmap.fr/). Expect iD to support more Q/A tools in the future."
|
||||
issues_h: "Handling Issues"
|
||||
issues: "Handling Q/A issues is similar to handling notes. Click on a marker to view the issue details in the sidebar. Each tool has its own capabilities, but generally you can comment and/or close an issue."
|
||||
field:
|
||||
@@ -2058,4 +2061,4 @@ en:
|
||||
wikidata:
|
||||
identifier: "Identifier"
|
||||
label: "Label"
|
||||
description: "Description"
|
||||
description: "Description"
|
||||
Vendored
+5
-1
@@ -782,6 +782,10 @@
|
||||
"tooltip": "Missing data automatically detected by improveosm.org",
|
||||
"title": "ImproveOSM Issues"
|
||||
},
|
||||
"osmose": {
|
||||
"tooltip": "Automatically detected map issues from osmose.openstreetmap.fr",
|
||||
"title": "Osmose Issues"
|
||||
},
|
||||
"custom": {
|
||||
"tooltip": "Drag and drop a data file onto the page, or click the button to setup",
|
||||
"title": "Custom Map Data",
|
||||
@@ -1653,7 +1657,7 @@
|
||||
"title": "Quality Assurance",
|
||||
"intro": "*Quality Assurance* (Q/A) tools can find improper tags, disconnected roads, and other issues with OpenStreetMap, which mappers can then fix. To view existing Q/A issues, click the {data} **Map data** panel to enable a specific Q/A layer.",
|
||||
"tools_h": "Tools",
|
||||
"tools": "The following tools are currently supported: [KeepRight](https://www.keepright.at/) and [ImproveOSM](https://improveosm.org/en/). Expect iD to support [Osmose](https://osmose.openstreetmap.fr/) and more Q/A tools in the future.",
|
||||
"tools": "The following tools are currently supported: [KeepRight](https://www.keepright.at/), [ImproveOSM](https://improveosm.org/en/) and [Osmose](https://osmose.openstreetmap.fr/). Expect iD to support more Q/A tools in the future.",
|
||||
"issues_h": "Handling Issues",
|
||||
"issues": "Handling Q/A issues is similar to handling notes. Click on a marker to view the issue details in the sidebar. Each tool has its own capabilities, but generally you can comment and/or close an issue."
|
||||
},
|
||||
|
||||
@@ -15,6 +15,7 @@ import { modeDragNode } from './drag_node';
|
||||
import { modeDragNote } from './drag_note';
|
||||
import { uiImproveOsmEditor } from '../ui/improveOSM_editor';
|
||||
import { uiKeepRightEditor } from '../ui/keepRight_editor';
|
||||
import { uiOsmoseEditor } from '../ui/osmose_editor';
|
||||
import { utilKeybinding } from '../util';
|
||||
|
||||
|
||||
@@ -49,6 +50,16 @@ export function modeSelectError(context, selectedErrorID, selectedErrorService)
|
||||
.show(errorEditor.error(error));
|
||||
});
|
||||
break;
|
||||
case 'osmose':
|
||||
errorEditor = uiOsmoseEditor(context)
|
||||
.on('change', function() {
|
||||
context.map().pan([0,0]); // trigger a redraw
|
||||
var error = checkSelectedID();
|
||||
if (!error) return;
|
||||
context.ui().sidebar
|
||||
.show(errorEditor.error(error));
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -154,4 +165,4 @@ export function modeSelectError(context, selectedErrorID, selectedErrorService)
|
||||
|
||||
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import serviceKeepRight from './keepRight';
|
||||
import serviceImproveOSM from './improveOSM';
|
||||
import serviceOsmose from './osmose';
|
||||
import serviceMapillary from './mapillary';
|
||||
import serviceMapRules from './maprules';
|
||||
import serviceNominatim from './nominatim';
|
||||
@@ -17,6 +18,7 @@ export var services = {
|
||||
geocoder: serviceNominatim,
|
||||
keepRight: serviceKeepRight,
|
||||
improveOSM: serviceImproveOSM,
|
||||
osmose: serviceOsmose,
|
||||
mapillary: serviceMapillary,
|
||||
openstreetcam: serviceOpenstreetcam,
|
||||
osm: serviceOsm,
|
||||
@@ -32,6 +34,7 @@ export var services = {
|
||||
export {
|
||||
serviceKeepRight,
|
||||
serviceImproveOSM,
|
||||
serviceOsmose,
|
||||
serviceMapillary,
|
||||
serviceMapRules,
|
||||
serviceNominatim,
|
||||
@@ -43,4 +46,4 @@ export {
|
||||
serviceVectorTile,
|
||||
serviceWikidata,
|
||||
serviceWikipedia
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,238 @@
|
||||
import RBush from 'rbush';
|
||||
|
||||
import { dispatch as d3_dispatch } from 'd3-dispatch';
|
||||
import { json as d3_json } from 'd3-fetch';
|
||||
|
||||
import { geoExtent, geoVecAdd, geoVecScale } from '../geo';
|
||||
import { qaError } from '../osm';
|
||||
import { t } from '../util/locale';
|
||||
import { utilRebind, utilTiler, utilQsString } from '../util';
|
||||
|
||||
|
||||
var tiler = utilTiler();
|
||||
var dispatch = d3_dispatch('loaded');
|
||||
|
||||
var _erCache;
|
||||
var _erZoom = 14;
|
||||
|
||||
var _osmoseUrlRoot = 'https://osmose.openstreetmap.fr/en/api/0.3beta/';
|
||||
|
||||
function abortRequest(controller) {
|
||||
if (controller) {
|
||||
controller.abort();
|
||||
}
|
||||
}
|
||||
|
||||
function abortUnwantedRequests(cache, tiles) {
|
||||
Object.keys(cache.inflightTile).forEach(function(k) {
|
||||
var wanted = tiles.find(function(tile) { return k === tile.id; });
|
||||
if (!wanted) {
|
||||
abortRequest(cache.inflightTile[k]);
|
||||
delete cache.inflightTile[k];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function encodeErrorRtree(d) {
|
||||
return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d };
|
||||
}
|
||||
|
||||
|
||||
// replace or remove error from rtree
|
||||
function updateRtree(item, replace) {
|
||||
_erCache.rtree.remove(item, function isEql(a, b) {
|
||||
return a.data.id === b.data.id;
|
||||
});
|
||||
|
||||
if (replace) {
|
||||
_erCache.rtree.insert(item);
|
||||
}
|
||||
}
|
||||
|
||||
function linkErrorObject(d) {
|
||||
return '<a class="error_object_link">' + d + '</a>';
|
||||
}
|
||||
|
||||
function linkEntity(d) {
|
||||
return '<a class="error_entity_link">' + d + '</a>';
|
||||
}
|
||||
|
||||
// Errors shouldn't obscure eachother
|
||||
function preventCoincident(loc, bumpUp) {
|
||||
var coincident = false;
|
||||
do {
|
||||
// first time, move marker up. after that, move marker right.
|
||||
var delta = coincident ? [0.00001, 0] : (bumpUp ? [0, 0.00001] : [0, 0]);
|
||||
loc = geoVecAdd(loc, delta);
|
||||
var bbox = geoExtent(loc).bbox();
|
||||
coincident = _erCache.rtree.search(bbox).length;
|
||||
} while (coincident);
|
||||
|
||||
return loc;
|
||||
}
|
||||
|
||||
export default {
|
||||
init: function() {
|
||||
if (!_erCache) {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
this.event = utilRebind(this, dispatch, 'on');
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
if (_erCache) {
|
||||
Object.values(_erCache.inflightTile).forEach(abortRequest);
|
||||
}
|
||||
_erCache = {
|
||||
data: {},
|
||||
loadedTile: {},
|
||||
inflightTile: {},
|
||||
inflightPost: {},
|
||||
closed: {},
|
||||
rtree: new RBush()
|
||||
};
|
||||
},
|
||||
|
||||
loadErrors: function(projection) {
|
||||
var options = {
|
||||
full: 'true', // Returns element IDs
|
||||
level: '1,2,3',
|
||||
zoom: '19'
|
||||
};
|
||||
|
||||
// determine the needed tiles to cover the view
|
||||
var tiles = tiler
|
||||
.zoomExtent([_erZoom, _erZoom])
|
||||
.getTiles(projection);
|
||||
|
||||
// abort inflight requests that are no longer needed
|
||||
abortUnwantedRequests(_erCache, tiles);
|
||||
|
||||
// issue new requests..
|
||||
tiles.forEach(function(tile) {
|
||||
if (_erCache.loadedTile[tile.id] || _erCache.inflightTile[tile.id]) return;
|
||||
|
||||
var rect = tile.extent.rectangle(); // E, N, W, S
|
||||
var params = Object.assign({}, options, { bbox: [rect[0], rect[1], rect[2], rect[3]].join() });
|
||||
|
||||
var url = _osmoseUrlRoot + 'issues?' + utilQsString(params);
|
||||
|
||||
var controller = new AbortController();
|
||||
_erCache.inflightTile[tile.id] = controller;
|
||||
|
||||
d3_json(url, { signal: controller.signal })
|
||||
.then(function(data) {
|
||||
delete _erCache.inflightTile[tile.id];
|
||||
_erCache.loadedTile[tile.id] = true;
|
||||
|
||||
if (data.issues) {
|
||||
data.issues.forEach(function(issue) {
|
||||
// Elements provided as string, separated by _ character
|
||||
var elems = issue.elems.split('_');
|
||||
var loc = [issue.lon, issue.lat];
|
||||
|
||||
loc = preventCoincident(loc, true);
|
||||
|
||||
var d = new qaError({
|
||||
// Info required for every error
|
||||
loc: loc,
|
||||
service: 'osmose',
|
||||
error_type: [issue.item, issue.classs].join('-'),
|
||||
// Extra details needed for this service
|
||||
identifier: issue.id, // this is used to post changes to the error
|
||||
elems: elems
|
||||
//object_id: elems[0],
|
||||
//object_type: elems[0].substring(0,1)
|
||||
});
|
||||
|
||||
// Variables used in the description
|
||||
d.replacements = {
|
||||
};
|
||||
|
||||
_erCache.data[d.id] = d;
|
||||
_erCache.rtree.insert(encodeErrorRtree(d));
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(function() {
|
||||
delete _erCache.inflightTile[tile.id];
|
||||
_erCache.loadedTile[tile.id] = true;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
postUpdate: function(d, callback) {
|
||||
if (_erCache.inflightPost[d.id]) {
|
||||
return callback({ message: 'Error update already inflight', status: -2 }, d);
|
||||
}
|
||||
|
||||
var that = this;
|
||||
|
||||
if (err) { return callback(err, d); }
|
||||
|
||||
// UI sets the status to either '/done' or '/false'
|
||||
var url = _osmoseUrlRoot + 'issue/' + d.identifier + d.newStatus;
|
||||
|
||||
var controller = new AbortController();
|
||||
_erCache.inflightPost[d.id] = controller;
|
||||
|
||||
fetch(url, { method: 'POST', signal: controller.signal })
|
||||
.then(function() {
|
||||
delete _erCache.inflightPost[d.id];
|
||||
|
||||
that.removeError(d);
|
||||
if (d.newStatus === '/done') {
|
||||
// No pretty identifier, so we just use coordinates
|
||||
var closedID = d.loc[1].toFixed(5) + '/' + d.loc[0].toFixed(5);
|
||||
_erCache.closed[key + ':' + closedID] = true;
|
||||
}
|
||||
if (callback) callback(null, d);
|
||||
})
|
||||
.catch(function(err) {
|
||||
delete _erCache.inflightPost[d.id];
|
||||
if (callback) callback(err.message);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
// get all cached errors covering the viewport
|
||||
getErrors: function(projection) {
|
||||
var viewport = projection.clipExtent();
|
||||
var min = [viewport[0][0], viewport[1][1]];
|
||||
var max = [viewport[1][0], viewport[0][1]];
|
||||
var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
|
||||
|
||||
return _erCache.rtree.search(bbox).map(function(d) {
|
||||
return d.data;
|
||||
});
|
||||
},
|
||||
|
||||
// get a single error from the cache
|
||||
getError: function(id) {
|
||||
return _erCache.data[id];
|
||||
},
|
||||
|
||||
// replace a single error in the cache
|
||||
replaceError: function(error) {
|
||||
if (!(error instanceof qaError) || !error.id) return;
|
||||
|
||||
_erCache.data[error.id] = error;
|
||||
updateRtree(encodeErrorRtree(error), true); // true = replace
|
||||
return error;
|
||||
},
|
||||
|
||||
// remove a single error from the cache
|
||||
removeError: function(error) {
|
||||
if (!(error instanceof qaError) || !error.id) return;
|
||||
|
||||
delete _erCache.data[error.id];
|
||||
updateRtree(encodeErrorRtree(error), false); // false = remove
|
||||
},
|
||||
|
||||
// Used to populate `closed:osmose` changeset tag
|
||||
getClosedIDs: function() {
|
||||
return Object.keys(_erCache.closed).sort();
|
||||
}
|
||||
};
|
||||
@@ -6,6 +6,7 @@ import { svgDebug } from './debug';
|
||||
import { svgGeolocate } from './geolocate';
|
||||
import { svgKeepRight } from './keepRight';
|
||||
import { svgImproveOSM } from './improveOSM';
|
||||
import { svgOsmose } from './osmose';
|
||||
import { svgStreetside } from './streetside';
|
||||
import { svgMapillaryImages } from './mapillary_images';
|
||||
import { svgMapillarySigns } from './mapillary_signs';
|
||||
@@ -27,6 +28,7 @@ export function svgLayers(projection, context) {
|
||||
{ id: 'data', layer: svgData(projection, context, dispatch) },
|
||||
{ id: 'keepRight', layer: svgKeepRight(projection, context, dispatch) },
|
||||
{ id: 'improveOSM', layer: svgImproveOSM(projection, context, dispatch) },
|
||||
{ id: 'osmose', layer: svgOsmose(projection, context, dispatch) },
|
||||
{ id: 'streetside', layer: svgStreetside(projection, context, dispatch)},
|
||||
{ id: 'mapillary', layer: svgMapillaryImages(projection, context, dispatch) },
|
||||
{ id: 'mapillary-map-features', layer: svgMapillaryMapFeatures(projection, context, dispatch) },
|
||||
@@ -116,4 +118,4 @@ export function svgLayers(projection, context) {
|
||||
|
||||
|
||||
return utilRebind(drawLayers, dispatch, 'on');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
import _throttle from 'lodash-es/throttle';
|
||||
import { select as d3_select } from 'd3-selection';
|
||||
|
||||
import { modeBrowse } from '../modes/browse';
|
||||
import { svgPointTransform } from './helpers';
|
||||
import { services } from '../services';
|
||||
|
||||
var _osmoseEnabled = false;
|
||||
var _errorService;
|
||||
|
||||
|
||||
export function svgOsmose(projection, context, dispatch) {
|
||||
var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000);
|
||||
var minZoom = 12;
|
||||
var touchLayer = d3_select(null);
|
||||
var drawLayer = d3_select(null);
|
||||
var _osmoseVisible = false;
|
||||
|
||||
function markerPath(selection, klass) {
|
||||
selection
|
||||
.attr('class', klass)
|
||||
.attr('transform', 'translate(-10, -28)')
|
||||
.attr('points', '16,3 4,3 1,6 1,17 4,20 7,20 10,27 13,20 16,20 19,17.033 19,6');
|
||||
}
|
||||
|
||||
|
||||
// Loosely-coupled osmose service for fetching errors.
|
||||
function getService() {
|
||||
if (services.osmose && !_errorService) {
|
||||
_errorService = services.osmose;
|
||||
_errorService.on('loaded', throttledRedraw);
|
||||
} else if (!services.osmose && _errorService) {
|
||||
_errorService = null;
|
||||
}
|
||||
|
||||
return _errorService;
|
||||
}
|
||||
|
||||
|
||||
// Show the errors
|
||||
function editOn() {
|
||||
if (!_osmoseVisible) {
|
||||
_osmoseVisible = true;
|
||||
drawLayer
|
||||
.style('display', 'block');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Immediately remove the errors and their touch targets
|
||||
function editOff() {
|
||||
if (_osmoseVisible) {
|
||||
_osmoseVisible = false;
|
||||
drawLayer
|
||||
.style('display', 'none');
|
||||
drawLayer.selectAll('.qa_error.osmose')
|
||||
.remove();
|
||||
touchLayer.selectAll('.qa_error.osmose')
|
||||
.remove();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Enable the layer. This shows the errors and transitions them to visible.
|
||||
function layerOn() {
|
||||
editOn();
|
||||
|
||||
drawLayer
|
||||
.style('opacity', 0)
|
||||
.transition()
|
||||
.duration(250)
|
||||
.style('opacity', 1)
|
||||
.on('end interrupt', function () {
|
||||
dispatch.call('change');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Disable the layer. This transitions the layer invisible and then hides the errors.
|
||||
function layerOff() {
|
||||
throttledRedraw.cancel();
|
||||
drawLayer.interrupt();
|
||||
touchLayer.selectAll('.qa_error.osmose')
|
||||
.remove();
|
||||
|
||||
drawLayer
|
||||
.transition()
|
||||
.duration(250)
|
||||
.style('opacity', 0)
|
||||
.on('end interrupt', function () {
|
||||
editOff();
|
||||
dispatch.call('change');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Update the error markers
|
||||
function updateMarkers() {
|
||||
if (!_osmoseVisible || !_osmoseEnabled) return;
|
||||
|
||||
var service = getService();
|
||||
var selectedID = context.selectedErrorID();
|
||||
var data = (service ? service.getErrors(projection) : []);
|
||||
var getTransform = svgPointTransform(projection);
|
||||
|
||||
// Draw markers..
|
||||
var markers = drawLayer.selectAll('.qa_error.osmose')
|
||||
.data(data, function(d) { return d.id; });
|
||||
|
||||
// exit
|
||||
markers.exit()
|
||||
.remove();
|
||||
|
||||
// enter
|
||||
var markersEnter = markers.enter()
|
||||
.append('g')
|
||||
.attr('class', function(d) {
|
||||
return [
|
||||
'qa_error',
|
||||
d.service,
|
||||
'error_id-' + d.id,
|
||||
'error_type-' + d.error_type,
|
||||
'category-' + d.category
|
||||
].join(' ');
|
||||
});
|
||||
|
||||
markersEnter
|
||||
.append('polygon')
|
||||
.call(markerPath, 'shadow');
|
||||
|
||||
markersEnter
|
||||
.append('ellipse')
|
||||
.attr('cx', 0)
|
||||
.attr('cy', 0)
|
||||
.attr('rx', 4.5)
|
||||
.attr('ry', 2)
|
||||
.attr('class', 'stroke');
|
||||
|
||||
markersEnter
|
||||
.append('polygon')
|
||||
.attr('fill', 'currentColor')
|
||||
.call(markerPath, 'qa_error-fill');
|
||||
|
||||
markersEnter
|
||||
.append('use')
|
||||
.attr('transform', 'translate(-5.5, -21)')
|
||||
.attr('class', 'icon-annotation')
|
||||
.attr('width', '11px')
|
||||
.attr('height', '11px')
|
||||
.attr('xlink:href', function(d) {
|
||||
var picon = d.icon;
|
||||
|
||||
if (!picon) {
|
||||
return '';
|
||||
} else {
|
||||
var isMaki = /^maki-/.test(picon);
|
||||
return '#' + picon + (isMaki ? '-11' : '');
|
||||
}
|
||||
});
|
||||
|
||||
// update
|
||||
markers
|
||||
.merge(markersEnter)
|
||||
.sort(sortY)
|
||||
.classed('selected', function(d) { return d.id === selectedID; })
|
||||
.attr('transform', getTransform);
|
||||
|
||||
|
||||
// Draw targets..
|
||||
if (touchLayer.empty()) return;
|
||||
var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
|
||||
|
||||
var targets = touchLayer.selectAll('.qa_error.osmose')
|
||||
.data(data, function(d) { return d.id; });
|
||||
|
||||
// exit
|
||||
targets.exit()
|
||||
.remove();
|
||||
|
||||
// enter/update
|
||||
targets.enter()
|
||||
.append('rect')
|
||||
.attr('width', '20px')
|
||||
.attr('height', '30px')
|
||||
.attr('x', '-10px')
|
||||
.attr('y', '-28px')
|
||||
.merge(targets)
|
||||
.sort(sortY)
|
||||
.attr('class', function(d) {
|
||||
return 'qa_error ' + d.service + ' target error_id-' + d.id + ' ' + fillClass;
|
||||
})
|
||||
.attr('transform', getTransform);
|
||||
|
||||
|
||||
function sortY(a, b) {
|
||||
return (a.id === selectedID) ? 1
|
||||
: (b.id === selectedID) ? -1
|
||||
: b.loc[1] - a.loc[1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Draw the Osmose layer and schedule loading errors and updating markers.
|
||||
function drawOsmose(selection) {
|
||||
var service = getService();
|
||||
|
||||
var surface = context.surface();
|
||||
if (surface && !surface.empty()) {
|
||||
touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
|
||||
}
|
||||
|
||||
drawLayer = selection.selectAll('.layer-osmose')
|
||||
.data(service ? [0] : []);
|
||||
|
||||
drawLayer.exit()
|
||||
.remove();
|
||||
|
||||
drawLayer = drawLayer.enter()
|
||||
.append('g')
|
||||
.attr('class', 'layer-osmose')
|
||||
.style('display', _osmoseEnabled ? 'block' : 'none')
|
||||
.merge(drawLayer);
|
||||
|
||||
if (_osmoseEnabled) {
|
||||
if (service && ~~context.map().zoom() >= minZoom) {
|
||||
editOn();
|
||||
service.loadErrors(projection);
|
||||
updateMarkers();
|
||||
} else {
|
||||
editOff();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Toggles the layer on and off
|
||||
drawOsmose.enabled = function(val) {
|
||||
if (!arguments.length) return _osmoseEnabled;
|
||||
|
||||
_osmoseEnabled = val;
|
||||
if (_osmoseEnabled) {
|
||||
layerOn();
|
||||
} else {
|
||||
layerOff();
|
||||
if (context.selectedErrorID()) {
|
||||
context.enter(modeBrowse(context));
|
||||
}
|
||||
}
|
||||
|
||||
dispatch.call('change');
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
drawOsmose.supported = function() {
|
||||
return !!getService();
|
||||
};
|
||||
|
||||
|
||||
return drawOsmose;
|
||||
}
|
||||
@@ -149,6 +149,12 @@ export function uiCommit(context) {
|
||||
tags['closed:improveosm'] = iOsmClosed.join(';').substr(0, tagCharLimit);
|
||||
}
|
||||
}
|
||||
if (services.osmose) {
|
||||
var osmoseClosed = services.osmose.getClosedIDs();
|
||||
if (osmoseClosed.length) {
|
||||
tags['closed:osmose'] = osmoseClosed.join(';').substr(0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
// remove existing issue counts
|
||||
for (var key in tags) {
|
||||
@@ -585,4 +591,4 @@ export function uiCommit(context) {
|
||||
|
||||
|
||||
return utilRebind(commit, dispatch, 'on');
|
||||
}
|
||||
}
|
||||
@@ -341,7 +341,7 @@ export function uiMapData(context) {
|
||||
|
||||
|
||||
function drawQAItems(selection) {
|
||||
var qaKeys = ['keepRight', 'improveOSM'];
|
||||
var qaKeys = ['keepRight', 'improveOSM', 'osmose'];
|
||||
var qaLayers = layers.all().filter(function(obj) { return qaKeys.indexOf(obj.id) !== -1; });
|
||||
|
||||
var ul = selection
|
||||
@@ -916,4 +916,4 @@ export function uiMapData(context) {
|
||||
};
|
||||
|
||||
return uiMapData;
|
||||
}
|
||||
}
|
||||
+11
-10
@@ -26,20 +26,21 @@ describe('iD.svgLayers', function () {
|
||||
it('creates default data layers', function () {
|
||||
container.call(iD.svgLayers(projection, context));
|
||||
var nodes = container.selectAll('svg .data-layer').nodes();
|
||||
expect(nodes.length).to.eql(13);
|
||||
expect(nodes.length).to.eql(14);
|
||||
expect(d3.select(nodes[0]).classed('osm')).to.be.true;
|
||||
expect(d3.select(nodes[1]).classed('notes')).to.be.true;
|
||||
expect(d3.select(nodes[2]).classed('data')).to.be.true;
|
||||
expect(d3.select(nodes[3]).classed('keepRight')).to.be.true;
|
||||
expect(d3.select(nodes[4]).classed('improveOSM')).to.be.true;
|
||||
expect(d3.select(nodes[5]).classed('streetside')).to.be.true;
|
||||
expect(d3.select(nodes[6]).classed('mapillary')).to.be.true;
|
||||
expect(d3.select(nodes[7]).classed('mapillary-map-features')).to.be.true;
|
||||
expect(d3.select(nodes[8]).classed('mapillary-signs')).to.be.true;
|
||||
expect(d3.select(nodes[9]).classed('openstreetcam')).to.be.true;
|
||||
expect(d3.select(nodes[10]).classed('debug')).to.be.true;
|
||||
expect(d3.select(nodes[11]).classed('geolocate')).to.be.true;
|
||||
expect(d3.select(nodes[12]).classed('touch')).to.be.true;
|
||||
expect(d3.select(nodes[5]).classed('osmose')).to.be.true;
|
||||
expect(d3.select(nodes[6]).classed('streetside')).to.be.true;
|
||||
expect(d3.select(nodes[7]).classed('mapillary')).to.be.true;
|
||||
expect(d3.select(nodes[8]).classed('mapillary-map-features')).to.be.true;
|
||||
expect(d3.select(nodes[9]).classed('mapillary-signs')).to.be.true;
|
||||
expect(d3.select(nodes[10]).classed('openstreetcam')).to.be.true;
|
||||
expect(d3.select(nodes[11]).classed('debug')).to.be.true;
|
||||
expect(d3.select(nodes[12]).classed('geolocate')).to.be.true;
|
||||
expect(d3.select(nodes[13]).classed('touch')).to.be.true;
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user