From b9ee4de5307dafeabf0d96c469722322cd66a2e9 Mon Sep 17 00:00:00 2001 From: Martin Raifer Date: Sun, 11 Aug 2024 16:59:00 +0200 Subject: [PATCH] restore accidental changes from development branch (unrelated to panoramax) --- ACCESSIBILITY.md | 1 - CHANGELOG.md | 1 + css/20_map.css | 3 +- css/65_data.css | 25 -- data/core.yaml | 32 +- data/qa_data.json | 10 - modules/modes/select_error.js | 11 - modules/services/improveOSM.js | 481 ----------------------------- modules/services/index.js | 3 - modules/services/panoramax.js | 8 +- modules/svg/improveOSM.js | 227 -------------- modules/svg/layers.js | 4 +- modules/ui/commit.js | 7 - modules/ui/improveOSM_comments.js | 92 ------ modules/ui/improveOSM_details.js | 125 -------- modules/ui/improveOSM_editor.js | 200 ------------ modules/ui/improveOSM_header.js | 67 ---- modules/ui/index.js | 4 - modules/ui/sections/data_layers.js | 2 +- modules/ui/sidebar.js | 6 +- test/spec/svg/layers.js | 29 +- 21 files changed, 24 insertions(+), 1314 deletions(-) delete mode 100644 modules/services/improveOSM.js delete mode 100644 modules/svg/improveOSM.js delete mode 100644 modules/ui/improveOSM_comments.js delete mode 100644 modules/ui/improveOSM_details.js delete mode 100644 modules/ui/improveOSM_editor.js delete mode 100644 modules/ui/improveOSM_header.js diff --git a/ACCESSIBILITY.md b/ACCESSIBILITY.md index 8fd90adc3f1..82eb48ba489 100644 --- a/ACCESSIBILITY.md +++ b/ACCESSIBILITY.md @@ -193,7 +193,6 @@ translated to one or more languages. | ✅ | OSM community index | | | | ✅ | iD validation issues | | | | ✅ | KeepRight issues | | | -| ✅ | ImproveOSM issues | | | | ✅ | Osmose issues | Translated strings are [provided by Osmose](https://www.transifex.com/openstreetmap-france/osmose/) itself, not iD | | ### Language Coverage diff --git a/CHANGELOG.md b/CHANGELOG.md index a2eb26ab174..efa3e95ebca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,7 @@ _Breaking developer changes, which may affect downstream projects or sites that * Add Panoramax as new street level imagery provider ([#9941], thanks [@mattiapezzotti]) * Fix intermittent issues with Bing Streetside sometimes returning API results in a undocumented format ([#10341]) #### :white_check_mark: Validation +* Drop deprecated validation service _ImproveOSM_ ([#10302], thanks [@arch0345]) #### :bug: Bugfixes * Fix bug which required a second button click when resolving/reopening of OSM notes ([#8994], thanks [@laigyu]) * Fix API URLs for ImproveOSM QA service ([#9993], thanks [@k-yle]) diff --git a/css/20_map.css b/css/20_map.css index 8f2f6b138d3..7856a6e5815 100644 --- a/css/20_map.css +++ b/css/20_map.css @@ -33,8 +33,7 @@ /* No interactivity except what we specifically allow */ .data-layer.osm *, .data-layer.notes *, -.data-layer.keepRight *, -.data-layer.improveOSM * { +.data-layer.keepRight * { pointer-events: none; } diff --git a/css/65_data.css b/css/65_data.css index d2c0db8bf87..f3bf3c46fdf 100644 --- a/css/65_data.css +++ b/css/65_data.css @@ -3,7 +3,6 @@ .qa-header-icon .qaItem-fill, .layer-keepRight .qaItem .qaItem-fill, -.layer-improveOSM .qaItem .qaItem-fill, .layer-osmose .qaItem .qaItem-fill { stroke: #333; stroke-width: 1.3px; /* NOTE: likely a better way to scale the icon stroke */ @@ -127,30 +126,6 @@ color: #c35; } -/* ImproveOSM Issues -------------------------------------------------------- */ - -.improveOSM.itemType-ow { /* missing one way */ - color: #1E90FF; -} - -.improveOSM.itemType-mr-road { /* missing road */ - color: #B452CD; -} -.improveOSM.itemType-mr-path { /* missing path */ - color: #A0522D; -} -.improveOSM.itemType-mr-parking { /* missing parking */ - color: #EEEE00; -} -.improveOSM.itemType-mr-both { /* missing road+parking */ - color: #FFA500; -} - -.improveOSM.itemType-tr { /* missing turn restriction */ - color: #EC1C24; -} - /* Custom Map Data (geojson, gpx, kml, vector tile) */ .layer-mapdata { pointer-events: none; diff --git a/data/core.yaml b/data/core.yaml index 86fa5b27ab4..209d8892223 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -844,9 +844,6 @@ en: keepRight: tooltip: Data issues detected by keepright.at title: KeepRight Issues - improveOSM: - tooltip: Missing data detected by improveosm.org - title: ImproveOSM Issues osmose: tooltip: Data issues detected by osmose.openstreetmap.fr title: Osmose Issues @@ -1101,33 +1098,6 @@ en: elems_title: Features fix_title: Fix Guidelines trap_title: Common Mistakes - improveOSM: - title: ImproveOSM Detection - geometry_types: - path: paths - parking: parking - road: roads - both: roads and parking - directions: - east: east - north: north - northeast: northeast - northwest: northwest - south: south - southeast: southeast - southwest: southwest - west: west - error_types: - ow: - title: Missing One-way - description: 'Along this section of {highway}, {percentage}% of {num_trips} recorded trips travel from {from_node} to {to_node}. There may be missing a "oneway" tag.' - mr: - title: Missing Geometry - description: '{num_trips} recorded trips in this area suggest there may be unmapped {geometry_type} here.' - description_alt: 'Data from a 3rd party suggests there may be unmapped {geometry_type} here.' - tr: - title: Missing Turn Restriction - description: '{num_passed} of {num_trips} recorded trips (travelling {travel_direction}) make a turn from {from_way} to {to_way} at {junction}. There may be a missing "{turn_restriction}" restriction.' keepRight: title: KeepRight detail_description: Description @@ -1696,7 +1666,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, open the {data_icon} **{map_data}** panel and enable a specific Q/A layer." tools_h: "Tools" - tools: "The following tools are currently supported: [KeepRight](https://www.keepright.at/), [ImproveOSM](https://improveosm.org/en/) and [Osmose](https://osmose.openstreetmap.fr/)." + tools: "The following tools are currently supported: [KeepRight](https://www.keepright.at/) and [Osmose](https://osmose.openstreetmap.fr/)." issues_h: "Handling Issues" issues: "Handling Q/A issues is similar to handling notes. Select 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: diff --git a/data/qa_data.json b/data/qa_data.json index 9091132c654..029d6a0dea7 100644 --- a/data/qa_data.json +++ b/data/qa_data.json @@ -1,14 +1,4 @@ { - "improveOSM": { - "icons": { - "ow": "fas-long-arrow-alt-right", - "mr-both": "maki-car", - "mr-parking": "maki-parking", - "mr-path": "maki-shoe", - "mr-road": "maki-car", - "tr": "temaki-junction" - } - }, "osmose": { "icons": { "0-1": "maki-home", diff --git a/modules/modes/select_error.js b/modules/modes/select_error.js index 37212590790..bde94e2f355 100644 --- a/modules/modes/select_error.js +++ b/modules/modes/select_error.js @@ -12,7 +12,6 @@ import { services } from '../services'; import { modeBrowse } from './browse'; 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'; @@ -29,16 +28,6 @@ export function modeSelectError(context, selectedErrorID, selectedErrorService) var errorService = services[selectedErrorService]; var errorEditor; switch (selectedErrorService) { - case 'improveOSM': - errorEditor = uiImproveOsmEditor(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; case 'keepRight': errorEditor = uiKeepRightEditor(context) .on('change', function() { diff --git a/modules/services/improveOSM.js b/modules/services/improveOSM.js deleted file mode 100644 index 903912619b8..00000000000 --- a/modules/services/improveOSM.js +++ /dev/null @@ -1,481 +0,0 @@ -import RBush from 'rbush'; - -import { dispatch as d3_dispatch } from 'd3-dispatch'; -import { json as d3_json } from 'd3-fetch'; - -import { fileFetcher } from '../core/file_fetcher'; -import { geoExtent, geoVecAdd, geoVecScale } from '../geo'; -import { QAItem } from '../osm'; -import { serviceOsm } from './index'; -import { t } from '../core/localizer'; -import { utilRebind, utilTiler, utilQsString } from '../util'; - - -const tiler = utilTiler(); -const dispatch = d3_dispatch('loaded'); -const _tileZoom = 14; -const _impOsmUrls = { - ow: 'https://community.improveosm.org/directionOfFlowService', - mr: 'https://community.improveosm.org/missingGeoService', - tr: 'https://community.improveosm.org/turnRestrictionService' -}; -let _impOsmData = { icons: {} }; - - -// This gets reassigned if reset -let _cache; - -function abortRequest(i) { - Object.values(i).forEach(controller => { - if (controller) { - controller.abort(); - } - }); -} - -function abortUnwantedRequests(cache, tiles) { - Object.keys(cache.inflightTile).forEach(k => { - const wanted = tiles.find(tile => k === tile.id); - if (!wanted) { - abortRequest(cache.inflightTile[k]); - delete cache.inflightTile[k]; - } - }); -} - -function encodeIssueRtree(d) { - return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d }; -} - -// Replace or remove QAItem from rtree -function updateRtree(item, replace) { - _cache.rtree.remove(item, (a, b) => a.data.id === b.data.id); - - if (replace) { - _cache.rtree.insert(item); - } -} - -function linkErrorObject(d) { - return { html: `${d}` }; -} - -function linkEntity(d) { - return { html: `${d}` }; -} - -function pointAverage(points) { - if (points.length) { - const sum = points.reduce( - (acc, point) => geoVecAdd(acc, [point.lon, point.lat]), - [0,0] - ); - return geoVecScale(sum, 1 / points.length); - } else { - return [0,0]; - } -} - -function relativeBearing(p1, p2) { - let angle = Math.atan2(p2.lon - p1.lon, p2.lat - p1.lat); - if (angle < 0) { - angle += 2 * Math.PI; - } - - // Return degrees - return angle * 180 / Math.PI; -} - -// Assuming range [0,360) -function cardinalDirection(bearing) { - const dir = 45 * Math.round(bearing / 45); - const compass = { - 0: 'north', - 45: 'northeast', - 90: 'east', - 135: 'southeast', - 180: 'south', - 225: 'southwest', - 270: 'west', - 315: 'northwest', - 360: 'north' - }; - - return t(`QA.improveOSM.directions.${compass[dir]}`); -} - -// Errors shouldn't obscure each other -function preventCoincident(loc, bumpUp) { - let coincident = false; - do { - // first time, move marker up. after that, move marker right. - let delta = coincident ? [0.00001, 0] : - bumpUp ? [0, 0.00001] : - [0, 0]; - loc = geoVecAdd(loc, delta); - let bbox = geoExtent(loc).bbox(); - coincident = _cache.rtree.search(bbox).length; - } while (coincident); - - return loc; -} - -export default { - title: 'improveOSM', - - init() { - fileFetcher.get('qa_data') - .then(d => _impOsmData = d.improveOSM); - - if (!_cache) { - this.reset(); - } - - this.event = utilRebind(this, dispatch, 'on'); - }, - - reset() { - if (_cache) { - Object.values(_cache.inflightTile).forEach(abortRequest); - } - _cache = { - data: {}, - loadedTile: {}, - inflightTile: {}, - inflightPost: {}, - closed: {}, - rtree: new RBush() - }; - }, - - loadIssues(projection) { - const options = { - client: 'iD', - status: 'OPEN', - zoom: '19' // Use a high zoom so that clusters aren't returned - }; - - // determine the needed tiles to cover the view - const tiles = tiler - .zoomExtent([_tileZoom, _tileZoom]) - .getTiles(projection); - - // abort inflight requests that are no longer needed - abortUnwantedRequests(_cache, tiles); - - // issue new requests.. - tiles.forEach(tile => { - if (_cache.loadedTile[tile.id] || _cache.inflightTile[tile.id]) return; - - const [ east, north, west, south ] = tile.extent.rectangle(); - const params = Object.assign({}, options, { east, south, west, north }); - - // 3 separate requests to store for each tile - const requests = {}; - - Object.keys(_impOsmUrls).forEach(k => { - // We exclude WATER from missing geometry as it doesn't seem useful - // We use most confident one-way and turn restrictions only, still have false positives - const kParams = Object.assign({}, - params, - (k === 'mr') ? { type: 'PARKING,ROAD,BOTH,PATH' } : { confidenceLevel: 'C1' } - ); - const url = `${_impOsmUrls[k]}/search?` + utilQsString(kParams); - const controller = new AbortController(); - - requests[k] = controller; - - d3_json(url, { signal: controller.signal }) - .then(data => { - delete _cache.inflightTile[tile.id][k]; - if (!Object.keys(_cache.inflightTile[tile.id]).length) { - delete _cache.inflightTile[tile.id]; - _cache.loadedTile[tile.id] = true; - } - - // Road segments at high zoom == oneways - if (data.roadSegments) { - data.roadSegments.forEach(feature => { - // Position error at the approximate middle of the segment - const { points, wayId, fromNodeId, toNodeId } = feature; - const itemId = `${wayId}${fromNodeId}${toNodeId}`; - let mid = points.length / 2; - let loc; - - // Even number of points, find midpoint of the middle two - // Odd number of points, use position of very middle point - if (mid % 1 === 0) { - loc = pointAverage([points[mid - 1], points[mid]]); - } else { - mid = points[Math.floor(mid)]; - loc = [mid.lon, mid.lat]; - } - - // One-ways can land on same segment in opposite direction - loc = preventCoincident(loc, false); - - let d = new QAItem(loc, this, k, itemId, { - issueKey: k, // used as a category - identifier: { // used to post changes - wayId, - fromNodeId, - toNodeId - }, - objectId: wayId, - objectType: 'way' - }); - - // Variables used in the description - d.replacements = { - percentage: feature.percentOfTrips, - num_trips: feature.numberOfTrips, - highway: linkErrorObject(t('QA.keepRight.error_parts.highway')), - from_node: linkEntity('n' + feature.fromNodeId), - to_node: linkEntity('n' + feature.toNodeId) - }; - - _cache.data[d.id] = d; - _cache.rtree.insert(encodeIssueRtree(d)); - }); - } - - // Tiles at high zoom == missing roads - if (data.tiles) { - data.tiles.forEach(feature => { - const { type, x, y, numberOfTrips } = feature; - const geoType = type.toLowerCase(); - const itemId = `${geoType}${x}${y}${numberOfTrips}`; - - // Average of recorded points should land on the missing geometry - // Missing geometry could happen to land on another error - let loc = pointAverage(feature.points); - loc = preventCoincident(loc, false); - - let d = new QAItem(loc, this, `${k}-${geoType}`, itemId, { - issueKey: k, - identifier: { x, y } - }); - - d.replacements = { - num_trips: numberOfTrips, - geometry_type: t(`QA.improveOSM.geometry_types.${geoType}`) - }; - - // -1 trips indicates data came from a 3rd party - if (numberOfTrips === -1) { - d.desc = t('QA.improveOSM.error_types.mr.description_alt', d.replacements); - } - - _cache.data[d.id] = d; - _cache.rtree.insert(encodeIssueRtree(d)); - }); - } - - // Entities at high zoom == turn restrictions - if (data.entities) { - data.entities.forEach(feature => { - const { point, id, segments, numberOfPasses, turnType } = feature; - const itemId = `${id.replace(/[,:+#]/g, '_')}`; - - // Turn restrictions could be missing at same junction - // We also want to bump the error up so node is accessible - const loc = preventCoincident([point.lon, point.lat], true); - - // Elements are presented in a strange way - const ids = id.split(','); - const from_way = ids[0]; - const via_node = ids[3]; - const to_way = ids[2].split(':')[1]; - - let d = new QAItem(loc, this, k, itemId, { - issueKey: k, - identifier: id, - objectId: via_node, - objectType: 'node' - }); - - // Travel direction along from_way clarifies the turn restriction - const [ p1, p2 ] = segments[0].points; - const dir_of_travel = cardinalDirection(relativeBearing(p1, p2)); - - // Variables used in the description - d.replacements = { - num_passed: numberOfPasses, - num_trips: segments[0].numberOfTrips, - turn_restriction: turnType.toLowerCase(), - from_way: linkEntity('w' + from_way), - to_way: linkEntity('w' + to_way), - travel_direction: dir_of_travel, - junction: linkErrorObject(t('QA.keepRight.error_parts.this_node')) - }; - - _cache.data[d.id] = d; - _cache.rtree.insert(encodeIssueRtree(d)); - dispatch.call('loaded'); - }); - } - }) - .catch(() => { - delete _cache.inflightTile[tile.id][k]; - if (!Object.keys(_cache.inflightTile[tile.id]).length) { - delete _cache.inflightTile[tile.id]; - _cache.loadedTile[tile.id] = true; - } - }); - }); - - _cache.inflightTile[tile.id] = requests; - }); - }, - - getComments(item) { - // If comments already retrieved no need to do so again - if (item.comments) { - return Promise.resolve(item); - } - - const key = item.issueKey; - let qParams = {}; - - if (key === 'ow') { - qParams = item.identifier; - } else if (key === 'mr') { - qParams.tileX = item.identifier.x; - qParams.tileY = item.identifier.y; - } else if (key === 'tr') { - qParams.targetId = item.identifier; - } - - const url = `${_impOsmUrls[key]}/retrieveComments?` + utilQsString(qParams); - const cacheComments = data => { - // Assign directly for immediate use afterwards - // comments are served newest to oldest - item.comments = data.comments ? data.comments.reverse() : []; - this.replaceItem(item); - }; - - return d3_json(url).then(cacheComments).then(() => item); - }, - - postUpdate(d, callback) { - if (!serviceOsm.authenticated()) { // Username required in payload - return callback({ message: 'Not Authenticated', status: -3}, d); - } - if (_cache.inflightPost[d.id]) { - return callback({ message: 'Error update already inflight', status: -2 }, d); - } - - // Payload can only be sent once username is established - serviceOsm.userDetails(sendPayload.bind(this)); - - function sendPayload(err, user) { - if (err) { return callback(err, d); } - - const key = d.issueKey; - const url = `${_impOsmUrls[key]}/comment`; - const payload = { - username: user.display_name, - targetIds: [ d.identifier ] - }; - - if (d.newStatus) { - payload.status = d.newStatus; - payload.text = 'status changed'; - } - - // Comment take place of default text - if (d.newComment) { - payload.text = d.newComment; - } - - const controller = new AbortController(); - _cache.inflightPost[d.id] = controller; - - const options = { - method: 'POST', - signal: controller.signal, - body: JSON.stringify(payload) - }; - - d3_json(url, options) - .then(() => { - delete _cache.inflightPost[d.id]; - - // Just a comment, update error in cache - if (!d.newStatus) { - const now = new Date(); - let comments = d.comments ? d.comments : []; - - comments.push({ - username: payload.username, - text: payload.text, - timestamp: now.getTime() / 1000 - }); - - this.replaceItem(d.update({ - comments: comments, - newComment: undefined - })); - } else { - this.removeItem(d); - if (d.newStatus === 'SOLVED') { - // Keep track of the number of issues closed per type to tag the changeset - if (!(d.issueKey in _cache.closed)) { - _cache.closed[d.issueKey] = 0; - } - _cache.closed[d.issueKey] += 1; - } - } - if (callback) callback(null, d); - }) - .catch(err => { - delete _cache.inflightPost[d.id]; - if (callback) callback(err.message); - }); - } - }, - - - // Get all cached QAItems covering the viewport - getItems(projection) { - const viewport = projection.clipExtent(); - const min = [viewport[0][0], viewport[1][1]]; - const max = [viewport[1][0], viewport[0][1]]; - const bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox(); - - return _cache.rtree.search(bbox).map(d => d.data); - }, - - // Get a QAItem from cache - // NOTE: Don't change method name until UI v3 is merged - getError(id) { - return _cache.data[id]; - }, - - // get the name of the icon to display for this item - getIcon(itemType) { - return _impOsmData.icons[itemType]; - }, - - // Replace a single QAItem in the cache - replaceItem(issue) { - if (!(issue instanceof QAItem) || !issue.id) return; - - _cache.data[issue.id] = issue; - updateRtree(encodeIssueRtree(issue), true); // true = replace - return issue; - }, - - // Remove a single QAItem from the cache - removeItem(issue) { - if (!(issue instanceof QAItem) || !issue.id) return; - - delete _cache.data[issue.id]; - updateRtree(encodeIssueRtree(issue), false); // false = remove - }, - - // Used to populate `closed:improveosm:*` changeset tags - getClosedCounts() { - return _cache.closed; - } -}; diff --git a/modules/services/index.js b/modules/services/index.js index 81fc9036e08..af98a7c3c70 100644 --- a/modules/services/index.js +++ b/modules/services/index.js @@ -1,5 +1,4 @@ import serviceKeepRight from './keepRight'; -import serviceImproveOSM from './improveOSM'; import serviceOsmose from './osmose'; import serviceMapillary from './mapillary'; import serviceMapRules from './maprules'; @@ -21,7 +20,6 @@ import servicePanoramax from './panoramax'; export let services = { geocoder: serviceNominatim, keepRight: serviceKeepRight, - improveOSM: serviceImproveOSM, osmose: serviceOsmose, mapillary: serviceMapillary, nsi: serviceNsi, @@ -41,7 +39,6 @@ export let services = { export { serviceKeepRight, - serviceImproveOSM, serviceOsmose, serviceMapillary, serviceMapRules, diff --git a/modules/services/panoramax.js b/modules/services/panoramax.js index 59ac968a0b6..807566da09d 100644 --- a/modules/services/panoramax.js +++ b/modules/services/panoramax.js @@ -278,10 +278,6 @@ export default { return data.flatMap((d, i) => d.features.filter(f => f.name === usernames[i]).map(f => f.id)); }, - getActiveImage: function(){ - return _activeImage; - }, - // Get visible sequences sequences: function(projection, zoom) { const viewport = projection.clipExtent(); @@ -324,6 +320,10 @@ export default { } }, + getActiveImage: function(){ + return _activeImage; + }, + // Update the currently highlighted sequence and selected bubble. setStyles: function(context, hovered) { const hoveredImageId = hovered && hovered.id; diff --git a/modules/svg/improveOSM.js b/modules/svg/improveOSM.js deleted file mode 100644 index b8cd77dbda6..00000000000 --- a/modules/svg/improveOSM.js +++ /dev/null @@ -1,227 +0,0 @@ -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'; - -let _layerEnabled = false; -let _qaService; - -export function svgImproveOSM(projection, context, dispatch) { - const throttledRedraw = _throttle(() => dispatch.call('change'), 1000); - const minZoom = 12; - - let touchLayer = d3_select(null); - let drawLayer = d3_select(null); - let layerVisible = 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 improveOSM service for fetching issues - function getService() { - if (services.improveOSM && !_qaService) { - _qaService = services.improveOSM; - _qaService.on('loaded', throttledRedraw); - } else if (!services.improveOSM && _qaService) { - _qaService = null; - } - - return _qaService; - } - - // Show the markers - function editOn() { - if (!layerVisible) { - layerVisible = true; - drawLayer - .style('display', 'block'); - } - } - - // Immediately remove the markers and their touch targets - function editOff() { - if (layerVisible) { - layerVisible = false; - drawLayer - .style('display', 'none'); - drawLayer.selectAll('.qaItem.improveOSM') - .remove(); - touchLayer.selectAll('.qaItem.improveOSM') - .remove(); - } - } - - // Enable the layer. This shows the markers and transitions them to visible. - function layerOn() { - editOn(); - - drawLayer - .style('opacity', 0) - .transition() - .duration(250) - .style('opacity', 1) - .on('end interrupt', () => dispatch.call('change')); - } - - // Disable the layer. This transitions the layer invisible and then hides the markers. - function layerOff() { - throttledRedraw.cancel(); - drawLayer.interrupt(); - touchLayer.selectAll('.qaItem.improveOSM') - .remove(); - - drawLayer - .transition() - .duration(250) - .style('opacity', 0) - .on('end interrupt', () => { - editOff(); - dispatch.call('change'); - }); - } - - // Update the issue markers - function updateMarkers() { - if (!layerVisible || !_layerEnabled) return; - - const service = getService(); - const selectedID = context.selectedErrorID(); - const data = (service ? service.getItems(projection) : []); - const getTransform = svgPointTransform(projection); - - // Draw markers.. - const markers = drawLayer.selectAll('.qaItem.improveOSM') - .data(data, d => d.id); - - // exit - markers.exit() - .remove(); - - // enter - const markersEnter = markers.enter() - .append('g') - .attr('class', d => `qaItem ${d.service} itemId-${d.id} itemType-${d.itemType}`); - - 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, 'qaItem-fill'); - - markersEnter - .append('use') - .attr('class', 'icon-annotation') - .attr('transform', 'translate(-6, -22)') - .attr('width', '12px') - .attr('height', '12px') - .attr('xlink:href', d => d.icon ? '#' + d.icon : ''); - - // update - markers - .merge(markersEnter) - .sort(sortY) - .classed('selected', d => d.id === selectedID) - .attr('transform', getTransform); - - - // Draw targets.. - if (touchLayer.empty()) return; - const fillClass = context.getDebug('target') ? 'pink ' : 'nocolor '; - - const targets = touchLayer.selectAll('.qaItem.improveOSM') - .data(data, d => 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', d => `qaItem ${d.service} target ${fillClass} itemId-${d.id}`) - .attr('transform', getTransform); - - function sortY(a, b) { - return (a.id === selectedID) ? 1 - : (b.id === selectedID) ? -1 - : b.loc[1] - a.loc[1]; - } - } - - // Draw the ImproveOSM layer and schedule loading issues and updating markers. - function drawImproveOSM(selection) { - const service = getService(); - - const surface = context.surface(); - if (surface && !surface.empty()) { - touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers'); - } - - drawLayer = selection.selectAll('.layer-improveOSM') - .data(service ? [0] : []); - - drawLayer.exit() - .remove(); - - drawLayer = drawLayer.enter() - .append('g') - .attr('class', 'layer-improveOSM') - .style('display', _layerEnabled ? 'block' : 'none') - .merge(drawLayer); - - if (_layerEnabled) { - if (service && ~~context.map().zoom() >= minZoom) { - editOn(); - service.loadIssues(projection); - updateMarkers(); - } else { - editOff(); - } - } - } - - // Toggles the layer on and off - drawImproveOSM.enabled = function(val) { - if (!arguments.length) return _layerEnabled; - - _layerEnabled = val; - if (_layerEnabled) { - layerOn(); - } else { - layerOff(); - if (context.selectedErrorID()) { - context.enter(modeBrowse(context)); - } - } - - dispatch.call('change'); - return this; - }; - - drawImproveOSM.supported = () => !!getService(); - - return drawImproveOSM; -} diff --git a/modules/svg/layers.js b/modules/svg/layers.js index 4db0de6292c..518bc5ce8bd 100644 --- a/modules/svg/layers.js +++ b/modules/svg/layers.js @@ -6,7 +6,6 @@ import { svgLocalPhotos} from './local_photos'; 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 { svgVegbilder} from './vegbilder'; @@ -32,7 +31,6 @@ export function svgLayers(projection, context) { { id: 'notes', layer: svgNotes(projection, context, dispatch) }, { 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) }, @@ -41,8 +39,8 @@ export function svgLayers(projection, context) { { id: 'mapillary-signs', layer: svgMapillarySigns(projection, context, dispatch) }, { id: 'kartaview', layer: svgKartaviewImages(projection, context, dispatch) }, { id: 'mapilio', layer: svgMapilioImages(projection, context, dispatch) }, - { id: 'panoramax', layer: svgPanoramaxImages(projection, context, dispatch) }, { id: 'vegbilder', layer: svgVegbilder(projection, context, dispatch) }, + { id: 'panoramax', layer: svgPanoramaxImages(projection, context, dispatch) }, { id: 'local-photos', layer: svgLocalPhotos(projection, context, dispatch) }, { id: 'debug', layer: svgDebug(projection, context, dispatch) }, { id: 'geolocate', layer: svgGeolocate(projection, context, dispatch) }, diff --git a/modules/ui/commit.js b/modules/ui/commit.js index 326b8202a69..59cbdaf8132 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -27,7 +27,6 @@ var readOnlyTags = [ /^resolved:/, /^closed:note$/, /^closed:keepright$/, - /^closed:improveosm:/, /^closed:osmose:/ ]; @@ -155,12 +154,6 @@ export function uiCommit(context) { tags['closed:keepright'] = context.cleanTagValue(krClosed.join(';')); } } - if (services.improveOSM) { - var iOsmClosed = services.improveOSM.getClosedCounts(); - for (itemType in iOsmClosed) { - tags['closed:improveosm:' + itemType] = context.cleanTagValue(iOsmClosed[itemType].toString()); - } - } if (services.osmose) { var osmoseClosed = services.osmose.getClosedCounts(); for (itemType in osmoseClosed) { diff --git a/modules/ui/improveOSM_comments.js b/modules/ui/improveOSM_comments.js deleted file mode 100644 index 92bdefc9119..00000000000 --- a/modules/ui/improveOSM_comments.js +++ /dev/null @@ -1,92 +0,0 @@ -import { select as d3_select } from 'd3-selection'; - -import { t, localizer } from '../core/localizer'; -import { svgIcon } from '../svg/icon'; -import { services } from '../services'; - -export function uiImproveOsmComments() { - let _qaItem; - - function issueComments(selection) { - // make the div immediately so it appears above the buttons - let comments = selection.selectAll('.comments-container') - .data([0]); - - comments = comments.enter() - .append('div') - .attr('class', 'comments-container') - .merge(comments); - - // must retrieve comments from API before they can be displayed - services.improveOSM.getComments(_qaItem) - .then(d => { - if (!d.comments) return; // nothing to do here - - const commentEnter = comments.selectAll('.comment') - .data(d.comments) - .enter() - .append('div') - .attr('class', 'comment'); - - commentEnter - .append('div') - .attr('class', 'comment-avatar') - .call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon')); - - const mainEnter = commentEnter - .append('div') - .attr('class', 'comment-main'); - - const metadataEnter = mainEnter - .append('div') - .attr('class', 'comment-metadata'); - - metadataEnter - .append('div') - .attr('class', 'comment-author') - .each(function(d) { - const osm = services.osm; - let selection = d3_select(this); - if (osm && d.username) { - selection = selection - .append('a') - .attr('class', 'comment-author-link') - .attr('href', osm.userURL(d.username)) - .attr('target', '_blank'); - } - selection - .text(d => d.username); - }); - - metadataEnter - .append('div') - .attr('class', 'comment-date') - .html(d => t.html('note.status.commented', { when: localeDateString(d.timestamp) })); - - mainEnter - .append('div') - .attr('class', 'comment-text') - .append('p') - .text(d => d.text); - }) - .catch(err => { - console.log(err); // eslint-disable-line no-console - }); - } - - function localeDateString(s) { - if (!s) return null; - const options = { day: 'numeric', month: 'short', year: 'numeric' }; - const d = new Date(s * 1000); // timestamp is served in seconds, date takes ms - if (isNaN(d.getTime())) return null; - return d.toLocaleDateString(localizer.localeCode(), options); - } - - issueComments.issue = function(val) { - if (!arguments.length) return _qaItem; - _qaItem = val; - return issueComments; - }; - - return issueComments; -} diff --git a/modules/ui/improveOSM_details.js b/modules/ui/improveOSM_details.js deleted file mode 100644 index 9f8c3e5438d..00000000000 --- a/modules/ui/improveOSM_details.js +++ /dev/null @@ -1,125 +0,0 @@ -import { - select as d3_select -} from 'd3-selection'; - -import { presetManager } from '../presets'; -import { modeSelect } from '../modes/select'; -import { t } from '../core/localizer'; -import { utilDisplayName, utilHighlightEntities, utilEntityRoot } from '../util'; - -export function uiImproveOsmDetails(context) { - let _qaItem; - - - function issueDetail(d) { - if (d.desc) return d.desc; - const issueKey = d.issueKey; - d.replacements = d.replacements || {}; - d.replacements.default = { html: t.html('inspector.unknown') }; // special key `default` works as a fallback string - return t.html(`QA.improveOSM.error_types.${issueKey}.description`, d.replacements); - } - - - function improveOsmDetails(selection) { - const details = selection.selectAll('.error-details') - .data( - (_qaItem ? [_qaItem] : []), - d => `${d.id}-${d.status || 0}` - ); - - details.exit() - .remove(); - - const detailsEnter = details.enter() - .append('div') - .attr('class', 'error-details qa-details-container'); - - - // description - const descriptionEnter = detailsEnter - .append('div') - .attr('class', 'qa-details-subsection'); - - descriptionEnter - .append('h4') - .call(t.append('QA.keepRight.detail_description')); - - descriptionEnter - .append('div') - .attr('class', 'qa-details-description-text') - .html(issueDetail); - - // If there are entity links in the error message.. - let relatedEntities = []; - descriptionEnter.selectAll('.error_entity_link, .error_object_link') - .attr('href', '#') - .each(function() { - const link = d3_select(this); - const isObjectLink = link.classed('error_object_link'); - const entityID = isObjectLink ? - (utilEntityRoot(_qaItem.objectType) + _qaItem.objectId) - : this.textContent; - const entity = context.hasEntity(entityID); - - relatedEntities.push(entityID); - - // Add click handler - link - .on('mouseenter', () => { - utilHighlightEntities([entityID], true, context); - }) - .on('mouseleave', () => { - utilHighlightEntities([entityID], false, context); - }) - .on('click', (d3_event) => { - d3_event.preventDefault(); - - utilHighlightEntities([entityID], false, context); - - const osmlayer = context.layers().layer('osm'); - if (!osmlayer.enabled()) { - osmlayer.enabled(true); - } - - context.map().centerZoom(_qaItem.loc, 20); - - if (entity) { - context.enter(modeSelect(context, [entityID])); - } else { - context.loadEntity(entityID, (err, result) => { - if (err) return; - const entity = result.data.find(e => e.id === entityID); - if (entity) context.enter(modeSelect(context, [entityID])); - }); - } - }); - - // Replace with friendly name if possible - // (The entity may not yet be loaded into the graph) - if (entity) { - let name = utilDisplayName(entity); // try to use common name - - if (!name && !isObjectLink) { - const preset = presetManager.match(entity, context.graph()); - name = preset && !preset.isFallback() && preset.name(); // fallback to preset name - } - - if (name) { - this.innerText = name; - } - } - }); - - // Don't hide entities related to this error - #5880 - context.features().forceVisible(relatedEntities); - context.map().pan([0,0]); // trigger a redraw - } - - improveOsmDetails.issue = function(val) { - if (!arguments.length) return _qaItem; - _qaItem = val; - return improveOsmDetails; - }; - - return improveOsmDetails; -} diff --git a/modules/ui/improveOSM_editor.js b/modules/ui/improveOSM_editor.js deleted file mode 100644 index 98473b3f46e..00000000000 --- a/modules/ui/improveOSM_editor.js +++ /dev/null @@ -1,200 +0,0 @@ -import { dispatch as d3_dispatch } from 'd3-dispatch'; -import { select as d3_select } from 'd3-selection'; - -import { t } from '../core/localizer'; -import { services } from '../services'; -import { modeBrowse } from '../modes/browse'; -import { svgIcon } from '../svg/icon'; - -import { uiImproveOsmComments } from './improveOSM_comments'; -import { uiImproveOsmDetails } from './improveOSM_details'; -import { uiImproveOsmHeader } from './improveOSM_header'; - -import { utilNoAuto, utilRebind } from '../util'; - -export function uiImproveOsmEditor(context) { - const dispatch = d3_dispatch('change'); - const qaDetails = uiImproveOsmDetails(context); - const qaComments = uiImproveOsmComments(context); - const qaHeader = uiImproveOsmHeader(context); - - let _qaItem; - - function improveOsmEditor(selection) { - - const headerEnter = selection.selectAll('.header') - .data([0]) - .enter() - .append('div') - .attr('class', 'header fillL'); - - headerEnter - .append('button') - .attr('class', 'close') - .attr('title', t('icons.close')) - .on('click', () => context.enter(modeBrowse(context))) - .call(svgIcon('#iD-icon-close')); - - headerEnter - .append('h2') - .call(t.append('QA.improveOSM.title')); - - let body = selection.selectAll('.body') - .data([0]); - - body = body.enter() - .append('div') - .attr('class', 'body') - .merge(body); - - const editor = body.selectAll('.qa-editor') - .data([0]); - - editor.enter() - .append('div') - .attr('class', 'modal-section qa-editor') - .merge(editor) - .call(qaHeader.issue(_qaItem)) - .call(qaDetails.issue(_qaItem)) - .call(qaComments.issue(_qaItem)) - .call(improveOsmSaveSection); - } - - function improveOsmSaveSection(selection) { - const isSelected = (_qaItem && _qaItem.id === context.selectedErrorID()); - const isShown = (_qaItem && (isSelected || _qaItem.newComment || _qaItem.comment)); - let saveSection = selection.selectAll('.qa-save') - .data( - (isShown ? [_qaItem] : []), - d => `${d.id}-${d.status || 0}` - ); - - // exit - saveSection.exit() - .remove(); - - // enter - const saveSectionEnter = saveSection.enter() - .append('div') - .attr('class', 'qa-save save-section cf'); - - saveSectionEnter - .append('h4') - .attr('class', '.qa-save-header') - .call(t.append('note.newComment')); - - saveSectionEnter - .append('textarea') - .attr('class', 'new-comment-input') - .attr('placeholder', t('QA.keepRight.comment_placeholder')) - .attr('maxlength', 1000) - .property('value', d => d.newComment) - .call(utilNoAuto) - .on('input', changeInput) - .on('blur', changeInput); - - // update - saveSection = saveSectionEnter - .merge(saveSection) - .call(qaSaveButtons); - - function changeInput() { - const input = d3_select(this); - let val = input.property('value').trim(); - - if (val === '') { - val = undefined; - } - - // store the unsaved comment with the issue itself - _qaItem = _qaItem.update({ newComment: val }); - - const qaService = services.improveOSM; - if (qaService) { - qaService.replaceItem(_qaItem); - } - - saveSection - .call(qaSaveButtons); - } - } - - function qaSaveButtons(selection) { - const isSelected = (_qaItem && _qaItem.id === context.selectedErrorID()); - let buttonSection = selection.selectAll('.buttons') - .data((isSelected ? [_qaItem] : []), d => d.status + d.id); - - // exit - buttonSection.exit() - .remove(); - - // enter - const buttonEnter = buttonSection.enter() - .append('div') - .attr('class', 'buttons'); - - buttonEnter - .append('button') - .attr('class', 'button comment-button action') - .call(t.append('QA.keepRight.save_comment')); - - buttonEnter - .append('button') - .attr('class', 'button close-button action'); - - buttonEnter - .append('button') - .attr('class', 'button ignore-button action'); - - // update - buttonSection = buttonSection - .merge(buttonEnter); - - buttonSection.select('.comment-button') - .attr('disabled', d => d.newComment ? null : true) - .on('click.comment', function(d3_event, d) { - this.blur(); // avoid keeping focus on the button - #4641 - const qaService = services.improveOSM; - if (qaService) { - qaService.postUpdate(d, (err, item) => dispatch.call('change', item)); - } - }); - - buttonSection.select('.close-button') - .html(d => { - const andComment = (d.newComment ? '_comment' : ''); - return t.html(`QA.keepRight.close${andComment}`); - }) - .on('click.close', function(d3_event, d) { - this.blur(); // avoid keeping focus on the button - #4641 - const qaService = services.improveOSM; - if (qaService) { - d.newStatus = 'SOLVED'; - qaService.postUpdate(d, (err, item) => dispatch.call('change', item)); - } - }); - - buttonSection.select('.ignore-button') - .html(d => { - const andComment = (d.newComment ? '_comment' : ''); - return t.html(`QA.keepRight.ignore${andComment}`); - }) - .on('click.ignore', function(d3_event, d) { - this.blur(); // avoid keeping focus on the button - #4641 - const qaService = services.improveOSM; - if (qaService) { - d.newStatus = 'INVALID'; - qaService.postUpdate(d, (err, item) => dispatch.call('change', item)); - } - }); - } - - // NOTE: Don't change method name until UI v3 is merged - improveOsmEditor.error = function(val) { - if (!arguments.length) return _qaItem; - _qaItem = val; - return improveOsmEditor; - }; - - return utilRebind(improveOsmEditor, dispatch, 'on'); -} diff --git a/modules/ui/improveOSM_header.js b/modules/ui/improveOSM_header.js deleted file mode 100644 index 164eba79466..00000000000 --- a/modules/ui/improveOSM_header.js +++ /dev/null @@ -1,67 +0,0 @@ -import { t } from '../core/localizer'; - - -export function uiImproveOsmHeader() { - let _qaItem; - - - function issueTitle(d) { - const issueKey = d.issueKey; - d.replacements = d.replacements || {}; - d.replacements.default = { html: t.html('inspector.unknown') }; // special key `default` works as a fallback string - return t.html(`QA.improveOSM.error_types.${issueKey}.title`, d.replacements); - } - - - function improveOsmHeader(selection) { - const header = selection.selectAll('.qa-header') - .data( - (_qaItem ? [_qaItem] : []), - d => `${d.id}-${d.status || 0}` - ); - - header.exit() - .remove(); - - const headerEnter = header.enter() - .append('div') - .attr('class', 'qa-header'); - - const svgEnter = headerEnter - .append('div') - .attr('class', 'qa-header-icon') - .classed('new', d => d.id < 0) - .append('svg') - .attr('width', '20px') - .attr('height', '30px') - .attr('viewbox', '0 0 20 30') - .attr('class', d => `preset-icon-28 qaItem ${d.service} itemId-${d.id} itemType-${d.itemType}`); - - svgEnter - .append('polygon') - .attr('fill', 'currentColor') - .attr('class', 'qaItem-fill') - .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'); - - svgEnter - .append('use') - .attr('class', 'icon-annotation') - .attr('width', '12px') - .attr('height', '12px') - .attr('transform', 'translate(4, 5.5)') - .attr('xlink:href', d => d.icon ? '#' + d.icon : ''); - - headerEnter - .append('div') - .attr('class', 'qa-header-label') - .html(issueTitle); - } - - improveOsmHeader.issue = function(val) { - if (!arguments.length) return _qaItem; - _qaItem = val; - return improveOsmHeader; - }; - - return improveOsmHeader; -} diff --git a/modules/ui/index.js b/modules/ui/index.js index 2ffbdd11b03..2097203e6ff 100644 --- a/modules/ui/index.js +++ b/modules/ui/index.js @@ -23,10 +23,6 @@ export { uiFlash } from './flash'; export { uiFormFields } from './form_fields'; export { uiFullScreen } from './full_screen'; export { uiGeolocate } from './geolocate'; -export { uiImproveOsmComments } from './improveOSM_comments'; -export { uiImproveOsmDetails } from './improveOSM_details'; -export { uiImproveOsmEditor } from './improveOSM_editor'; -export { uiImproveOsmHeader } from './improveOSM_header'; export { uiInfo } from './info'; export { uiInspector } from './inspector'; export { uiIssuesInfo } from './issues_info'; diff --git a/modules/ui/sections/data_layers.js b/modules/ui/sections/data_layers.js index 41afb19f980..4053c3bf179 100644 --- a/modules/ui/sections/data_layers.js +++ b/modules/ui/sections/data_layers.js @@ -128,7 +128,7 @@ export function uiSectionDataLayers(context) { } function drawQAItems(selection) { - var qaKeys = ['keepRight', 'improveOSM', 'osmose']; + var qaKeys = ['keepRight', 'osmose']; var qaLayers = layers.all().filter(function(obj) { return qaKeys.indexOf(obj.id) !== -1; }); var ul = selection diff --git a/modules/ui/sidebar.js b/modules/ui/sidebar.js index 418910e8c51..0c7aec564bf 100644 --- a/modules/ui/sidebar.js +++ b/modules/ui/sidebar.js @@ -12,7 +12,6 @@ import { services } from '../services'; import { uiDataEditor } from './data_editor'; import { uiFeatureList } from './feature_list'; import { uiInspector } from './inspector'; -import { uiImproveOsmEditor } from './improveOSM_editor'; import { uiKeepRightEditor } from './keepRight_editor'; import { uiOsmoseEditor } from './osmose_editor'; import { uiNoteEditor } from './note_editor'; @@ -23,7 +22,6 @@ export function uiSidebar(context) { var inspector = uiInspector(context); var dataEditor = uiDataEditor(context); var noteEditor = uiNoteEditor(context); - var improveOsmEditor = uiImproveOsmEditor(context); var keepRightEditor = uiKeepRightEditor(context); var osmoseEditor = uiOsmoseEditor(context); var _current; @@ -211,10 +209,8 @@ export function uiSidebar(context) { var errEditor; if (datum.service === 'keepRight') { errEditor = keepRightEditor; - } else if (datum.service === 'osmose') { - errEditor = osmoseEditor; } else { - errEditor = improveOsmEditor; + errEditor = osmoseEditor; } context.container().selectAll('.qaItem.' + datum.service) diff --git a/test/spec/svg/layers.js b/test/spec/svg/layers.js index 96da0606d31..390b59099fd 100644 --- a/test/spec/svg/layers.js +++ b/test/spec/svg/layers.js @@ -26,26 +26,25 @@ 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(19); + expect(nodes.length).to.eql(18); 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('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-position')).to.be.true; - expect(d3.select(nodes[9]).classed('mapillary-map-features')).to.be.true; - expect(d3.select(nodes[10]).classed('mapillary-signs')).to.be.true; - expect(d3.select(nodes[11]).classed('kartaview')).to.be.true; - expect(d3.select(nodes[12]).classed('mapilio')).to.be.true; + expect(d3.select(nodes[4]).classed('osmose')).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-position')).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('kartaview')).to.be.true; + expect(d3.select(nodes[11]).classed('mapilio')).to.be.true; + expect(d3.select(nodes[12]).classed('vegbilder')).to.be.true; expect(d3.select(nodes[13]).classed('panoramax')).to.be.true; - expect(d3.select(nodes[14]).classed('vegbilder')).to.be.true; - expect(d3.select(nodes[15]).classed('local-photos')).to.be.true; - expect(d3.select(nodes[16]).classed('debug')).to.be.true; - expect(d3.select(nodes[17]).classed('geolocate')).to.be.true; - expect(d3.select(nodes[18]).classed('touch')).to.be.true; + expect(d3.select(nodes[14]).classed('local-photos')).to.be.true; + expect(d3.select(nodes[15]).classed('debug')).to.be.true; + expect(d3.select(nodes[16]).classed('geolocate')).to.be.true; + expect(d3.select(nodes[17]).classed('touch')).to.be.true; }); });