import * as fs from 'fs'; import * as path from 'path'; import axios from 'axios'; // Constants const API_URL = process.env.TEST_API_URL || 'https://map-saas.intaleqapp.com/api/geocoding/search'; const API_KEY = process.env.TEST_API_KEY || process.env.MAP_API_KEY || ''; let DATA_FILE = path.join(__dirname, '../../../data/golden_tests/queries_syria.json'); if (!fs.existsSync(DATA_FILE) && fs.existsSync('/data/data/golden_tests/queries_syria.json')) { DATA_FILE = '/data/data/golden_tests/queries_syria.json'; // Docker environment fallback } // Interface for Test Query interface TestQuery { query: string; expected_location: { lat: number; lon: number; }; expected_category?: string; tolerance_meters: number; } // Interface for API Result interface SearchResult { place_id: number; name: string; latitude: number | string; longitude: number | string; category: string; type: string; distance?: number; } // Haversine Formula to calculate distance in meters function calculateDistance(lat1: number, lon1: number, lat2: number, lon2: number): number { const R = 6371e3; // Earth radius in meters const φ1 = (lat1 * Math.PI) / 180; const φ2 = (lat2 * Math.PI) / 180; const Δφ = ((lat2 - lat1) * Math.PI) / 180; const Δλ = ((lon2 - lon1) * Math.PI) / 180; const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) + Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return R * c; } async function runEvaluation() { console.log('🚀 Starting Geocoding Evaluation (Golden Test Set)...\n'); if (!fs.existsSync(DATA_FILE)) { console.error(`❌ Data file not found at ${DATA_FILE}`); process.exit(1); } const rawData = fs.readFileSync(DATA_FILE, 'utf8'); const queries: TestQuery[] = JSON.parse(rawData); let top1Hits = 0; let top3Hits = 0; let zeroResults = 0; let latencies: number[] = []; for (const test of queries) { console.log(`Testing query: "${test.query}"`); const startTime = Date.now(); try { const response = await axios.get(API_URL, { headers: { 'x-api-key': API_KEY }, params: { q: test.query, country: 'syria' } }); const latency = Date.now() - startTime; latencies.push(latency); const results: SearchResult[] = response.data.results || response.data; if (!results || results.length === 0) { console.log(` ❌ Zero results returned.`); zeroResults++; continue; } let foundInTop1 = false; let foundInTop3 = false; for (let i = 0; i < Math.min(results.length, 3); i++) { const res = results[i]; const resLat = Number(res.latitude || (res as any).lat); const resLon = Number(res.longitude || (res as any).lon); const dist = calculateDistance(test.expected_location.lat, test.expected_location.lon, resLat, resLon); const isMatch = dist <= test.tolerance_meters; console.log(` [Rank ${i + 1}] Name: "${res.name_ar || res.name}" | Dist: ${Math.round(dist)}m | Source: ${res.source} | Category: ${res.category}`); if (isMatch) { if (i === 0) foundInTop1 = true; foundInTop3 = true; console.log(` ✅ Match found at rank ${i + 1} (Distance: ${Math.round(dist)}m)`); // We found the match, but we still print the rest for debugging } } if (!foundInTop3) { const topDist = calculateDistance(test.expected_location.lat, test.expected_location.lon, Number(results[0].latitude || (results[0] as any).lat), Number(results[0].longitude || (results[0] as any).lon)); console.log(` ❌ No match in Top-3. Top result distance: ${Math.round(topDist)}m`); } if (foundInTop1) top1Hits++; if (foundInTop3) top3Hits++; } catch (error: any) { console.error(` ❌ API Error: ${error.message}`); } } // Calculate metrics latencies.sort((a, b) => a - b); const p50 = latencies[Math.floor(latencies.length * 0.5)] || 0; const p95 = latencies[Math.floor(latencies.length * 0.95)] || 0; const total = queries.length; console.log('\n======================================='); console.log('📊 EVALUATION RESULTS'); console.log('======================================='); console.log(`Total Queries : ${total}`); console.log(`Top-1 Accuracy : ${((top1Hits / total) * 100).toFixed(1)}% (${top1Hits}/${total})`); console.log(`Top-3 Accuracy : ${((top3Hits / total) * 100).toFixed(1)}% (${top3Hits}/${total})`); console.log(`Zero Results : ${((zeroResults / total) * 100).toFixed(1)}% (${zeroResults}/${total})`); console.log(`p50 Latency : ${p50} ms`); console.log(`p95 Latency : ${p95} ms`); console.log('=======================================\n'); } runEvaluation().catch(console.error);