function loadNextTilePage()

in modules/services/mapillary.js [87:227]


function loadNextTilePage(which, currZoom, url, tile) {
    var cache = _mlyCache[which];
    var rect = tile.extent.rectangle();
    var maxPages = maxPageAtZoom(currZoom);
    var nextPage = cache.nextPage[tile.id] || 0;
    var nextURL = cache.nextURL[tile.id] || url +
        utilQsString({
            per_page: maxResults,
            page: nextPage,
            client_id: clientId,
            bbox: [rect[0], rect[1], rect[2], rect[3]].join(','),
        });

    if (nextPage > maxPages) return;

    var id = tile.id + ',' + String(nextPage);
    if (cache.loaded[id] || cache.inflight[id]) return;

    var controller = new AbortController();
    cache.inflight[id] = controller;

    var options = {
        method: 'GET',
        signal: controller.signal,
        headers: { 'Content-Type': 'application/json' }
    };

    fetch(nextURL, options)
        .then(function(response) {
            if (!response.ok) {
                throw new Error(response.status + ' ' + response.statusText);
            }
            var linkHeader = response.headers.get('Link');
            if (linkHeader) {
                var pagination = parsePagination(linkHeader);
                if (pagination.next) {
                    cache.nextURL[tile.id] = pagination.next;
                }
            }
            return response.json();
        })
        .then(function(data) {
            cache.loaded[id] = true;
            delete cache.inflight[id];
            if (!data || !data.features || !data.features.length) {
                throw new Error('No Data');
            }

            var features = data.features.map(function(feature) {
                var loc = feature.geometry.coordinates;
                var d;

                // An image (shown as a green dot on the map) is a single street photo with extra
                // information such as location, camera angle (CA), camera model, and so on.
                // Each image feature is a GeoJSON Point
                if (which === 'images') {
                    d = {
                        loc: loc,
                        key: feature.properties.key,
                        ca: feature.properties.ca,
                        captured_at: feature.properties.captured_at,
                        captured_by: feature.properties.username,
                        pano: feature.properties.pano
                    };

                    cache.forImageKey[d.key] = d;     // cache imageKey -> image

                // Mapillary organizes images as sequences. A sequence of images are continuously captured
                // by a user at a give time. Sequences are shown on the map as green lines.
                // Each sequence feature is a GeoJSON LineString
                } else if (which === 'sequences') {
                    var sequenceKey = feature.properties.key;
                    cache.lineString[sequenceKey] = feature;           // cache sequenceKey -> lineString
                    feature.properties.coordinateProperties.image_keys.forEach(function(imageKey) {
                        cache.forImageKey[imageKey] = sequenceKey;     // cache imageKey -> sequenceKey
                    });
                    return false;    // because no `d` data worth loading into an rbush

                // An image detection is a semantic pixel area on an image. The area could indicate
                // sky, trees, sidewalk in the image. A detection can be a polygon, a bounding box, or a point.
                // Each image_detection feature is a GeoJSON Point (located where the image was taken)
                } else if (which === 'image_detections') {
                    d = {
                        key: feature.properties.key,
                        image_key: feature.properties.image_key,
                        value: feature.properties.value,
                        package: feature.properties.package,
                        shape: feature.properties.shape
                    };

                    // cache imageKey -> image_detections
                    if (!cache.forImageKey[d.image_key]) {
                        cache.forImageKey[d.image_key] = [];
                    }
                    cache.forImageKey[d.image_key].push(d);
                    return false;    // because no `d` data worth loading into an rbush


                // A map feature is a real world object that can be shown on a map. It could be any object
                // recognized from images, manually added in images, or added on the map.
                // Each map feature is a GeoJSON Point (located where the feature is)
                } else if (which === 'map_features' || which === 'points') {
                    d = {
                        loc: loc,
                        key: feature.properties.key,
                        value: feature.properties.value,
                        package: feature.properties.package,
                        detections: feature.properties.detections
                    };
                }

                return {
                    minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d
                };

            }).filter(Boolean);

            if (cache.rtree && features) {
                cache.rtree.load(features);
            }

            if (data.features.length === maxResults) {  // more pages to load
                cache.nextPage[tile.id] = nextPage + 1;
                loadNextTilePage(which, currZoom, url, tile);
            } else {
                cache.nextPage[tile.id] = Infinity;     // no more pages to load
            }

            if (which === 'images' || which === 'sequences') {
                dispatch.call('loadedImages');
            } else if (which === 'map_features') {
                dispatch.call('loadedSigns');
            } else if (which === 'points') {
                dispatch.call('loadedMapFeatures');
            }
        })
        .catch(function() {
            cache.loaded[id] = true;
            delete cache.inflight[id];
        });
}