379 lines
14 KiB
PHP
379 lines
14 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Carbon\Carbon;
|
|
|
|
// Add This For Export Data In Excel
|
|
use App\Exports\HourlyRainfallExport;
|
|
// Ni install manual in the container
|
|
use Maatwebsite\Excel\Facades\Excel;
|
|
|
|
class RainfallController extends Controller
|
|
{
|
|
// Function Retrieve Rainfall Data For Each Station
|
|
public function index(Request $request)
|
|
{
|
|
$stationFilter = $request->get('station');
|
|
$dateTImeFilter = $request->input('date');
|
|
|
|
$displayDate = $dateTImeFilter ? $dateTImeFilter : now();
|
|
|
|
|
|
$dateFilter = $dateTImeFilter ? Carbon::parse($dateTImeFilter)->format('Y-m-d')
|
|
:Carbon::today()->format('Y-m-d');
|
|
|
|
|
|
$stations = DB::table('station')->select('stationid', 'name')
|
|
->where('rainfall',1)
|
|
->orderBy('stationid')->get();
|
|
|
|
$bindings = [];
|
|
$stationCondition = '';
|
|
if ($stationFilter) {
|
|
$stationCondition = " AND s.stationid = ?";
|
|
$bindings[] = $stationFilter;
|
|
}
|
|
$bindings[] = $displayDate;
|
|
$bindings[] = $dateFilter;
|
|
$bindings[] = $dateFilter;
|
|
$bindings[] = $dateFilter;
|
|
$bindings[] = $dateFilter;
|
|
$bindings[] = $dateFilter;
|
|
$bindings[] = $dateFilter;
|
|
$bindings[] = $dateFilter;
|
|
$bindings[] = $dateFilter;
|
|
$bindings[] = $dateFilter;
|
|
$bindings[] = $dateFilter;
|
|
|
|
$rainfallData = collect(DB::select("
|
|
SELECT
|
|
s.stationid,
|
|
s.name,
|
|
s.district,
|
|
CAST(? AS timestamp) AS selected_timestamp,
|
|
|
|
(
|
|
SELECT l2.hourly
|
|
FROM rainfall l2
|
|
WHERE l2.stationid = s.stationid
|
|
ORDER BY l2.timestamp DESC
|
|
LIMIT 1
|
|
) AS hourly,
|
|
|
|
(
|
|
SELECT l3.daily
|
|
FROM rainfall l3
|
|
WHERE l3.stationid = s.stationid
|
|
ORDER BY l3.timestamp DESC
|
|
LIMIT 1
|
|
) AS daily,
|
|
|
|
(
|
|
SELECT l4.timestamp
|
|
FROM rainfall l4
|
|
WHERE l4.stationid = s.stationid
|
|
AND DATE(l4.timestamp) <= CAST(? AS date)
|
|
ORDER BY l4.timestamp DESC
|
|
LIMIT 1
|
|
) AS last_updated,
|
|
|
|
MAX(CASE WHEN DATE(l.timestamp) = CAST(? as date) - INTERVAL '6 days' THEN l.daily END) AS day1,
|
|
MAX(CASE WHEN DATE(l.timestamp) = CAST(? as date) - INTERVAL '5 days' THEN l.daily END) AS day2,
|
|
MAX(CASE WHEN DATE(l.timestamp) = CAST(? as date) - INTERVAL '4 days' THEN l.daily END) AS day3,
|
|
MAX(CASE WHEN DATE(l.timestamp) = CAST(? as date) - INTERVAL '3 days' THEN l.daily END) AS day4,
|
|
MAX(CASE WHEN DATE(l.timestamp) = CAST(? as date) - INTERVAL '2 days' THEN l.daily END) AS day5,
|
|
MAX(CASE WHEN DATE(l.timestamp) = CAST(? as date) - INTERVAL '1 day' THEN l.daily END) AS day6,
|
|
MAX(CASE WHEN DATE(l.timestamp) = CAST(? as date) THEN l.daily END) AS day7
|
|
|
|
FROM station s
|
|
INNER JOIN rainfall l ON s.stationid = l.stationid
|
|
WHERE TO_CHAR(l.timestamp, 'HH24:MI:SS') != '00:00:00'
|
|
AND l.timestamp >= CAST(? as date) - INTERVAL '6 days'
|
|
$stationCondition
|
|
GROUP BY s.stationid, s.name, s.district
|
|
",
|
|
$bindings
|
|
));
|
|
|
|
|
|
|
|
|
|
$lastupdate = DB::table('rainfall')->max('timestamp');
|
|
|
|
|
|
$dates = collect(range(6,1))->map(fn($i)=>Carbon::parse($dateFilter)->subDays($i)->format('d/m/Y'));
|
|
return view('layout.rainfall',compact('rainfallData','lastupdate','dates','stations','displayDate'));
|
|
}
|
|
|
|
// Function For 6 Hours Rainfall Data for Each Station For Early Warning Threshold
|
|
public function rainfallSum(Request $request)
|
|
{
|
|
$stationFilter = $request->input('station');
|
|
$dateFilter = $request->input('date');
|
|
|
|
// If no date submitted → use current local time
|
|
$displayDate = $dateFilter ? $dateFilter : now('Asia/Kuala_Lumpur');
|
|
|
|
// Convert to Y-m-d H:i:s for SQL comparison
|
|
$sqlDate = \Carbon\Carbon::parse($displayDate)->format('Y-m-d H:i:s');
|
|
|
|
// Fetch stations
|
|
$stations = DB::table('station')->select('stationid', 'name')->where('rainfall',1)->orderBy('stationid')->get();
|
|
|
|
// Build SQL query
|
|
$sql = "
|
|
WITH latest AS (
|
|
SELECT
|
|
s.stationid,
|
|
s.name,
|
|
s.district,
|
|
MAX(l.timestamp) AS latest_timestamp
|
|
FROM station s
|
|
INNER JOIN rainfall l ON s.stationid = l.stationid
|
|
WHERE 1=1
|
|
";
|
|
|
|
$bindings = [];
|
|
|
|
// Apply date filter
|
|
if ($dateFilter) {
|
|
$sql .= " AND l.timestamp = :dateFilter ";
|
|
$bindings['dateFilter'] = $sqlDate;
|
|
}
|
|
|
|
$sql .= "
|
|
GROUP BY s.stationid, s.name, s.district
|
|
),
|
|
hourly_intervals AS (
|
|
SELECT
|
|
s.stationid,
|
|
s.name,
|
|
s.district,
|
|
l.currentrf,
|
|
l.timestamp,
|
|
EXTRACT(EPOCH FROM (lt.latest_timestamp - l.timestamp)) / 3600 AS hours_diff,
|
|
lt.latest_timestamp
|
|
FROM station s
|
|
INNER JOIN rainfall l ON s.stationid = l.stationid
|
|
INNER JOIN latest lt ON lt.stationid = s.stationid
|
|
WHERE l.timestamp <= lt.latest_timestamp
|
|
AND l.timestamp > lt.latest_timestamp - INTERVAL '6 hours'
|
|
)
|
|
SELECT
|
|
stationid,
|
|
name,
|
|
district,
|
|
MAX(CASE WHEN hours_diff < 1 THEN currentrf END) AS hour1_value,
|
|
MAX(CASE WHEN hours_diff < 1 THEN TO_CHAR(timestamp, 'HH24:MI:SS') END) AS hour1_time,
|
|
MAX(CASE WHEN hours_diff >= 1 AND hours_diff < 2 THEN currentrf END) AS hour2_value,
|
|
MAX(CASE WHEN hours_diff >= 1 AND hours_diff < 2 THEN TO_CHAR(timestamp, 'HH24:MI:SS') END) AS hour2_time,
|
|
MAX(CASE WHEN hours_diff >= 2 AND hours_diff < 3 THEN currentrf END) AS hour3_value,
|
|
MAX(CASE WHEN hours_diff >= 2 AND hours_diff < 3 THEN TO_CHAR(timestamp, 'HH24:MI:SS') END) AS hour3_time,
|
|
MAX(CASE WHEN hours_diff >= 3 AND hours_diff < 4 THEN currentrf END) AS hour4_value,
|
|
MAX(CASE WHEN hours_diff >= 3 AND hours_diff < 4 THEN TO_CHAR(timestamp, 'HH24:MI:SS') END) AS hour4_time,
|
|
MAX(CASE WHEN hours_diff >= 4 AND hours_diff < 5 THEN currentrf END) AS hour5_value,
|
|
MAX(CASE WHEN hours_diff >= 4 AND hours_diff < 5 THEN TO_CHAR(timestamp, 'HH24:MI:SS') END) AS hour5_time,
|
|
MAX(CASE WHEN hours_diff >= 5 AND hours_diff < 6 THEN currentrf END) AS hour6_value,
|
|
MAX(CASE WHEN hours_diff >= 5 AND hours_diff < 6 THEN TO_CHAR(timestamp, 'HH24:MI:SS') END) AS hour6_time,
|
|
MAX(latest_timestamp) AS last_update
|
|
FROM hourly_intervals
|
|
WHERE 1=1
|
|
";
|
|
|
|
// Apply station filter
|
|
if ($stationFilter) {
|
|
$sql .= " AND stationid = :stationFilter ";
|
|
$bindings['stationFilter'] = $stationFilter;
|
|
}
|
|
|
|
$sql .= " GROUP BY stationid, name, district ORDER BY stationid;";
|
|
|
|
// Execute query
|
|
$thresholdData = collect(DB::select($sql, $bindings));
|
|
$lastupdate = DB::table('rainfall')->max('timestamp');
|
|
|
|
|
|
return view('layout.threshold', compact('thresholdData', 'stations', 'displayDate','lastupdate'));
|
|
}
|
|
|
|
// Function retrieve Hourly rainfall Graph For Each Station
|
|
public function rainfallGraph($stationid)
|
|
{
|
|
$graphData = DB::table('rainfall')
|
|
->select(DB::raw("TO_CHAR(timestamp, 'HH24:MI') AS hour"), DB::raw("CASE
|
|
WHEN TO_CHAR(timestamp, 'HH24:MI') LIKE '00:%'
|
|
THEN 0
|
|
ELSE hourly
|
|
END AS hourly") )
|
|
->whereDate('timestamp', today())
|
|
->whereRaw("EXTRACT(MINUTE FROM timestamp) = 0")
|
|
->where('stationid', $stationid)
|
|
->orderBy('timestamp')
|
|
->get();
|
|
return response()->json($graphData);
|
|
}
|
|
|
|
// Function retrieve data of 6 Hours Rainfall fo Graph Page
|
|
public function graphData($stationid,$dates)
|
|
{
|
|
$dates = urldecode($dates);
|
|
|
|
$latest = DB::table('rainfall')
|
|
->where('stationid', $stationid)
|
|
->where('timestamp',$dates)
|
|
->max('timestamp');
|
|
|
|
if (!$latest) {
|
|
// no data in table
|
|
return response()->json([
|
|
'labels' => [],
|
|
'data' => []
|
|
]);
|
|
}
|
|
|
|
// 2. Generate 6 hourly timestamps counting back from latest
|
|
$labels = [];
|
|
$times = [];
|
|
$latestCarbon = Carbon::parse($latest);
|
|
for ($i = 5; $i >= 0; $i--) {
|
|
$ts = $latestCarbon->copy()->subHours($i);
|
|
$labels[] = $ts->format('H:i'); // chart label
|
|
$times[] = $ts; // for query matching
|
|
}
|
|
|
|
// 3. Query rainfall data for these 6 timestamps
|
|
$graphData = DB::table('rainfall')
|
|
->select(
|
|
DB::raw("TO_CHAR(timestamp, 'HH24:MI') as time"),
|
|
'hourly'
|
|
)
|
|
->where('stationid', $stationid)
|
|
->whereBetween('timestamp', [
|
|
$latestCarbon->copy()->subHours(5),
|
|
$latestCarbon
|
|
])
|
|
->orderBy('timestamp')
|
|
->get();
|
|
|
|
// 4. Map data to labels, fill missing hours with 0
|
|
$data = [];
|
|
foreach ($labels as $label) {
|
|
$record = $graphData->firstWhere('time', $label);
|
|
$data[] = $record ? $record->hourly : 0;
|
|
}
|
|
|
|
return response()->json([
|
|
'labels' => $labels,
|
|
'data' => $data
|
|
]);
|
|
|
|
|
|
|
|
}
|
|
|
|
//Return View Graph Page
|
|
public function graphPage($stationid,$dates)
|
|
{
|
|
return view('layout.graph.rainfall',compact('stationid','dates'));
|
|
}
|
|
|
|
// Function for Retrieve Historical Rainfall Data
|
|
public function historicalRainfall(Request $request)
|
|
{
|
|
$validated = $request->validate([
|
|
'station' => 'required|string|max:20',
|
|
'startdate' => 'required|date',
|
|
'enddate' => 'required|date|after_or_equal:startdate',
|
|
]);
|
|
|
|
$stationFilter = $validated['station'];
|
|
$startDateInput = $validated['startdate'];
|
|
$endDateInput = $validated['enddate'];
|
|
|
|
$displayDate = $startDateInput ?: now();
|
|
$displayEndDate = $endDateInput ?: now();
|
|
|
|
$stations = DB::table('station')
|
|
->select('stationid', 'name')
|
|
->where('rainfall', 1)
|
|
->orderBy('stationid')
|
|
->get();
|
|
|
|
$startDate = Carbon::parse($startDateInput)->toDateString();
|
|
$endDate = Carbon::parse($endDateInput)->toDateString();
|
|
|
|
// Build the hourly columns without referencing grouped outer query
|
|
$hourlyColumns = [];
|
|
for ($i = 0; $i <= 23; $i++) {
|
|
$hour = sprintf("%02d", $i);
|
|
$hourlyColumns[] = "
|
|
MAX(CASE WHEN EXTRACT(HOUR FROM timestamp) = $i THEN hourly END) AS hour_$hour
|
|
";
|
|
}
|
|
$hourlyColumnSql = implode(",", $hourlyColumns);
|
|
|
|
// Use a CTE to get latest per hour per day first
|
|
$sql = "
|
|
WITH latest_per_hour AS (
|
|
SELECT DISTINCT ON (stationid, timestamp::date, EXTRACT(HOUR FROM timestamp))
|
|
stationid,
|
|
timestamp,
|
|
hourly,
|
|
daily
|
|
FROM rainfall
|
|
WHERE stationid = :stationid
|
|
AND timestamp::date BETWEEN :startDate AND :endDate
|
|
ORDER BY stationid, timestamp::date, EXTRACT(HOUR FROM timestamp), timestamp DESC
|
|
)
|
|
SELECT
|
|
timestamp::date AS date,
|
|
stationid,
|
|
$hourlyColumnSql,
|
|
MAX(daily) FILTER (WHERE EXTRACT(HOUR FROM timestamp) <> 0) AS total_24
|
|
FROM latest_per_hour
|
|
GROUP BY timestamp::date, stationid
|
|
ORDER BY timestamp::date
|
|
";
|
|
|
|
$historyData = collect(DB::select($sql, [
|
|
'stationid' => $stationFilter,
|
|
'startDate' => $startDate,
|
|
'endDate' => $endDate
|
|
]));
|
|
|
|
return view('layout.historicalrainfall', compact(
|
|
'historyData',
|
|
'stations',
|
|
'displayDate',
|
|
'displayEndDate'
|
|
));
|
|
}
|
|
|
|
|
|
// Function for export Historical Rainfall To Excel File
|
|
public function exportHourlyRainfallExcel(Request $request)
|
|
{
|
|
$validated = $request->validate([
|
|
'station' => 'required|string|max:20',
|
|
'startdate' => 'required|date',
|
|
'enddate' => 'required|date|after_or_equal:startdate',
|
|
]);
|
|
|
|
$stationid = $validated['station'];
|
|
$startDate = $validated['startdate'];
|
|
$endDate = $validated['enddate'];
|
|
|
|
|
|
$startDate2 = Carbon::parse($startDate)->toDateString();
|
|
$endDate2 = Carbon::parse($endDate)->toDateString();
|
|
|
|
return Excel::download(
|
|
new HourlyRainfallExport($stationid,$startDate,$endDate),"Hourly Rainfall {$stationid} {$startDate2} - {$endDate2}.xlsx"
|
|
|
|
);
|
|
}
|
|
}
|