update: if address start and finish null, change to address from latitude and longitude

This commit is contained in:
wayanrivan
2026-08-01 20:39:20 +07:00
parent cbca01a8e2
commit 994608327a

View File

@ -25,8 +25,8 @@
@if ($user_role == \App\Models\Users::ROLE_VENDOR || $user_role == \App\Models\Users::ROLE_ADMIN)
@can('vehicle.create')
<!-- <div class="col text-end">
<button id="btnMdlNewVhc" class="btn btn-sm btn-danger">Add New Vehicle</button>
</div> -->
<button id="btnMdlNewVhc" class="btn btn-sm btn-danger">Add New Vehicle</button>
</div> -->
@endcan
{{-- <div class="col-auto text-end ps-0">
@ -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')}<br>`+formatSiteLabel(nearestSite);
}
return `
${moment.unix(data + AppState.TIMEFIX).format('DD MMM YYYY HH:mm:ss')}<br>
${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')}<br>${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')}<br>`+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')}
<br>
${
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')}<br>${locationLabel}`;
}
},
{