Files
sides/src/app/Http/Controllers/RainfallController.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"
);
}
}