import { RideSample, ZoneAnalysis } from './types'; import { assignZone, classifyZoneType } from './clustering'; /** * Analyze pricing by geographical zone (2.5km grid). * Groups samples into zones and computes per-zone statistics. */ export function analyzeByZone(samples: RideSample[]): ZoneAnalysis[] { const zoneMap = new Map(); for (const s of samples) { // Use start location for zone assignment const zone = assignZone(s.startLat, s.startLng); if (!zoneMap.has(zone)) zoneMap.set(zone, []); zoneMap.get(zone)!.push(s); } const results: ZoneAnalysis[] = []; for (const [zoneKey, zoneSamples] of zoneMap) { if (zoneSamples.length < 3) continue; const ppkValues = zoneSamples.map(s => s.ppk); const avgPpk = Math.round( (ppkValues.reduce((a, b) => a + b, 0) / ppkValues.length) * 1000 ) / 1000; // Count by tier — thresholds depend on currency scale const tierCounts: Record = {}; const sample = zoneSamples[0]; const isHighDenom = sample.countryCode === 'SY' || sample.countryCode === 'IQ'; const econThreshold = isHighDenom ? 15 : 0.35; const stdThreshold = isHighDenom ? 40 : 0.55; for (const s of zoneSamples) { const tier = s.ppk < econThreshold ? 'economy' : s.ppk < stdThreshold ? 'standard' : 'premium'; tierCounts[tier] = (tierCounts[tier] || 0) + 1; } const [latStr, lngStr] = zoneKey.split(','); results.push({ zoneKey, centerLat: parseFloat(latStr), centerLng: parseFloat(lngStr), samples: zoneSamples, avgPpk, tierDistribution: tierCounts, }); } return results.sort((a, b) => a.avgPpk - b.avgPpk); } /** * Analyze pricing by zone type (centre, mid, suburb, outskirts). * Passes countryCode to classifyZoneType so the correct city center is used. */ export function analyzeByZoneType( samples: RideSample[] ): Array<{ zoneType: string; avgPpk: number; sampleCount: number; avgPrice: number }> { const typeMap = new Map(); for (const s of samples) { // Pass countryCode so we use the correct city center (not always Amman) const zoneType = classifyZoneType(s.startLat, s.startLng, s.countryCode); if (!typeMap.has(zoneType)) typeMap.set(zoneType, []); typeMap.get(zoneType)!.push(s.ppk); } return Array.from(typeMap.entries()) .map(([zoneType, ppks]) => ({ zoneType, avgPpk: Math.round( (ppks.reduce((a, b) => a + b, 0) / ppks.length) * 1000 ) / 1000, sampleCount: ppks.length, avgPrice: 0, // calculated below if needed })) .sort((a, b) => a.avgPpk - b.avgPpk); }