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)
|
@if ($user_role == \App\Models\Users::ROLE_VENDOR || $user_role == \App\Models\Users::ROLE_ADMIN)
|
||||||
@can('vehicle.create')
|
@can('vehicle.create')
|
||||||
<!-- <div class="col text-end">
|
<!-- <div class="col text-end">
|
||||||
<button id="btnMdlNewVhc" class="btn btn-sm btn-danger">Add New Vehicle</button>
|
<button id="btnMdlNewVhc" class="btn btn-sm btn-danger">Add New Vehicle</button>
|
||||||
</div> -->
|
</div> -->
|
||||||
@endcan
|
@endcan
|
||||||
|
|
||||||
{{-- <div class="col-auto text-end ps-0">
|
{{-- <div class="col-auto text-end ps-0">
|
||||||
@ -300,6 +300,81 @@
|
|||||||
return `Site ${site.address}, Timor-Leste`;
|
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
|
// END SITE LABELING
|
||||||
// ================================
|
// ================================
|
||||||
@ -346,7 +421,16 @@
|
|||||||
&poolcode=${$('#poolcode').val() || ''}
|
&poolcode=${$('#poolcode').val() || ''}
|
||||||
`,
|
`,
|
||||||
type: 'GET',
|
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);
|
callback(json);
|
||||||
},
|
},
|
||||||
error: function(xhr, status, error) {
|
error: function(xhr, status, error) {
|
||||||
@ -417,76 +501,43 @@
|
|||||||
// ==== END KOLOM BARU ====
|
// ==== END KOLOM BARU ====
|
||||||
{
|
{
|
||||||
data: 'start',
|
data: 'start',
|
||||||
render: (data, type, row, meta) => {
|
render: (data, type, row) => {
|
||||||
// Parse start_lat_long -> { lat, lng }
|
|
||||||
const {
|
const {
|
||||||
lat: startLat,
|
lat: startLat,
|
||||||
lng: startLng
|
lng: startLng
|
||||||
} = parseLatLong(row.start_lat_long);
|
} = 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);
|
const nearestSite = findNearestSite(startLat, startLng);
|
||||||
|
|
||||||
if (nearestSite) {
|
// Prioritas: site terdaftar > startLoc (dari API atau hasil reverse geocode) > '-'
|
||||||
return `${moment.unix(data + AppState.TIMEFIX).format('DD MMM YYYY HH:mm:ss')}<br>`+formatSiteLabel(nearestSite);
|
const locationLabel = nearestSite ?
|
||||||
}
|
formatSiteLabel(nearestSite) :
|
||||||
return `
|
(row.startLoc ?
|
||||||
${moment.unix(data + AppState.TIMEFIX).format('DD MMM YYYY HH:mm:ss')}<br>
|
Helper.shortenText(decodeURIComponent(row.startLoc), 255) :
|
||||||
${Helper.shortenText(decodeURIComponent(row.startLoc || 'address'), 255) || "-"}
|
'-');
|
||||||
`;
|
|
||||||
|
if (type === 'export') return locationLabel;
|
||||||
|
|
||||||
|
return `${moment.unix(data + AppState.TIMEFIX).format('DD MMM YYYY HH:mm:ss')}<br>${locationLabel}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
data: 'finish',
|
data: 'finish',
|
||||||
render: (data, type, row) => {
|
render: (data, type, row) => {
|
||||||
// Parse finish_lat_long -> { lat, lng }
|
|
||||||
const {
|
const {
|
||||||
lat: finishLat,
|
lat: finishLat,
|
||||||
lng: finishLng
|
lng: finishLng
|
||||||
} = parseLatLong(row.finish_lat_long);
|
} = 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);
|
const nearestSite = findNearestSite(finishLat, finishLng);
|
||||||
|
|
||||||
if (nearestSite) {
|
const locationLabel = nearestSite ?
|
||||||
return `${moment.unix(data + AppState.TIMEFIX).format('DD MMM YYYY HH:mm:ss')}<br>`+formatSiteLabel(nearestSite);
|
formatSiteLabel(nearestSite) :
|
||||||
}
|
(row.finishLoc ?
|
||||||
|
Helper.shortenText(decodeURIComponent(row.finishLoc), 255) :
|
||||||
|
'-');
|
||||||
|
|
||||||
// Tampilan di tabel
|
if (type === 'export') return locationLabel;
|
||||||
return `
|
|
||||||
${moment.unix(data + AppState.TIMEFIX)
|
return `${moment.unix(data + AppState.TIMEFIX).format('DD MMM YYYY HH:mm:ss')}<br>${locationLabel}`;
|
||||||
.format('DD MMM YYYY HH:mm:ss')}
|
|
||||||
<br>
|
|
||||||
${
|
|
||||||
Helper.shortenText(
|
|
||||||
decodeURIComponent(row.finishLoc || 'address'),
|
|
||||||
255
|
|
||||||
) || '-'
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user