update: if address start and finish null, change to address from latitude and longitude
This commit is contained in:
@ -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}`;
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user