mirror of
https://github.com/tiennm99/vngeoguessr.git
synced 2026-10-15 06:16:23 +00:00
fix(mapillary): always sub-sample small bbox + retry on transient errors
This commit is contained in:
1 parent
963175886f
commit
e47b7fe28b
1 file changed
+28
-32
+28
-32
@@ -1,40 +1,34 @@
|
||||
// Mapillary API utilities
|
||||
|
||||
// Maximum bbox area allowed by Mapillary API (square degrees).
|
||||
// Mapillary tightened limits: keep area small enough to avoid 500 'reduce data' errors.
|
||||
const MAX_BBOX_AREA = 0.001;
|
||||
// Maximum bbox area allowed per Mapillary query (square degrees).
|
||||
// Mapillary returns 500 'reduce data' / 'unknown error' on dense urban bboxes,
|
||||
// so we keep each query small (~1km square) and rely on the retry loop to
|
||||
// re-roll a new random sub-bbox if one query fails or returns empty.
|
||||
const MAX_BBOX_AREA = 0.0001;
|
||||
|
||||
// Pick a random sub-bbox within the city bbox that fits Mapillary's area limit
|
||||
// Pick a random sub-bbox within the city bbox that fits Mapillary's area limit.
|
||||
function getRandomSubBbox(bbox) {
|
||||
const [minLng, minLat, maxLng, maxLat] = bbox;
|
||||
const width = maxLng - minLng;
|
||||
const height = maxLat - minLat;
|
||||
const area = width * height;
|
||||
|
||||
if (area <= MAX_BBOX_AREA) {
|
||||
return bbox;
|
||||
}
|
||||
|
||||
// Calculate sub-bbox side length (square) that fits within the limit
|
||||
const side = Math.sqrt(MAX_BBOX_AREA) * 0.9; // 10% margin for safety
|
||||
// Always sub-sample even if the parent bbox is smaller than the cap — a
|
||||
// smaller window keeps Mapillary happy in dense regions like central Hanoi.
|
||||
const side = Math.sqrt(MAX_BBOX_AREA) * 0.9; // 10% safety margin
|
||||
const subWidth = Math.min(side, width);
|
||||
const subHeight = Math.min(side, height);
|
||||
|
||||
const startLng = minLng + Math.random() * (width - subWidth);
|
||||
const startLat = minLat + Math.random() * (height - subHeight);
|
||||
const startLng = minLng + Math.random() * Math.max(0, width - subWidth);
|
||||
const startLat = minLat + Math.random() * Math.max(0, height - subHeight);
|
||||
|
||||
return [startLng, startLat, startLng + subWidth, startLat + subHeight];
|
||||
}
|
||||
|
||||
// Fetch images from a single bbox query
|
||||
// Fetch images from a single bbox query.
|
||||
async function queryMapillary(accessToken, queryBbox) {
|
||||
const bboxString = queryBbox.join(',');
|
||||
const apiUrl = `https://graph.mapillary.com/images?access_token=${accessToken}&fields=id,thumb_2048_url,geometry,is_pano&limit=5&bbox=${bboxString}&is_pano=true`;
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.log('Mapillary query bbox:', bboxString);
|
||||
}
|
||||
|
||||
const response = await fetch(apiUrl, {
|
||||
headers: { 'Accept': 'application/json' },
|
||||
next: { revalidate: 0 }
|
||||
@@ -54,37 +48,39 @@ async function queryMapillary(accessToken, queryBbox) {
|
||||
return data.data || [];
|
||||
}
|
||||
|
||||
// Fetch images using city bbox, retrying with different sub-bboxes
|
||||
const MAX_RETRIES = 5;
|
||||
// Fetch panoramic images, retrying with a fresh sub-bbox on failure or empty
|
||||
// result. Auth failures still abort early.
|
||||
const MAX_RETRIES = 8;
|
||||
|
||||
export async function fetchMapillaryImages(bbox) {
|
||||
const accessToken = process.env.MAPILLARY_ACCESS_TOKEN;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('MAPILLARY_ACCESS_TOKEN environment variable is not set');
|
||||
}
|
||||
|
||||
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
||||
try {
|
||||
const queryBbox = getRandomSubBbox(bbox);
|
||||
const images = await queryMapillary(accessToken, queryBbox);
|
||||
let lastError = null;
|
||||
|
||||
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
||||
const queryBbox = getRandomSubBbox(bbox);
|
||||
try {
|
||||
const images = await queryMapillary(accessToken, queryBbox);
|
||||
if (images.length > 0) {
|
||||
console.log(`Found ${images.length} images on attempt ${attempt}`);
|
||||
return { success: true, data: images };
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.log(`Attempt ${attempt}/${MAX_RETRIES}: no images found, retrying...`);
|
||||
}
|
||||
console.log(`Attempt ${attempt}/${MAX_RETRIES}: no images, retrying with new sub-bbox`);
|
||||
} catch (error) {
|
||||
console.error(`Attempt ${attempt}/${MAX_RETRIES} failed:`, error.message);
|
||||
throw error;
|
||||
// Auth issues are not transient — abort fast.
|
||||
if (error.message === 'Mapillary authentication failed') throw error;
|
||||
lastError = error;
|
||||
console.error(`Attempt ${attempt}/${MAX_RETRIES} failed: ${error.message}; retrying with new sub-bbox`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: 'No street view images found in city bbox'
|
||||
error: lastError
|
||||
? `No street view images found after ${MAX_RETRIES} attempts (last: ${lastError.message})`
|
||||
: 'No street view images found in city bbox',
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user