import { NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { authOptions } from '../../auth/[...nextauth]/route'; import { prisma } from '@/lib/prisma'; interface Coordinate { lat: number; lon: number; } interface AddressPoint { coordinate: Coordinate; city: string; street: string; originalIndex: number; } // İki nokta arası mesafeyi hesapla (Haversine formülü) function calculateDistance(coord1: Coordinate, coord2: Coordinate): number { const R = 6371; // Dünya'nın yarıçapı (km) const dLat = toRad(coord2.lat - coord1.lat); const dLon = toRad(coord2.lon - coord1.lon); const lat1 = toRad(coord1.lat); const lat2 = toRad(coord2.lat); const a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); return R * c; } function toRad(value: number): number { return value * Math.PI / 180; } async function geocodeAddress(address: string): Promise { try { const query = encodeURIComponent(address + ", Switzerland"); const response = await fetch( `https://nominatim.openstreetmap.org/search?q=${query}&format=json&limit=1`, { headers: { 'User-Agent': 'PostaciApp/1.0' } } ); if (!response.ok) { throw new Error('Geocoding request failed'); } const data = await response.json(); if (data && data.length > 0) { return { lat: parseFloat(data[0].lat), lon: parseFloat(data[0].lon) }; } return null; } catch (error) { console.error('Geocoding error:', error); return null; } } function optimizeRoute(points: AddressPoint[]): number[] { if (points.length < 2) return points.map((_, i) => i); const optimizedRoute: number[] = [0]; // Başlangıç noktası const unvisited = points.slice(1).map((_, i) => i + 1); while (unvisited.length > 0) { const currentPoint = points[optimizedRoute[optimizedRoute.length - 1]]; let nearestPoint = -1; let nearestIndex = -1; let minScore = Infinity; // Tüm ziyaret edilmemiş noktaları değerlendir for (let i = 0; i < unvisited.length; i++) { const pointIndex = unvisited[i]; const candidatePoint = points[pointIndex]; // Mesafe ve şehir bazlı skor hesapla const distance = calculateDistance(currentPoint.coordinate, candidatePoint.coordinate); // Şehir aynıysa ekstra bonus ver (daha düşük skor daha iyi) const cityScore = currentPoint.city === candidatePoint.city ? 0 : 2; // Toplam skoru hesapla (mesafe + şehir skoru) const totalScore = distance + cityScore; // En düşük skorlu noktayı bul if (totalScore < minScore) { minScore = totalScore; nearestPoint = pointIndex; nearestIndex = i; } } if (nearestPoint !== -1) { optimizedRoute.push(nearestPoint); unvisited.splice(nearestIndex, 1); // Debug bilgisi console.log(`Eklenen nokta: ${points[nearestPoint].street}, ${points[nearestPoint].city}`); console.log(`Mesafe skoru: ${minScore}`); } } return optimizedRoute; } export async function POST(request: Request) { try { // Oturum kontrolü const session = await getServerSession(authOptions); if (!session?.user?.email) { return new Response( JSON.stringify({ success: false, error: 'Oturum gerekli' }), { status: 200, headers: { 'Content-Type': 'application/json' } } ); } const { addresses, routeId } = await request.json(); console.log('Gelen adresler:', addresses); if (!Array.isArray(addresses) || addresses.length < 2) { return new Response( JSON.stringify({ success: false, error: 'En az 2 adres gerekli' }), { status: 200, headers: { 'Content-Type': 'application/json' } } ); } // Adresleri geocode et const points: AddressPoint[] = []; for (let i = 0; i < addresses.length; i++) { const address = addresses[i]; const parts = address.split(','); const cityMatch = parts[1]?.match(/\d{4}\s+([^\d]+)/); const city = cityMatch ? cityMatch[1].trim() : ''; const coordinate = await geocodeAddress(address); console.log(`Adres ${i + 1} koordinatları:`, { address, city, coordinate }); if (coordinate) { points.push({ coordinate, city, street: parts[0].trim(), originalIndex: i }); } // Rate limiting if (i < addresses.length - 1) { await new Promise(resolve => setTimeout(resolve, 1000)); } } console.log('Geocode edilmiş noktalar:', points); if (points.length < 2) { return new Response( JSON.stringify({ success: false, error: 'Yeterli sayıda geçerli adres bulunamadı' }), { status: 200, headers: { 'Content-Type': 'application/json' } } ); } // Rotayı optimize et const optimizedIndices = optimizeRoute(points); console.log('Optimize edilmiş sıralama:', optimizedIndices); // Doğrudan points dizisinden alarak optimizedRoute'u oluştur const optimizedRoute = optimizedIndices.map(i => ({ index: i, // originalIndex yerine doğrudan sıra numarasını kullan lat: points[i].coordinate.lat, lon: points[i].coordinate.lon, address: addresses[i] // points[i].originalIndex yerine doğrudan i kullan })); console.log('Optimize edilmiş rota:', optimizedRoute); // Toplam mesafeyi hesapla let totalDistance = 0; for (let i = 0; i < optimizedRoute.length - 1; i++) { totalDistance += calculateDistance( { lat: optimizedRoute[i].lat, lon: optimizedRoute[i].lon }, { lat: optimizedRoute[i + 1].lat, lon: optimizedRoute[i + 1].lon } ); } // Tahmini süreyi hesapla (ortalama 30 km/saat hız varsayarak) const estimatedDuration = (totalDistance / 30) * 60; // dakika cinsinden // Rotanın durumunu güncelle if (routeId) { // Önce tüm adresleri getir const existingAddresses = await prisma.route.findUnique({ where: { id: routeId }, include: { addresses: true } }); if (existingAddresses) { // Her optimize edilmiş adres için güncelleme yap await Promise.all(optimizedRoute.map(async (route, newOrder) => { const addressToUpdate = existingAddresses.addresses.find( addr => addr.street === route.address.split(',')[0].trim() ); if (addressToUpdate) { await prisma.address.update({ where: { id: addressToUpdate.id }, data: { order: newOrder, latitude: route.lat, longitude: route.lon } }); } })); // Route'un durumunu güncelle await prisma.route.update({ where: { id: routeId }, data: { status: 'OPTIMIZED' } }); } } return new Response( JSON.stringify({ success: true, data: { route: optimizedRoute, distance: Math.round(totalDistance * 10) / 10, // 1 ondalık basamak duration: Math.round(estimatedDuration) } }), { status: 200, headers: { 'Content-Type': 'application/json' } } ); } catch (error) { console.error('Route optimization error:', error); return new Response( JSON.stringify({ success: false, error: 'Rota optimizasyonu sırasında bir hata oluştu' }), { status: 200, headers: { 'Content-Type': 'application/json' } } ); } }