update
This commit is contained in:
@ -234,6 +234,7 @@ class VehiclesController extends Controller
|
||||
"nopol1" => strtoupper($req->nopol1),
|
||||
// "nopol2" => strtoupper($req->nopol2),
|
||||
// "nopol3" => strtoupper($req->nopol3),
|
||||
"sum_milleage" => $req->sum_milleage ?? 0,
|
||||
"c_did" => $req->d_current ?? 0,
|
||||
"a_did" => $req->d_assign ?? 0,
|
||||
"is_track_holiday" => Vehicles::DEFAULT_TRACK_HOLIDAY,
|
||||
@ -489,6 +490,7 @@ class VehiclesController extends Controller
|
||||
"nopol3" => strtoupper($req->nopol3),
|
||||
"c_did" => $req->d_current ?? 0,
|
||||
"a_did" => $req->d_assign ?? 0,
|
||||
"sum_milleage" => $req->sum_milleage ?? 0,
|
||||
"is_track_holiday" => Vehicles::DEFAULT_TRACK_HOLIDAY,
|
||||
"track_sch_d" => Vehicles::DEFAULT_TRACK_SCH_D,
|
||||
"track_sch_h" => Vehicles::DEFAULT_TRACK_SCH_H,
|
||||
|
||||
@ -305,11 +305,85 @@ class Tracks extends Model
|
||||
return $list;
|
||||
}
|
||||
|
||||
// public static function lastMoveTracks($vid, $filter = [])
|
||||
// {
|
||||
// $now = time();
|
||||
// $params = [];
|
||||
// $query = "SELECT";
|
||||
// $query .= " v.id as vid,v.device_id,v.nopol1,v.nopol2,v.nopol3";
|
||||
// $query .= " ,tr.id as master_id,tr.latitude,tr.longitude,tr.speed,tr.orientation";
|
||||
// $query .= " ,tr.crt AS lst_loc_crt,tr.crt_d AS lst_loc_crt_d,tr.crt_s AS lst_loc_crt_s";
|
||||
// $query .= " ,tr.ignition,tr.stts_engine";
|
||||
// $query .= " ,tr.pre_milleage,tr.sum_milleage,tr.vhc_milleage,v.sum_milleage AS vhc_sum_milleage_1";
|
||||
// $query .= " ,addr.master_id AS addr_master_id,addr.crt AS addr_loc_crt";
|
||||
// $query .=
|
||||
// " ,addr.country_text,addr.state_text,addr.city_text,addr.district_text,addr.village_text,addr.postcode,addr.streets,addr.postcode,addr.fulladdress";
|
||||
// $query .= " FROM t_vehicles AS v";
|
||||
// // $query .= " INNER JOIN " . self::T_TRACKS . " AS tr ON v.device_id = tr.device_id"; // cara lama berlaku utk gps tracker saja
|
||||
// $query .= " INNER JOIN " . self::T_TRACKS . " AS tr ON v.id = tr.vhc_id"; // support gps tracker dan smartphone / apapun yang mempunyai device_id(IMEI)
|
||||
// $query .= " LEFT JOIN " . self::T_TRACKS_ADDR . " AS addr ON tr.id = addr.master_id";
|
||||
// $query .= " WHERE v.dlt is null";
|
||||
// $query .= " AND v.id = ?";
|
||||
// array_push($params, $vid);
|
||||
// $query .= " AND tr.latitude is not null";
|
||||
// $query .= " AND tr.longitude is not null";
|
||||
|
||||
// // last move based on date
|
||||
// // $query .= " AND tr.crt BETWEEN ? AND ?";
|
||||
// // array_push($params, strtotime('-24 hours', $now), $now);
|
||||
|
||||
// if (isset($filter["start_date"]) && isset($filter["end_date"])) {
|
||||
// $query .= " AND tr.crt_d BETWEEN ? AND ?";
|
||||
// array_push($params, $filter["start_date"], $filter["end_date"]);
|
||||
// }
|
||||
|
||||
// if (isset($filter["start_at"])) {
|
||||
// $query .= " AND tr.crt > ?";
|
||||
// $params[] = $filter["start_at"];
|
||||
// }
|
||||
|
||||
// // last move based on last count data
|
||||
// $query .= " GROUP BY tr.crt_d";
|
||||
// $query .= " ORDER BY tr.crt_d DESC";
|
||||
// // array_push($params, strtotime('-24 hours', $now), $now);
|
||||
|
||||
// // if (isset($filter["limit"])) {
|
||||
// // $query .= " LIMIT ?";
|
||||
// // $params[] = $filter["limit"] ?? 500;
|
||||
// // }
|
||||
|
||||
// $query .= " ;";
|
||||
|
||||
// // return DB::select($query, $params);
|
||||
// $rawTracks = DB::select($query, $params);
|
||||
// $cleanTracks = self::filterJumps($rawTracks);
|
||||
|
||||
// if (isset($filter["limit"])) {
|
||||
// return array_slice($cleanTracks, 0, $filter["limit"]);
|
||||
// }
|
||||
// return $cleanTracks;
|
||||
// }
|
||||
|
||||
public static function lastMoveTracks($vid, $filter = [])
|
||||
{
|
||||
$now = time();
|
||||
$params = [];
|
||||
|
||||
/**
|
||||
* bikin lemot 30s timeout karena ada 2 join di rows yang banyak
|
||||
* solution: indexing column
|
||||
* show index from t_gps_tracks_address;
|
||||
* create index addr_device_id on t_gps_tracks_address (device_id);
|
||||
* create index addr_master_id on t_gps_tracks_address (master_id);
|
||||
* show index from t_gps_tracks;
|
||||
* create index tracks_device_id on t_gps_tracks (device_id);
|
||||
* show index from t_vehicles;
|
||||
* create index vhc_device_id on t_vehicles (device_id);
|
||||
*
|
||||
* solution not working. Don't reinvent the wheel
|
||||
* create view. not working
|
||||
*/
|
||||
|
||||
$query = "SELECT";
|
||||
$query .= " v.id as vid,v.device_id,v.nopol1,v.nopol2,v.nopol3";
|
||||
$query .= " ,tr.id as master_id,tr.latitude,tr.longitude,tr.speed,tr.orientation";
|
||||
@ -320,17 +394,22 @@ class Tracks extends Model
|
||||
$query .=
|
||||
" ,addr.country_text,addr.state_text,addr.city_text,addr.district_text,addr.village_text,addr.postcode,addr.streets,addr.postcode,addr.fulladdress";
|
||||
$query .= " FROM t_vehicles AS v";
|
||||
$query .= " INNER JOIN " . self::T_TRACKS . " AS tr ON v.id = tr.vhc_id";
|
||||
// $query .= " INNER JOIN " . self::T_TRACKS . " AS tr ON v.device_id = tr.device_id"; // cara lama berlaku utk gps tracker saja
|
||||
$query .= " INNER JOIN " . self::T_TRACKS . " AS tr ON v.id = tr.vhc_id"; // support gps tracker dan smartphone / apapun yang mempunyai device_id(IMEI)
|
||||
$query .= " LEFT JOIN " . self::T_TRACKS_ADDR . " AS addr ON tr.id = addr.master_id";
|
||||
$query .= " WHERE v.dlt is null AND v.id = ?";
|
||||
$params[] = $vid;
|
||||
$query .= " WHERE v.dlt is null";
|
||||
$query .= " AND v.id = ?";
|
||||
array_push($params, $vid);
|
||||
$query .= " AND tr.latitude is not null";
|
||||
$query .= " AND tr.longitude is not null";
|
||||
|
||||
$query .= " AND tr.latitude is not null AND tr.longitude is not null";
|
||||
// last move based on date
|
||||
// $query .= " AND tr.crt BETWEEN ? AND ?";
|
||||
// array_push($params, strtotime('-24 hours', $now), $now);
|
||||
|
||||
if (isset($filter["start_date"]) && isset($filter["end_date"])) {
|
||||
$query .= " AND tr.crt_d BETWEEN ? AND ?";
|
||||
$params[] = $filter["start_date"];
|
||||
$params[] = $filter["end_date"];
|
||||
array_push($params, $filter["start_date"], $filter["end_date"]);
|
||||
}
|
||||
|
||||
if (isset($filter["start_at"])) {
|
||||
@ -338,8 +417,10 @@ class Tracks extends Model
|
||||
$params[] = $filter["start_at"];
|
||||
}
|
||||
|
||||
// ASC supaya filter berurutan waktu
|
||||
$query .= " ORDER BY tr.crt DESC";
|
||||
// last move based on last count data
|
||||
$query .= " GROUP BY tr.crt_d";
|
||||
$query .= " ORDER BY tr.crt_d DESC";
|
||||
// array_push($params, strtotime('-24 hours', $now), $now);
|
||||
|
||||
if (isset($filter["limit"])) {
|
||||
$query .= " LIMIT ?";
|
||||
@ -348,74 +429,7 @@ class Tracks extends Model
|
||||
|
||||
$query .= " ;";
|
||||
|
||||
// Ambil data
|
||||
$rawTracks = DB::select($query, $params);
|
||||
|
||||
// Sort manual jika perlu
|
||||
usort($rawTracks, fn($a, $b) => $a->lst_loc_crt <=> $b->lst_loc_crt);
|
||||
|
||||
// Filter loncatan
|
||||
$filtered = self::filterJumps($rawTracks, 3000, 5, 300);
|
||||
|
||||
return $filtered;
|
||||
// return array_reverse($filtered);
|
||||
}
|
||||
|
||||
public static function filterJumps($points, $maxDistance = 3000, $maxTimeGap = 5, $maxSpeed = 150)
|
||||
{
|
||||
$filtered = [];
|
||||
$last = null;
|
||||
|
||||
// Sort dari terbaru ke terlama (pastikan sesuai kebutuhan)
|
||||
usort($points, fn($a, $b) => $b->lst_loc_crt <=> $a->lst_loc_crt);
|
||||
|
||||
foreach ($points as $p) {
|
||||
// 1. Validasi koordinat
|
||||
if (!is_numeric($p->latitude) || !is_numeric($p->longitude)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
strlen(explode(".", $p->latitude)[1] ?? "") < 6 ||
|
||||
strlen(explode(".", $p->longitude)[1] ?? "") < 6
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$last) {
|
||||
$filtered[] = $p;
|
||||
$last = $p;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 2. Jarak & waktu
|
||||
$distance = self::haversineDistance(
|
||||
$last->latitude,
|
||||
$last->longitude,
|
||||
$p->latitude,
|
||||
$p->longitude
|
||||
);
|
||||
$timeGap = $last->lst_loc_crt - $p->lst_loc_crt;
|
||||
|
||||
if ($timeGap <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$speed = ($distance / $timeGap) * 3.6; // m/s to km/h
|
||||
|
||||
// 3. Filter logis
|
||||
if ($speed <= $maxSpeed || $distance <= $maxDistance) {
|
||||
$filtered[] = $p;
|
||||
$last = $p;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Pastikan hasil minimal N
|
||||
if (count($filtered) < 500 && count($points) > 500) {
|
||||
return array_slice($points, 0, 500); // fallback ke raw jika terlalu sedikit
|
||||
}
|
||||
|
||||
return $filtered;
|
||||
return DB::select($query, $params);
|
||||
}
|
||||
|
||||
public static function haversineDistance($lat1, $lon1, $lat2, $lon2)
|
||||
@ -436,6 +450,35 @@ class Tracks extends Model
|
||||
return $earthRadius * $c;
|
||||
}
|
||||
|
||||
public static function filterJumps($points, $maxDistance = 50, $maxTimeGap = 10)
|
||||
{
|
||||
$filtered = [];
|
||||
$last = null;
|
||||
|
||||
foreach ($points as $p) {
|
||||
if (!$last) {
|
||||
$filtered[] = $p;
|
||||
$last = $p;
|
||||
continue;
|
||||
}
|
||||
|
||||
$distance = self::haversineDistance(
|
||||
$last->latitude,
|
||||
$last->longitude,
|
||||
$p->latitude,
|
||||
$p->longitude
|
||||
);
|
||||
$timeGap = $p->lst_loc_crt - $last->lst_loc_crt;
|
||||
|
||||
if ($distance <= $maxDistance || $timeGap > $maxTimeGap) {
|
||||
$filtered[] = $p;
|
||||
$last = $p;
|
||||
}
|
||||
}
|
||||
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
public static function nearestInCircle($lat, $lng, $rad, $filter = [])
|
||||
{
|
||||
$earth_rad = Helper::EARTH_RADIUS_M;
|
||||
|
||||
@ -310,21 +310,6 @@ class Tracks_copy extends Model
|
||||
$now = time();
|
||||
$params = [];
|
||||
|
||||
/**
|
||||
* bikin lemot 30s timeout karena ada 2 join di rows yang banyak
|
||||
* solution: indexing column
|
||||
* show index from t_gps_tracks_address;
|
||||
* create index addr_device_id on t_gps_tracks_address (device_id);
|
||||
* create index addr_master_id on t_gps_tracks_address (master_id);
|
||||
* show index from t_gps_tracks;
|
||||
* create index tracks_device_id on t_gps_tracks (device_id);
|
||||
* show index from t_vehicles;
|
||||
* create index vhc_device_id on t_vehicles (device_id);
|
||||
*
|
||||
* solution not working. Don't reinvent the wheel
|
||||
* create view. not working
|
||||
*/
|
||||
|
||||
$query = "SELECT";
|
||||
$query .= " v.id as vid,v.device_id,v.nopol1,v.nopol2,v.nopol3";
|
||||
$query .= " ,tr.id as master_id,tr.latitude,tr.longitude,tr.speed,tr.orientation";
|
||||
@ -335,22 +320,17 @@ class Tracks_copy extends Model
|
||||
$query .=
|
||||
" ,addr.country_text,addr.state_text,addr.city_text,addr.district_text,addr.village_text,addr.postcode,addr.streets,addr.postcode,addr.fulladdress";
|
||||
$query .= " FROM t_vehicles AS v";
|
||||
// $query .= " INNER JOIN " . self::T_TRACKS . " AS tr ON v.device_id = tr.device_id"; // cara lama berlaku utk gps tracker saja
|
||||
$query .= " INNER JOIN " . self::T_TRACKS . " AS tr ON v.id = tr.vhc_id"; // support gps tracker dan smartphone / apapun yang mempunyai device_id(IMEI)
|
||||
$query .= " INNER JOIN " . self::T_TRACKS . " AS tr ON v.id = tr.vhc_id";
|
||||
$query .= " LEFT JOIN " . self::T_TRACKS_ADDR . " AS addr ON tr.id = addr.master_id";
|
||||
$query .= " WHERE v.dlt is null";
|
||||
$query .= " AND v.id = ?";
|
||||
array_push($params, $vid);
|
||||
$query .= " AND tr.latitude is not null";
|
||||
$query .= " AND tr.longitude is not null";
|
||||
$query .= " WHERE v.dlt is null AND v.id = ?";
|
||||
$params[] = $vid;
|
||||
|
||||
// last move based on date
|
||||
// $query .= " AND tr.crt BETWEEN ? AND ?";
|
||||
// array_push($params, strtotime('-24 hours', $now), $now);
|
||||
$query .= " AND tr.latitude is not null AND tr.longitude is not null";
|
||||
|
||||
if (isset($filter["start_date"]) && isset($filter["end_date"])) {
|
||||
$query .= " AND tr.crt_d BETWEEN ? AND ?";
|
||||
array_push($params, $filter["start_date"], $filter["end_date"]);
|
||||
$params[] = $filter["start_date"];
|
||||
$params[] = $filter["end_date"];
|
||||
}
|
||||
|
||||
if (isset($filter["start_at"])) {
|
||||
@ -358,10 +338,8 @@ class Tracks_copy extends Model
|
||||
$params[] = $filter["start_at"];
|
||||
}
|
||||
|
||||
// last move based on last count data
|
||||
$query .= " GROUP BY tr.crt_d";
|
||||
$query .= " ORDER BY tr.crt_d DESC";
|
||||
// array_push($params, strtotime('-24 hours', $now), $now);
|
||||
// ASC supaya filter berurutan waktu
|
||||
$query .= " ORDER BY tr.crt DESC";
|
||||
|
||||
if (isset($filter["limit"])) {
|
||||
$query .= " LIMIT ?";
|
||||
@ -370,7 +348,92 @@ class Tracks_copy extends Model
|
||||
|
||||
$query .= " ;";
|
||||
|
||||
return DB::select($query, $params);
|
||||
// Ambil data
|
||||
$rawTracks = DB::select($query, $params);
|
||||
|
||||
// Sort manual jika perlu
|
||||
usort($rawTracks, fn($a, $b) => $a->lst_loc_crt <=> $b->lst_loc_crt);
|
||||
|
||||
// Filter loncatan
|
||||
$filtered = self::filterJumps($rawTracks, 3000, 5, 300);
|
||||
|
||||
return $filtered;
|
||||
// return array_reverse($filtered);
|
||||
}
|
||||
|
||||
public static function filterJumps($points, $maxDistance = 3000, $maxTimeGap = 5, $maxSpeed = 150)
|
||||
{
|
||||
$filtered = [];
|
||||
$last = null;
|
||||
|
||||
// Sort dari terbaru ke terlama (pastikan sesuai kebutuhan)
|
||||
usort($points, fn($a, $b) => $b->lst_loc_crt <=> $a->lst_loc_crt);
|
||||
|
||||
foreach ($points as $p) {
|
||||
// 1. Validasi koordinat
|
||||
if (!is_numeric($p->latitude) || !is_numeric($p->longitude)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
strlen(explode(".", $p->latitude)[1] ?? "") < 6 ||
|
||||
strlen(explode(".", $p->longitude)[1] ?? "") < 6
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$last) {
|
||||
$filtered[] = $p;
|
||||
$last = $p;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 2. Jarak & waktu
|
||||
$distance = self::haversineDistance(
|
||||
$last->latitude,
|
||||
$last->longitude,
|
||||
$p->latitude,
|
||||
$p->longitude
|
||||
);
|
||||
$timeGap = $last->lst_loc_crt - $p->lst_loc_crt;
|
||||
|
||||
if ($timeGap <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$speed = ($distance / $timeGap) * 3.6; // m/s to km/h
|
||||
|
||||
// 3. Filter logis
|
||||
if ($speed <= $maxSpeed || $distance <= $maxDistance) {
|
||||
$filtered[] = $p;
|
||||
$last = $p;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Pastikan hasil minimal N
|
||||
if (count($filtered) < 500 && count($points) > 500) {
|
||||
return array_slice($points, 0, 500); // fallback ke raw jika terlalu sedikit
|
||||
}
|
||||
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
public static function haversineDistance($lat1, $lon1, $lat2, $lon2)
|
||||
{
|
||||
$earthRadius = 6371000; // meters
|
||||
|
||||
$lat1 = deg2rad($lat1);
|
||||
$lon1 = deg2rad($lon1);
|
||||
$lat2 = deg2rad($lat2);
|
||||
$lon2 = deg2rad($lon2);
|
||||
|
||||
$dlat = $lat2 - $lat1;
|
||||
$dlon = $lon2 - $lon1;
|
||||
|
||||
$a = sin($dlat / 2) ** 2 + cos($lat1) * cos($lat2) * sin($dlon / 2) ** 2;
|
||||
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
|
||||
|
||||
return $earthRadius * $c;
|
||||
}
|
||||
|
||||
public static function nearestInCircle($lat, $lng, $rad, $filter = [])
|
||||
|
||||
@ -11,18 +11,18 @@
|
||||
@section('customcss')
|
||||
<style>
|
||||
/* .select2-container {
|
||||
z-index: 99999;
|
||||
} */
|
||||
z-index: 99999;
|
||||
} */
|
||||
|
||||
/* .landscape-photo {
|
||||
max-height: max(21vh, 210px);
|
||||
} */
|
||||
max-height: max(21vh, 210px);
|
||||
} */
|
||||
|
||||
/* .thumb-img-table {
|
||||
width: max(4vw, 75px);
|
||||
height: max(4vh, 55px);
|
||||
object-fit: cover;
|
||||
} */
|
||||
width: max(4vw, 75px);
|
||||
height: max(4vh, 55px);
|
||||
object-fit: cover;
|
||||
} */
|
||||
</style>
|
||||
@endsection
|
||||
|
||||
@ -146,10 +146,22 @@
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-0">
|
||||
<label for="add-speedlimit" class="col-form-label">Speed Limit (kph):</label>
|
||||
<input type="number" id="add-speedlimit" class="form-control">
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="mb-0">
|
||||
<label for="add-speedlimit" class="col-form-label">Speed Limit (kph):</label>
|
||||
<input type="number" id="add-speedlimit" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="mb-0">
|
||||
<label for="add-sum_milleage" class="col-form-label">Current Mileage (km):</label>
|
||||
<input type="number" id="add-sum_milleage" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -351,9 +363,19 @@
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-0">
|
||||
<label for="edt-speedlimit" class="col-form-label">Speed Limit (kph):</label>
|
||||
<input type="number" id="edt-speedlimit" class="form-control">
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="mb-0">
|
||||
<label for="edt-speedlimit" class="col-form-label">Speed Limit (kph):</label>
|
||||
<input type="number" id="edt-speedlimit" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="mb-0">
|
||||
<label for="edt-sum_milleage" class="col-form-label">Current Mileage (km):</label>
|
||||
<input type="number" id="edt-sum_milleage" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1002,6 +1024,7 @@
|
||||
data.append('type_id', safeVal('#add-type'));
|
||||
data.append('model_id', safeVal('#add-model') ?? 0);
|
||||
data.append('speed_limit', safeVal('#add-speedlimit'));
|
||||
data.append('sum_milleage', safeVal('#add-sum_milleage'));
|
||||
data.append('fuel_capacity', safeVal('#add-fuelcapacity') ?? 0);
|
||||
data.append('fuel_drop_treshold', safeVal('#add-fueldroptreshold') ?? 0);
|
||||
data.append('max_pressure', safeVal('#add-maxpressure') ?? 0);
|
||||
@ -1217,6 +1240,7 @@
|
||||
$('#edt-model').val(data?.model_id).trigger('change');
|
||||
|
||||
$('#edt-speedlimit').val(data?.speed_limit);
|
||||
$('#edt-sum_milleage').val(data?.sum_milleage);
|
||||
$('#edt-fuelcapacity').val(data?.fuel_capacity);
|
||||
$('#edt-fueldroptreshold').val(data?.fuel_drop_treshold);
|
||||
$('#edt-maxpressure').val(data?.max_pressure);
|
||||
@ -1272,6 +1296,7 @@
|
||||
data.model_id = $('#edt-model').val();
|
||||
|
||||
data.speed_limit = $('#edt-speedlimit').val();
|
||||
data.sum_milleage = $('#edt-sum_milleage').val();
|
||||
data.fuel_capacity = $('#edt-fuelcapacity').val();
|
||||
data.fuel_drop_treshold = $('#edt-fueldroptreshold').val();
|
||||
data.max_pressure = $('#edt-maxpressure').val();
|
||||
|
||||
Reference in New Issue
Block a user