loadErrors: function()

in modules/services/improveOSM.js [144:338]


    loadErrors: function(projection) {
        var 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
        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();
            var params = Object.assign({}, options, { east: rect[0], south: rect[3], west: rect[2], north: rect[1] });

            // 3 separate requests to store for each tile
            var requests = {};

            Object.keys(_impOsmUrls).forEach(function(k) {
                var v = _impOsmUrls[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
                var kParams = Object.assign({},
                    params,
                    (k === 'mr') ? { type: 'PARKING,ROAD,BOTH,PATH' } : { confidenceLevel: 'C1' }
                );
                var url = v + '/search?' + utilQsString(kParams);

                var controller = new AbortController();
                requests[k] = controller;

                d3_json(url, { signal: controller.signal })
                    .then(function(data) {
                        delete _erCache.inflightTile[tile.id][k];
                        if (!Object.keys(_erCache.inflightTile[tile.id]).length) {
                            delete _erCache.inflightTile[tile.id];
                            _erCache.loadedTile[tile.id] = true;
                        }

                        // Road segments at high zoom == oneways
                        if (data.roadSegments) {
                            data.roadSegments.forEach(function(feature) {
                                // Position error at the approximate middle of the segment
                                var points = feature.points;
                                var mid = points.length / 2;
                                var 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);

                                var d = new qaError({
                                    // Info required for every error
                                    loc: loc,
                                    service: 'improveOSM',
                                    error_type: k,
                                    // Extra details needed for this service
                                    error_key: k,
                                    identifier: { // this is used to post changes to the error
                                        wayId: feature.wayId,
                                        fromNodeId: feature.fromNodeId,
                                        toNodeId: feature.toNodeId
                                    },
                                    object_id: feature.wayId,
                                    object_type: 'way',
                                    status: feature.status
                                });

                                // 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)
                                };

                                _erCache.data[d.id] = d;
                                _erCache.rtree.insert(encodeErrorRtree(d));
                            });
                        }

                        // Tiles at high zoom == missing roads
                        if (data.tiles) {
                            data.tiles.forEach(function(feature) {
                                var geoType = feature.type.toLowerCase();

                                // Average of recorded points should land on the missing geometry
                                // Missing geometry could happen to land on another error
                                var loc = pointAverage(feature.points);
                                loc = preventCoincident(loc, false);

                                var d = new qaError({
                                    // Info required for every error
                                    loc: loc,
                                    service: 'improveOSM',
                                    error_type: k + '-' + geoType,
                                    // Extra details needed for this service
                                    error_key: k,
                                    identifier: { x: feature.x, y: feature.y },
                                    status: feature.status
                                });

                                d.replacements = {
                                    num_trips: feature.numberOfTrips,
                                    geometry_type: t('QA.improveOSM.geometry_types.' + geoType)
                                };

                                // -1 trips indicates data came from a 3rd party
                                if (feature.numberOfTrips === -1) {
                                    d.desc = t('QA.improveOSM.error_types.mr.description_alt', d.replacements);
                                }

                                _erCache.data[d.id] = d;
                                _erCache.rtree.insert(encodeErrorRtree(d));
                            });
                        }

                        // Entities at high zoom == turn restrictions
                        if (data.entities) {
                            data.entities.forEach(function(feature) {
                                // Turn restrictions could be missing at same junction
                                // We also want to bump the error up so node is accessible
                                var loc = feature.point;
                                loc = preventCoincident([loc.lon, loc.lat], true);

                                // Elements are presented in a strange way
                                var ids = feature.id.split(',');
                                var from_way = ids[0];
                                var via_node = ids[3];
                                var to_way = ids[2].split(':')[1];

                                var d = new qaError({
                                    // Info required for every error
                                    loc: loc,
                                    service: 'improveOSM',
                                    error_type: k,
                                    // Extra details needed for this service
                                    error_key: k,
                                    identifier: feature.id,
                                    object_id: via_node,
                                    object_type: 'node',
                                    status: feature.status
                                });

                                // Travel direction along from_way clarifies the turn restriction
                                var p1 = feature.segments[0].points[0];
                                var p2 = feature.segments[0].points[1];

                                var dir_of_travel = cardinalDirection(relativeBearing(p1, p2));

                                // Variables used in the description
                                d.replacements = {
                                    num_passed: feature.numberOfPasses,
                                    num_trips: feature.segments[0].numberOfTrips,
                                    turn_restriction: feature.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'))
                                };

                                _erCache.data[d.id] = d;
                                _erCache.rtree.insert(encodeErrorRtree(d));
                                dispatch.call('loaded');
                            });
                        }
                    })
                    .catch(function() {
                        delete _erCache.inflightTile[tile.id][k];
                        if (!Object.keys(_erCache.inflightTile[tile.id]).length) {
                            delete _erCache.inflightTile[tile.id];
                            _erCache.loadedTile[tile.id] = true;
                        }
                    });
            });

            _erCache.inflightTile[tile.id] = requests;
        });
    },