From 994608327a6ef021c654c4091e177f6012796141 Mon Sep 17 00:00:00 2001 From: wayanrivan Date: Sat, 1 Aug 2026 20:39:20 +0700 Subject: [PATCH] update: if address start and finish null, change to address from latitude and longitude --- .../menu_v1/reports/vehicle_trips.blade.php | 161 ++++++++++++------ 1 file changed, 106 insertions(+), 55 deletions(-) diff --git a/resources/views/menu_v1/reports/vehicle_trips.blade.php b/resources/views/menu_v1/reports/vehicle_trips.blade.php index ef7e2cc..a32b5aa 100644 --- a/resources/views/menu_v1/reports/vehicle_trips.blade.php +++ b/resources/views/menu_v1/reports/vehicle_trips.blade.php @@ -25,8 +25,8 @@ @if ($user_role == \App\Models\Users::ROLE_VENDOR || $user_role == \App\Models\Users::ROLE_ADMIN) @can('vehicle.create') + + --> @endcan {{--
@@ -300,6 +300,81 @@ return `Site ${site.address}, Timor-Leste`; } + // ================================ + // REVERSE GEOCODING (Nominatim) - fallback ketika startLoc/finishLoc kosong + // ================================ + + // Cache in-memory supaya koordinat yang sama tidak fetch berulang + const geocodeCache = new Map(); + + // Antrian sederhana supaya request ke Nominatim tidak lebih dari 1/detik + let geocodeQueue = Promise.resolve(); + + function reverseGeocode(lat, lng) { + if (lat === null || lng === null || lat === undefined || lng === undefined || isNaN(lat) || isNaN(lng)) { + return Promise.resolve(null); + } + + const key = `${lat.toFixed(5)},${lng.toFixed(5)}`; + if (geocodeCache.has(key)) { + return Promise.resolve(geocodeCache.get(key)); + } + + // Chain ke queue supaya jaraknya minimal ~1100ms antar request + geocodeQueue = geocodeQueue.then(() => + new Promise(resolve => setTimeout(resolve, 1100)) + ); + + return geocodeQueue.then(async () => { + try { + const url = + `https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&zoom=18&addressdetails=1`; + const res = await fetch(url, { + headers: { + 'Accept-Language': 'id' + } + }); + const json = await res.json(); + const address = json && json.display_name ? json.display_name : null; + geocodeCache.set(key, address); + return address; + } catch (e) { + console.error('Reverse geocode gagal:', e); + geocodeCache.set(key, null); + return null; + } + }); + } + + // Isi row.startLoc / row.finishLoc yang masih kosong (null/undefined/'') + // menggunakan hasil reverse geocode dari koordinat start_lat_long / finish_lat_long. + // Nilai disimpan dalam bentuk encodeURIComponent supaya konsisten dengan + // field startLoc/finishLoc bawaan API yang memang sudah URL-encoded, + // karena proses render memanggil decodeURIComponent(row.startLoc). + async function fillMissingLocations(rows) { + if (!Array.isArray(rows)) return; + + for (const row of rows) { + if (!row.startLoc) { + const { + lat, + lng + } = parseLatLong(row.start_lat_long); + const address = await reverseGeocode(lat, lng); + row.startLoc = address ? encodeURIComponent(address) : null; + } + + if (!row.finishLoc) { + const { + lat, + lng + } = parseLatLong(row.finish_lat_long); + const address = await reverseGeocode(lat, lng); + row.finishLoc = address ? encodeURIComponent(address) : null; + } + } + } + // ================================ // END SITE LABELING // ================================ @@ -346,7 +421,16 @@ &poolcode=${$('#poolcode').val() || ''} `, type: 'GET', - success: function(json) { + success: async function(json) { + // Isi startLoc/finishLoc yang kosong via Nominatim + // SEBELUM tabel dirender, karena render() DataTables bersifat sinkron + // dan tidak bisa menunggu fetch() di dalamnya. + try { + await fillMissingLocations(json && json.data); + } catch (e) { + console.error('Gagal melengkapi lokasi via reverse geocode:', e); + } + callback(json); }, error: function(xhr, status, error) { @@ -417,76 +501,43 @@ // ==== END KOLOM BARU ==== { data: 'start', - render: (data, type, row, meta) => { - // Parse start_lat_long -> { lat, lng } + render: (data, type, row) => { const { lat: startLat, lng: startLng } = parseLatLong(row.start_lat_long); - - // saat export, hanya tampilkan lokasi tanpa tanggal/jam - if (type === 'export') { - const nearestSite = findNearestSite(startLat, startLng); - - if (nearestSite) { - return formatSiteLabel(nearestSite); - } - return Helper.shortenText(decodeURIComponent(row.startLoc || - 'address'), 255) || "-"; - } - const nearestSite = findNearestSite(startLat, startLng); - if (nearestSite) { - return `${moment.unix(data + AppState.TIMEFIX).format('DD MMM YYYY HH:mm:ss')}
`+formatSiteLabel(nearestSite); - } - return ` - ${moment.unix(data + AppState.TIMEFIX).format('DD MMM YYYY HH:mm:ss')}
- ${Helper.shortenText(decodeURIComponent(row.startLoc || 'address'), 255) || "-"} - `; + // Prioritas: site terdaftar > startLoc (dari API atau hasil reverse geocode) > '-' + const locationLabel = nearestSite ? + formatSiteLabel(nearestSite) : + (row.startLoc ? + Helper.shortenText(decodeURIComponent(row.startLoc), 255) : + '-'); + + if (type === 'export') return locationLabel; + + return `${moment.unix(data + AppState.TIMEFIX).format('DD MMM YYYY HH:mm:ss')}
${locationLabel}`; } }, { data: 'finish', render: (data, type, row) => { - // Parse finish_lat_long -> { lat, lng } const { lat: finishLat, lng: finishLng } = parseLatLong(row.finish_lat_long); - - // Saat export Excel - if (type === 'export') { - const nearestSite = findNearestSite(finishLat, finishLng); - - if (nearestSite) { - return formatSiteLabel(nearestSite); - } - - return Helper.shortenText( - decodeURIComponent(row.finishLoc || 'address'), - 255 - ) || '-'; - } - const nearestSite = findNearestSite(finishLat, finishLng); - if (nearestSite) { - return `${moment.unix(data + AppState.TIMEFIX).format('DD MMM YYYY HH:mm:ss')}
`+formatSiteLabel(nearestSite); - } + const locationLabel = nearestSite ? + formatSiteLabel(nearestSite) : + (row.finishLoc ? + Helper.shortenText(decodeURIComponent(row.finishLoc), 255) : + '-'); - // Tampilan di tabel - return ` - ${moment.unix(data + AppState.TIMEFIX) - .format('DD MMM YYYY HH:mm:ss')} -
- ${ - Helper.shortenText( - decodeURIComponent(row.finishLoc || 'address'), - 255 - ) || '-' - } -`; + if (type === 'export') return locationLabel; + + return `${moment.unix(data + AppState.TIMEFIX).format('DD MMM YYYY HH:mm:ss')}
${locationLabel}`; } }, {